15#include <qfilesystemmodel.h>
16#include <qabstractfileiconprovider.h>
17#include <qfiledialog.h>
21using namespace Qt::StringLiterals;
26 QStyledItemDelegate::initStyleOption(option,index);
27 QVariant value = index.data(QUrlModel::EnabledRole);
28 if (value.isValid()) {
30 if (!qvariant_cast<
bool>(value))
31 option->state &= ~QStyle::State_Enabled;
36
37
38
39
40
41
42
43QUrlModel::QUrlModel(QObject *parent) : QStandardItemModel(parent), showFullPath(
false), fileSystemModel(
nullptr)
47QUrlModel::~QUrlModel()
49 for (
const auto &conn : std::as_const(modelConnections))
55#if QT_CONFIG(draganddrop)
56static bool hasSupportedFormat(
const QMimeData *data)
58 return data->hasFormat(QLatin1StringView(uriListMimeType));
63
64
65QStringList QUrlModel::mimeTypes()
const
67 return QStringList(QLatin1StringView(uriListMimeType));
71
72
73Qt::ItemFlags QUrlModel::flags(
const QModelIndex &index)
const
75 Qt::ItemFlags flags = QStandardItemModel::flags(index);
76 if (index.isValid()) {
77 flags &= ~Qt::ItemIsEditable;
79 flags &= ~Qt::ItemIsDropEnabled;
82 if (index.data(Qt::DecorationRole).isNull())
83 flags &= ~Qt::ItemIsEnabled;
89
90
91QMimeData *QUrlModel::mimeData(
const QModelIndexList &indexes)
const
94 for (
const auto &index : indexes) {
95 if (index.column() == 0)
96 list.append(index.data(UrlRole).toUrl());
98 QMimeData *data =
new QMimeData();
103#if QT_CONFIG(draganddrop)
106
107
108
109
110bool QUrlModel::canDrop(QDragEnterEvent *event)
112 if (!hasSupportedFormat(event->mimeData()))
115 const QList<QUrl> list = event->mimeData()->urls();
116 for (
const auto &url : list) {
117 const QModelIndex idx = fileSystemModel->index(url.toLocalFile());
118 if (!fileSystemModel->isDir(idx))
125
126
127bool QUrlModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
128 int row,
int column,
const QModelIndex &parent)
130 if (!hasSupportedFormat(data))
135 addUrls(data->urls(), row);
142
143
144
145
146bool QUrlModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
148 if (value.userType() == QMetaType::QUrl) {
149 QUrl url = value.toUrl();
150 QModelIndex dirIndex = fileSystemModel->index(url.toLocalFile());
153 QStandardItemModel::setData(index, QDir::toNativeSeparators(fileSystemModel->data(dirIndex, QFileSystemModel::FilePathRole).toString()));
155 QStandardItemModel::setData(index, QDir::toNativeSeparators(fileSystemModel->data(dirIndex, QFileSystemModel::FilePathRole).toString()), Qt::ToolTipRole);
156 QStandardItemModel::setData(index, fileSystemModel->data(dirIndex).toString());
158 QStandardItemModel::setData(index, fileSystemModel->data(dirIndex, Qt::DecorationRole),
160 QStandardItemModel::setData(index, url, UrlRole);
163 return QStandardItemModel::setData(index, value, role);
166void QUrlModel::setUrl(
const QModelIndex &index,
const QUrl &url,
const QModelIndex &dirIndex)
168 setData(index, url, UrlRole);
169 if (url.path().isEmpty()) {
170 setData(index, fileSystemModel->myComputer());
171 setData(index, fileSystemModel->myComputer(Qt::DecorationRole), Qt::DecorationRole);
176 newName = QDir::toNativeSeparators(dirIndex.data(QFileSystemModel::FilePathRole).toString());
178 newName = dirIndex.data().toString();
181 QIcon newIcon = qvariant_cast<QIcon>(dirIndex.data(Qt::DecorationRole));
182 if (!dirIndex.isValid()) {
183 const QAbstractFileIconProvider *provider = fileSystemModel->iconProvider();
185 newIcon = provider->icon(QAbstractFileIconProvider::Folder);
186 newName = QFileInfo(url.toLocalFile()).fileName();
187 if (!invalidUrls.contains(url))
188 invalidUrls.append(url);
190 setData(index,
false, EnabledRole);
193 setData(index,
true, EnabledRole);
197 if (!newIcon.isNull()) {
199 const QSize size = newIcon.actualSize(QSize(32,32));
200 if (size.width() < 32) {
201 const auto widget = qobject_cast<QWidget *>(parent());
202 const auto dpr = widget ? widget->devicePixelRatio() : qApp->devicePixelRatio();
203 const auto smallPixmap = newIcon.pixmap(QSize(32, 32), dpr);
204 const auto newPixmap = smallPixmap.scaledToWidth(32 * dpr, Qt::SmoothTransformation);
205 newIcon.addPixmap(newPixmap);
209 if (index.data().toString() != newName)
210 setData(index, newName);
211 QIcon oldIcon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
212 if (oldIcon.cacheKey() != newIcon.cacheKey())
213 setData(index, newIcon, Qt::DecorationRole);
217void QUrlModel::setUrls(
const QList<QUrl> &list)
219 removeRows(0, rowCount());
226
227
228
229
230
231void QUrlModel::addUrls(
const QList<QUrl> &list,
int row,
bool move)
235 row = qMin(row, rowCount());
236 const auto rend = list.crend();
237 for (
auto it = list.crbegin(); it != rend; ++it) {
239 if (!url.isValid() || url.scheme() !=
"file"_L1)
242 const QString cleanUrl = QDir::cleanPath(url.toLocalFile());
243 if (!cleanUrl.isEmpty())
244 url = QUrl::fromLocalFile(cleanUrl);
246 for (
int j = 0; move && j < rowCount(); ++j) {
247 QString local = index(j, 0).data(UrlRole).toUrl().toLocalFile();
249 const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
251 const Qt::CaseSensitivity cs = Qt::CaseSensitive;
253 if (!cleanUrl.compare(local, cs)) {
261 QModelIndex idx = fileSystemModel->index(cleanUrl);
262 if (!fileSystemModel->isDir(idx))
265 setUrl(index(row, 0), url, idx);
266 watching.append({idx, cleanUrl});
271
272
273QList<QUrl> QUrlModel::urls()
const
276 const int numRows = rowCount();
277 list.reserve(numRows);
278 for (
int i = 0; i < numRows; ++i)
279 list.append(data(index(i, 0), UrlRole).toUrl());
284
285
286void QUrlModel::setFileSystemModel(QFileSystemModel *model)
288 if (model == fileSystemModel)
290 if (fileSystemModel !=
nullptr) {
291 for (
const auto &conn : std::as_const(modelConnections))
294 fileSystemModel = model;
295 if (fileSystemModel !=
nullptr) {
297 connect(model, &QFileSystemModel::dataChanged,
298 this, &QUrlModel::dataChanged),
299 connect(model, &QFileSystemModel::layoutChanged,
300 this, &QUrlModel::layoutChanged),
301 connect(model, &QFileSystemModel::rowsRemoved,
302 this, &QUrlModel::layoutChanged),
310
311
312void QUrlModel::dataChanged(
const QModelIndex &topLeft,
const QModelIndex &bottomRight)
314 QModelIndex parent = topLeft.parent();
315 for (
int i = 0; i < watching.size(); ++i) {
316 QModelIndex index = watching.at(i).index;
317 if (index.model() && topLeft.model()) {
318 Q_ASSERT(index.model() == topLeft.model());
320 if ( index.row() >= topLeft.row()
321 && index.row() <= bottomRight.row()
322 && index.column() >= topLeft.column()
323 && index.column() <= bottomRight.column()
324 && index.parent() == parent) {
325 changed(watching.at(i).path);
331
332
333void QUrlModel::layoutChanged()
336 paths.reserve(watching.size());
337 for (
const WatchItem &item : std::as_const(watching))
338 paths.append(item.path);
340 for (
const auto &path : paths) {
341 QModelIndex newIndex = fileSystemModel->index(path);
342 watching.append({newIndex, path});
343 if (newIndex.isValid())
349
350
351
352
353void QUrlModel::changed(
const QString &path)
355 for (
int i = 0; i < rowCount(); ++i) {
356 QModelIndex idx = index(i, 0);
357 if (idx.data(UrlRole).toUrl().toLocalFile() == path) {
358 setData(idx, idx.data(UrlRole).toUrl());
363QSidebar::QSidebar(QWidget *parent) : QListView(parent)
367void QSidebar::setModelAndUrls(QFileSystemModel *model,
const QList<QUrl> &newUrls)
369 setUniformItemSizes(
true);
370 urlModel =
new QUrlModel(
this);
371 urlModel->setFileSystemModel(model);
373 setItemDelegate(
new QSideBarDelegate(
this));
375 connect(selectionModel(), &QItemSelectionModel::currentChanged,
376 this, &QSidebar::clicked);
377#if QT_CONFIG(draganddrop)
378 setDragDropMode(QAbstractItemView::DragDrop);
381 setContextMenuPolicy(Qt::CustomContextMenu);
382 connect(
this, &QSidebar::customContextMenuRequested,
383 this, &QSidebar::showContextMenu);
385 urlModel->setUrls(newUrls);
386 setCurrentIndex(
this->model()->index(0,0));
393#if QT_CONFIG(draganddrop)
394void QSidebar::dragEnterEvent(QDragEnterEvent *event)
396 if (urlModel->canDrop(event))
397 QListView::dragEnterEvent(event);
401QSize QSidebar::sizeHint()
const
404 return QListView::sizeHintForIndex(model()->index(0, 0)) + QSize(2 * frameWidth(), 2 * frameWidth());
405 return QListView::sizeHint();
408void QSidebar::selectUrl(
const QUrl &url)
410 disconnect(selectionModel(), &QItemSelectionModel::currentChanged,
411 this, &QSidebar::clicked);
413 selectionModel()->clear();
414 for (
int i = 0; i < model()->rowCount(); ++i) {
415 if (model()->index(i, 0).data(QUrlModel::UrlRole).toUrl() == url) {
416 selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select);
421 connect(selectionModel(), &QItemSelectionModel::currentChanged,
422 this, &QSidebar::clicked);
427
428
429
430
431void QSidebar::showContextMenu(
const QPoint &position)
433 QList<QAction *> actions;
434 if (indexAt(position).isValid()) {
435 QAction *action =
new QAction(QFileDialog::tr(
"Remove"),
this);
436 if (indexAt(position).data(QUrlModel::UrlRole).toUrl().path().isEmpty())
437 action->setEnabled(
false);
438 connect(action, &QAction::triggered,
this, &QSidebar::removeEntry);
439 actions.append(action);
441 if (actions.size() > 0)
442 QMenu::exec(actions, mapToGlobal(position));
447
448
449
450
451void QSidebar::removeEntry()
453 const QList<QModelIndex> idxs = selectionModel()->selectedIndexes();
456 const QList<QPersistentModelIndex> persIndexes(idxs.cbegin(), idxs.cend());
457 for (
const QPersistentModelIndex &persistent : persIndexes) {
458 if (!persistent.data(QUrlModel::UrlRole).toUrl().path().isEmpty())
459 model()->removeRow(persistent.row());
464
465
466
467
468void QSidebar::clicked(
const QModelIndex &index)
470 QUrl url = model()->index(index.row(), 0).data(QUrlModel::UrlRole).toUrl();
476
477
478
479void QSidebar::focusInEvent(QFocusEvent *event)
481 QAbstractScrollArea::focusInEvent(event);
482 viewport()->update();
486
487
488bool QSidebar::event(QEvent * event)
490 if (event->type() == QEvent::KeyRelease) {
491 QKeyEvent *ke =
static_cast<QKeyEvent *>(event);
492 if (ke->key() == Qt::Key_Delete) {
497 return QListView::event(event);
502#include "moc_qsidebar_p.cpp"