14#include <qfilesystemmodel.h>
15#include <qabstractfileiconprovider.h>
16#include <qfiledialog.h>
20using namespace Qt::StringLiterals;
25 QStyledItemDelegate::initStyleOption(option,index);
26 QVariant value = index.data(QUrlModel::EnabledRole);
27 if (value.isValid()) {
29 if (!qvariant_cast<
bool>(value))
30 option->state &= ~QStyle::State_Enabled;
35
36
37
38
39
40
41
42QUrlModel::QUrlModel(QObject *parent) : QStandardItemModel(parent), showFullPath(
false), fileSystemModel(
nullptr)
46QUrlModel::~QUrlModel()
48 for (
const auto &conn : std::as_const(modelConnections))
60
61
62QStringList QUrlModel::mimeTypes()
const
64 return QStringList(QLatin1StringView(uriListMimeType));
68
69
70Qt::ItemFlags QUrlModel::flags(
const QModelIndex &index)
const
72 Qt::ItemFlags flags = QStandardItemModel::flags(index);
73 if (index.isValid()) {
74 flags &= ~Qt::ItemIsEditable;
76 flags &= ~Qt::ItemIsDropEnabled;
79 if (index.data(Qt::DecorationRole).isNull())
80 flags &= ~Qt::ItemIsEnabled;
86
87
88QMimeData *QUrlModel::mimeData(
const QModelIndexList &indexes)
const
91 for (
const auto &index : indexes) {
92 if (index.column() == 0)
93 list.append(index.data(UrlRole).toUrl());
95 QMimeData *data =
new QMimeData();
100#if QT_CONFIG(draganddrop)
103
104
105
106
107bool QUrlModel::canDrop(QDragEnterEvent *event)
109 if (!hasSupportedFormat(event->mimeData()))
112 const QList<QUrl> list = event->mimeData()->urls();
113 for (
const auto &url : list) {
114 const QModelIndex idx = fileSystemModel->index(url.toLocalFile());
115 if (!fileSystemModel->isDir(idx))
122
123
124bool QUrlModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
125 int row,
int column,
const QModelIndex &parent)
127 if (!hasSupportedFormat(data))
132 addUrls(data->urls(), row);
139
140
141
142
143bool QUrlModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
145 if (value.userType() == QMetaType::QUrl) {
146 QUrl url = value.toUrl();
147 QModelIndex dirIndex = fileSystemModel->index(url.toLocalFile());
150 QStandardItemModel::setData(index, QDir::toNativeSeparators(fileSystemModel->data(dirIndex, QFileSystemModel::FilePathRole).toString()));
152 QStandardItemModel::setData(index, QDir::toNativeSeparators(fileSystemModel->data(dirIndex, QFileSystemModel::FilePathRole).toString()), Qt::ToolTipRole);
153 QStandardItemModel::setData(index, fileSystemModel->data(dirIndex).toString());
155 QStandardItemModel::setData(index, fileSystemModel->data(dirIndex, Qt::DecorationRole),
157 QStandardItemModel::setData(index, url, UrlRole);
160 return QStandardItemModel::setData(index, value, role);
163void QUrlModel::setUrl(
const QModelIndex &index,
const QUrl &url,
const QModelIndex &dirIndex)
165 setData(index, url, UrlRole);
166 if (url.path().isEmpty()) {
167 setData(index, fileSystemModel->myComputer());
168 setData(index, fileSystemModel->myComputer(Qt::DecorationRole), Qt::DecorationRole);
173 newName = QDir::toNativeSeparators(dirIndex.data(QFileSystemModel::FilePathRole).toString());
175 newName = dirIndex.data().toString();
178 QIcon newIcon = qvariant_cast<QIcon>(dirIndex.data(Qt::DecorationRole));
179 if (!dirIndex.isValid()) {
180 const QAbstractFileIconProvider *provider = fileSystemModel->iconProvider();
182 newIcon = provider->icon(QAbstractFileIconProvider::Folder);
183 newName = QFileInfo(url.toLocalFile()).fileName();
184 if (!invalidUrls.contains(url))
185 invalidUrls.append(url);
187 setData(index,
false, EnabledRole);
190 setData(index,
true, EnabledRole);
194 if (!newIcon.isNull()) {
196 const QSize size = newIcon.actualSize(QSize(32,32));
197 if (size.width() < 32) {
198 QPixmap smallPixmap = newIcon.pixmap(QSize(32, 32));
199 newIcon.addPixmap(smallPixmap.scaledToWidth(32, Qt::SmoothTransformation));
203 if (index.data().toString() != newName)
204 setData(index, newName);
205 QIcon oldIcon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
206 if (oldIcon.cacheKey() != newIcon.cacheKey())
207 setData(index, newIcon, Qt::DecorationRole);
211void QUrlModel::setUrls(
const QList<QUrl> &list)
213 removeRows(0, rowCount());
220
221
222
223
224
225void QUrlModel::addUrls(
const QList<QUrl> &list,
int row,
bool move)
229 row = qMin(row, rowCount());
230 const auto rend = list.crend();
231 for (
auto it = list.crbegin(); it != rend; ++it) {
233 if (!url.isValid() || url.scheme() !=
"file"_L1)
236 const QString cleanUrl = QDir::cleanPath(url.toLocalFile());
237 if (!cleanUrl.isEmpty())
238 url = QUrl::fromLocalFile(cleanUrl);
240 for (
int j = 0; move && j < rowCount(); ++j) {
241 QString local = index(j, 0).data(UrlRole).toUrl().toLocalFile();
243 const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
245 const Qt::CaseSensitivity cs = Qt::CaseSensitive;
247 if (!cleanUrl.compare(local, cs)) {
255 QModelIndex idx = fileSystemModel->index(cleanUrl);
256 if (!fileSystemModel->isDir(idx))
259 setUrl(index(row, 0), url, idx);
260 watching.append({idx, cleanUrl});
265
266
267QList<QUrl> QUrlModel::urls()
const
270 const int numRows = rowCount();
271 list.reserve(numRows);
272 for (
int i = 0; i < numRows; ++i)
273 list.append(data(index(i, 0), UrlRole).toUrl());
278
279
280void QUrlModel::setFileSystemModel(QFileSystemModel *model)
282 if (model == fileSystemModel)
284 if (fileSystemModel !=
nullptr) {
285 for (
const auto &conn : std::as_const(modelConnections))
288 fileSystemModel = model;
289 if (fileSystemModel !=
nullptr) {
291 connect(model, &QFileSystemModel::dataChanged,
292 this, &QUrlModel::dataChanged),
293 connect(model, &QFileSystemModel::layoutChanged,
294 this, &QUrlModel::layoutChanged),
295 connect(model, &QFileSystemModel::rowsRemoved,
296 this, &QUrlModel::layoutChanged),
304
305
306void QUrlModel::dataChanged(
const QModelIndex &topLeft,
const QModelIndex &bottomRight)
308 QModelIndex parent = topLeft.parent();
309 for (
int i = 0; i < watching.size(); ++i) {
310 QModelIndex index = watching.at(i).index;
311 if (index.model() && topLeft.model()) {
312 Q_ASSERT(index.model() == topLeft.model());
314 if ( index.row() >= topLeft.row()
315 && index.row() <= bottomRight.row()
316 && index.column() >= topLeft.column()
317 && index.column() <= bottomRight.column()
318 && index.parent() == parent) {
319 changed(watching.at(i).path);
325
326
327void QUrlModel::layoutChanged()
330 paths.reserve(watching.size());
331 for (
const WatchItem &item : std::as_const(watching))
332 paths.append(item.path);
334 for (
const auto &path : paths) {
335 QModelIndex newIndex = fileSystemModel->index(path);
336 watching.append({newIndex, path});
337 if (newIndex.isValid())
343
344
345
346
347void QUrlModel::changed(
const QString &path)
349 for (
int i = 0; i < rowCount(); ++i) {
350 QModelIndex idx = index(i, 0);
351 if (idx.data(UrlRole).toUrl().toLocalFile() == path) {
352 setData(idx, idx.data(UrlRole).toUrl());
357QSidebar::QSidebar(QWidget *parent) : QListView(parent)
361void QSidebar::setModelAndUrls(QFileSystemModel *model,
const QList<QUrl> &newUrls)
363 setUniformItemSizes(
true);
364 urlModel =
new QUrlModel(
this);
365 urlModel->setFileSystemModel(model);
367 setItemDelegate(
new QSideBarDelegate(
this));
369 connect(selectionModel(), &QItemSelectionModel::currentChanged,
370 this, &QSidebar::clicked);
371#if QT_CONFIG(draganddrop)
372 setDragDropMode(QAbstractItemView::DragDrop);
375 setContextMenuPolicy(Qt::CustomContextMenu);
376 connect(
this, &QSidebar::customContextMenuRequested,
377 this, &QSidebar::showContextMenu);
379 urlModel->setUrls(newUrls);
380 setCurrentIndex(
this->model()->index(0,0));
387#if QT_CONFIG(draganddrop)
388void QSidebar::dragEnterEvent(QDragEnterEvent *event)
390 if (urlModel->canDrop(event))
391 QListView::dragEnterEvent(event);
395QSize QSidebar::sizeHint()
const
398 return QListView::sizeHintForIndex(model()->index(0, 0)) + QSize(2 * frameWidth(), 2 * frameWidth());
399 return QListView::sizeHint();
402void QSidebar::selectUrl(
const QUrl &url)
404 disconnect(selectionModel(), &QItemSelectionModel::currentChanged,
405 this, &QSidebar::clicked);
407 selectionModel()->clear();
408 for (
int i = 0; i < model()->rowCount(); ++i) {
409 if (model()->index(i, 0).data(QUrlModel::UrlRole).toUrl() == url) {
410 selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select);
415 connect(selectionModel(), &QItemSelectionModel::currentChanged,
416 this, &QSidebar::clicked);
421
422
423
424
425void QSidebar::showContextMenu(
const QPoint &position)
427 QList<QAction *> actions;
428 if (indexAt(position).isValid()) {
429 QAction *action =
new QAction(QFileDialog::tr(
"Remove"),
this);
430 if (indexAt(position).data(QUrlModel::UrlRole).toUrl().path().isEmpty())
431 action->setEnabled(
false);
432 connect(action, &QAction::triggered,
this, &QSidebar::removeEntry);
433 actions.append(action);
435 if (actions.size() > 0)
436 QMenu::exec(actions, mapToGlobal(position));
441
442
443
444
445void QSidebar::removeEntry()
447 const QList<QModelIndex> idxs = selectionModel()->selectedIndexes();
450 const QList<QPersistentModelIndex> persIndexes(idxs.cbegin(), idxs.cend());
451 for (
const QPersistentModelIndex &persistent : persIndexes) {
452 if (!persistent.data(QUrlModel::UrlRole).toUrl().path().isEmpty())
453 model()->removeRow(persistent.row());
458
459
460
461
462void QSidebar::clicked(
const QModelIndex &index)
464 QUrl url = model()->index(index.row(), 0).data(QUrlModel::UrlRole).toUrl();
470
471
472
473void QSidebar::focusInEvent(QFocusEvent *event)
475 QAbstractScrollArea::focusInEvent(event);
476 viewport()->update();
480
481
482bool QSidebar::event(QEvent * event)
484 if (event->type() == QEvent::KeyRelease) {
485 QKeyEvent *ke =
static_cast<QKeyEvent *>(event);
486 if (ke->key() == Qt::Key_Delete) {
491 return QListView::event(event);
496#include "moc_qsidebar_p.cpp"