qsidebar.cpp

Absolute File Name:/home/qt/qt5_coco/qt5/qtbase/src/widgets/dialogs/qsidebar.cpp
Source codeSwitch to Preprocessed file
LineSourceCount
1/****************************************************************************-
2**-
3** Copyright (C) 2016 The Qt Company Ltd.-
4** Contact: https://www.qt.io/licensing/-
5**-
6** This file is part of the QtWidgets module of the Qt Toolkit.-
7**-
8** $QT_BEGIN_LICENSE:LGPL$-
9** Commercial License Usage-
10** Licensees holding valid commercial Qt licenses may use this file in-
11** accordance with the commercial license agreement provided with the-
12** Software or, alternatively, in accordance with the terms contained in-
13** a written agreement between you and The Qt Company. For licensing terms-
14** and conditions see https://www.qt.io/terms-conditions. For further-
15** information use the contact form at https://www.qt.io/contact-us.-
16**-
17** GNU Lesser General Public License Usage-
18** Alternatively, this file may be used under the terms of the GNU Lesser-
19** General Public License version 3 as published by the Free Software-
20** Foundation and appearing in the file LICENSE.LGPL3 included in the-
21** packaging of this file. Please review the following information to-
22** ensure the GNU Lesser General Public License version 3 requirements-
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.-
24**-
25** GNU General Public License Usage-
26** Alternatively, this file may be used under the terms of the GNU-
27** General Public License version 2.0 or (at your option) the GNU General-
28** Public license version 3 or any later version approved by the KDE Free-
29** Qt Foundation. The licenses are as published by the Free Software-
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3-
31** included in the packaging of this file. Please review the following-
32** information to ensure the GNU General Public License requirements will-
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and-
34** https://www.gnu.org/licenses/gpl-3.0.html.-
35**-
36** $QT_END_LICENSE$-
37**-
38****************************************************************************/-
39-
40#include "qsidebar_p.h"-
41#include "qfilesystemmodel.h"-
42-
43#ifndef QT_NO_FILEDIALOG-
44-
45#include <qaction.h>-
46#include <qurl.h>-
47#include <qmenu.h>-
48#include <qmimedata.h>-
49#include <qevent.h>-
50#include <qdebug.h>-
51#include <qfileiconprovider.h>-
52#include <qfiledialog.h>-
53-
54QT_BEGIN_NAMESPACE-
55-
56void QSideBarDelegate::initStyleOption(QStyleOptionViewItem *option,-
57 const QModelIndex &index) const-
58{-
59 QStyledItemDelegate::initStyleOption(option,index);-
60 QVariant value = index.data(QUrlModel::EnabledRole);-
61 if (value.isValid()) {-
62 //If the bookmark/entry is not enabled then we paint it in gray-
63 if (!qvariant_cast<bool>(value))-
64 option->state &= ~QStyle::State_Enabled;-
65 }-
66}-
67-
68/*!-
69 \internal-
70 \class QUrlModel-
71 QUrlModel lets you have indexes from a QFileSystemModel to a list. When QFileSystemModel-
72 changes them QUrlModel will automatically update.-
73-
74 Example usage: File dialog sidebar and combo box-
75 */-
76QUrlModel::QUrlModel(QObject *parent) : QStandardItemModel(parent), showFullPath(false), fileSystemModel(0)-
77{-
78}-
79-
80/*!-
81 \reimp-
82*/-
83QStringList QUrlModel::mimeTypes() const-
84{-
85 return QStringList(QLatin1String("text/uri-list"));-
86}-
87-
88/*!-
89 \reimp-
90*/-
91Qt::ItemFlags QUrlModel::flags(const QModelIndex &index) const-
92{-
93 Qt::ItemFlags flags = QStandardItemModel::flags(index);-
94 if (index.isValid()) {-
95 flags &= ~Qt::ItemIsEditable;-
96 // ### some future version could support "moving" urls onto a folder-
97 flags &= ~Qt::ItemIsDropEnabled;-
98 }-
99-
100 if (index.data(Qt::DecorationRole).isNull())-
101 flags &= ~Qt::ItemIsEnabled;-
102-
103 return flags;-
104}-
105-
106/*!-
107 \reimp-
108*/-
109QMimeData *QUrlModel::mimeData(const QModelIndexList &indexes) const-
110{-
111 QList<QUrl> list;-
112 for (int i = 0; i <const auto &index : indexes.count(); ++i) {-
113 if (indexesindex.at(i).column() == 0)
index.column() == 0Description
TRUEnever evaluated
FALSEnever evaluated
0
114 list.append(indexesindex.at(i).data(UrlRole).toUrl());
never executed: list.append(index.data(UrlRole).toUrl());
0
115 }
never executed: end of block
0
116 QMimeData *data = new QMimeData();-
117 data->setUrls(list);-
118 return data;
never executed: return data;
0
119}-
120-
121#ifndef QT_NO_DRAGANDDROP-
122-
123/*!-
124 Decide based upon the data if it should be accepted or not-
125-
126 We only accept dirs and not files-
127*/-
128bool QUrlModel::canDrop(QDragEnterEvent *event)-
129{-
130 if (!event->mimeData()->formats().contains(mimeTypes().firstconstFirst()))
!event->mimeDa....constFirst())Description
TRUEnever evaluated
FALSEnever evaluated
0
131 return false;
never executed: return false;
0
132-
133 const QList<QUrl> list = event->mimeData()->urls();-
134 for (int i = 0; i <const auto &url : list.count(); ++i) {-
135 const QModelIndex idx = fileSystemModel->index(listurl.at(0).toLocalFile());-
136 if (!fileSystemModel->isDir(idx))
!fileSystemModel->isDir(idx)Description
TRUEnever evaluated
FALSEnever evaluated
0
137 return false;
never executed: return false;
0
138 }
never executed: end of block
0
139 return true;
never executed: return true;
0
140}-
141-
142/*!-
143 \reimp-
144*/-
145bool QUrlModel::dropMimeData(const QMimeData *data, Qt::DropAction action,-
146 int row, int column, const QModelIndex &parent)-
147{-
148 if (!data->formats().contains(mimeTypes().firstconstFirst()))
!data->formats....constFirst())Description
TRUEnever evaluated
FALSEnever evaluated
0
149 return false;
never executed: return false;
0
150 Q_UNUSED(action);-
151 Q_UNUSED(column);-
152 Q_UNUSED(parent);-
153 addUrls(data->urls(), row);-
154 return true;
never executed: return true;
0
155}-
156-
157#endif // QT_NO_DRAGANDDROP-
158-
159/*!-
160 \reimp-
161-
162 If the role is the UrlRole then handle otherwise just pass to QStandardItemModel-
163*/-
164bool QUrlModel::setData(const QModelIndex &index, const QVariant &value, int role)-
165{-
166 if (value.type() == QVariant::Url) {-
167 QUrl url = value.toUrl();-
168 QModelIndex dirIndex = fileSystemModel->index(url.toLocalFile());-
169 //On windows the popup display the "C:\", convert to nativeSeparators-
170 if (showFullPath)-
171 QStandardItemModel::setData(index, QDir::toNativeSeparators(fileSystemModel->data(dirIndex, QFileSystemModel::FilePathRole).toString()));-
172 else {-
173 QStandardItemModel::setData(index, QDir::toNativeSeparators(fileSystemModel->data(dirIndex, QFileSystemModel::FilePathRole).toString()), Qt::ToolTipRole);-
174 QStandardItemModel::setData(index, fileSystemModel->data(dirIndex).toString());-
175 }-
176 QStandardItemModel::setData(index, fileSystemModel->data(dirIndex, Qt::DecorationRole),-
177 Qt::DecorationRole);-
178 QStandardItemModel::setData(index, url, UrlRole);-
179 return true;-
180 }-
181 return QStandardItemModel::setData(index, value, role);-
182}-
183-
184void QUrlModel::setUrl(const QModelIndex &index, const QUrl &url, const QModelIndex &dirIndex)-
185{-
186 setData(index, url, UrlRole);-
187 if (url.path().isEmpty()) {-
188 setData(index, fileSystemModel->myComputer());-
189 setData(index, fileSystemModel->myComputer(Qt::DecorationRole), Qt::DecorationRole);-
190 } else {-
191 QString newName;-
192 if (showFullPath) {-
193 //On windows the popup display the "C:\", convert to nativeSeparators-
194 newName = QDir::toNativeSeparators(dirIndex.data(QFileSystemModel::FilePathRole).toString());-
195 } else {-
196 newName = dirIndex.data().toString();-
197 }-
198-
199 QIcon newIcon = qvariant_cast<QIcon>(dirIndex.data(Qt::DecorationRole));-
200 if (!dirIndex.isValid()) {-
201 const QFileIconProvider *provider = fileSystemModel->iconProvider();-
202 if (provider)-
203 newIcon = provider->icon(QFileIconProvider::Folder);-
204 newName = QFileInfo(url.toLocalFile()).fileName();-
205 if (!invalidUrls.contains(url))-
206 invalidUrls.append(url);-
207 //The bookmark is invalid then we set to false the EnabledRole-
208 setData(index, false, EnabledRole);-
209 } else {-
210 //The bookmark is valid then we set to true the EnabledRole-
211 setData(index, true, EnabledRole);-
212 }-
213-
214 // Make sure that we have at least 32x32 images-
215 const QSize size = newIcon.actualSize(QSize(32,32));-
216 if (size.width() < 32) {-
217 QPixmap smallPixmap = newIcon.pixmap(QSize(32, 32));-
218 newIcon.addPixmap(smallPixmap.scaledToWidth(32, Qt::SmoothTransformation));-
219 }-
220-
221 if (index.data().toString() != newName)-
222 setData(index, newName);-
223 QIcon oldIcon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));-
224 if (oldIcon.cacheKey() != newIcon.cacheKey())-
225 setData(index, newIcon, Qt::DecorationRole);-
226 }-
227}-
228-
229void QUrlModel::setUrls(const QList<QUrl> &list)-
230{-
231 removeRows(0, rowCount());-
232 invalidUrls.clear();-
233 watching.clear();-
234 addUrls(list, 0);-
235}-
236-
237/*!-
238 Add urls \a list into the list at \a row. If move then movie-
239 existing ones to row.-
240-
241 \sa dropMimeData()-
242*/-
243void QUrlModel::addUrls(const QList<QUrl> &list, int row, bool move)-
244{-
245 if (row == -1)-
246 row = rowCount();-
247 row = qMin(row, rowCount());-
248 for (int i = list.count() - 1; i >= 0; --i) {-
249 QUrl url = list.at(i);-
250 if (!url.isValid() || url.scheme() != QLatin1String("file"))-
251 continue;-
252 //this makes sure the url is clean-
253 const QString cleanUrl = QDir::cleanPath(url.toLocalFile());-
254 if (!cleanUrl.isEmpty())-
255 url = QUrl::fromLocalFile(cleanUrl);-
256-
257 for (int j = 0; move && j < rowCount(); ++j) {-
258 QString local = index(j, 0).data(UrlRole).toUrl().toLocalFile();-
259#if defined(Q_OS_WIN)-
260 const Qt::CaseSensitivity cs = Qt::CaseInsensitive;-
261#else-
262 const Qt::CaseSensitivity cs = Qt::CaseSensitive;-
263#endif-
264 if (!cleanUrl.compare(local, cs)) {-
265 removeRow(j);-
266 if (j <= row)-
267 row--;-
268 break;-
269 }-
270 }-
271 row = qMax(row, 0);-
272 QModelIndex idx = fileSystemModel->index(cleanUrl);-
273 if (!fileSystemModel->isDir(idx))-
274 continue;-
275 insertRows(row, 1);-
276 setUrl(index(row, 0), url, idx);-
277 watching.append(qMakePair(idx, cleanUrl));-
278 }-
279}-
280-
281/*!-
282 Return the complete list of urls in a QList.-
283*/-
284QList<QUrl> QUrlModel::urls() const-
285{-
286 QList<QUrl> list;-
287 const int numRows = rowCount();-
288 list.reserve(numRows);-
289 for (int i = 0; i < numRows; ++i)-
290 list.append(data(index(i, 0), UrlRole).toUrl());-
291 return list;-
292}-
293-
294/*!-
295 QFileSystemModel to get index's from, clears existing rows-
296*/-
297void QUrlModel::setFileSystemModel(QFileSystemModel *model)-
298{-
299 if (model == fileSystemModel)-
300 return;-
301 if (fileSystemModel != 0) {-
302 disconnect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),-
303 this, SLOT(dataChanged(QModelIndex,QModelIndex)));-
304 disconnect(model, SIGNAL(layoutChanged()),-
305 this, SLOT(layoutChanged()));-
306 disconnect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),-
307 this, SLOT(layoutChanged()));-
308 }-
309 fileSystemModel = model;-
310 if (fileSystemModel != 0) {-
311 connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),-
312 this, SLOT(dataChanged(QModelIndex,QModelIndex)));-
313 connect(model, SIGNAL(layoutChanged()),-
314 this, SLOT(layoutChanged()));-
315 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),-
316 this, SLOT(layoutChanged()));-
317 }-
318 clear();-
319 insertColumns(0, 1);-
320}-
321-
322/*-
323 If one of the index's we are watching has changed update our internal data-
324*/-
325void QUrlModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)-
326{-
327 QModelIndex parent = topLeft.parent();-
328 for (int i = 0; i < watching.count(); ++i) {-
329 QModelIndex index = watching.at(i).first;-
330 if (index.model() && topLeft.model()) {-
331 Q_ASSERT(index.model() == topLeft.model());-
332 }-
333 if ( index.row() >= topLeft.row()-
334 && index.row() <= bottomRight.row()-
335 && index.column() >= topLeft.column()-
336 && index.column() <= bottomRight.column()-
337 && index.parent() == parent) {-
338 changed(watching.at(i).second);-
339 }-
340 }-
341}-
342-
343/*!-
344 Re-get all of our data, anything could have changed!-
345 */-
346void QUrlModel::layoutChanged()-
347{-
348 QStringList paths;-
349 const int numPaths = watching.count();-
350 paths.reserve(numPaths);-
351 for (int i = 0; i < numPaths; ++i)-
352 paths.append(watching.at(i).second);-
353 watching.clear();-
354 for (int i = 0; i < numPaths; ++i) {-
355 QString path = paths.at(i);-
356 QModelIndex newIndex = fileSystemModel->index(path);-
357 watching.append(QPair<QModelIndex, QString>(newIndex, path));-
358 if (newIndex.isValid())-
359 changed(path);-
360 }-
361}-
362-
363/*!-
364 The following path changed data update our copy of that data-
365-
366 \sa layoutChanged(), dataChanged()-
367*/-
368void QUrlModel::changed(const QString &path)-
369{-
370 for (int i = 0; i < rowCount(); ++i) {-
371 QModelIndex idx = index(i, 0);-
372 if (idx.data(UrlRole).toUrl().toLocalFile() == path) {-
373 setData(idx, idx.data(UrlRole).toUrl());-
374 }-
375 }-
376}-
377-
378QSidebar::QSidebar(QWidget *parent) : QListView(parent)-
379{-
380}-
381-
382void QSidebar::setModelAndUrls(QFileSystemModel *model, const QList<QUrl> &newUrls)-
383{-
384 // ### TODO make icon size dynamic-
385 setIconSize(QSize(24,24));-
386 setUniformItemSizes(true);-
387 urlModel = new QUrlModel(this);-
388 urlModel->setFileSystemModel(model);-
389 setModel(urlModel);-
390 setItemDelegate(new QSideBarDelegate(this));-
391-
392 connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),-
393 this, SLOT(clicked(QModelIndex)));-
394#ifndef QT_NO_DRAGANDDROP-
395 setDragDropMode(QAbstractItemView::DragDrop);-
396#endif-
397 setContextMenuPolicy(Qt::CustomContextMenu);-
398 connect(this, SIGNAL(customContextMenuRequested(QPoint)),-
399 this, SLOT(showContextMenu(QPoint)));-
400 urlModel->setUrls(newUrls);-
401 setCurrentIndex(this->model()->index(0,0));-
402}-
403-
404QSidebar::~QSidebar()-
405{-
406}-
407-
408#ifndef QT_NO_DRAGANDDROP-
409void QSidebar::dragEnterEvent(QDragEnterEvent *event)-
410{-
411 if (urlModel->canDrop(event))-
412 QListView::dragEnterEvent(event);-
413}-
414#endif // QT_NO_DRAGANDDROP-
415-
416QSize QSidebar::sizeHint() const-
417{-
418 if (model())-
419 return QListView::sizeHintForIndex(model()->index(0, 0)) + QSize(2 * frameWidth(), 2 * frameWidth());-
420 return QListView::sizeHint();-
421}-
422-
423void QSidebar::selectUrl(const QUrl &url)-
424{-
425 disconnect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),-
426 this, SLOT(clicked(QModelIndex)));-
427-
428 selectionModel()->clear();-
429 for (int i = 0; i < model()->rowCount(); ++i) {-
430 if (model()->index(i, 0).data(QUrlModel::UrlRole).toUrl() == url) {-
431 selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select);-
432 break;-
433 }-
434 }-
435-
436 connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),-
437 this, SLOT(clicked(QModelIndex)));-
438}-
439-
440#ifndef QT_NO_MENU-
441/*!-
442 \internal-
443-
444 \sa removeEntry()-
445*/-
446void QSidebar::showContextMenu(const QPoint &position)-
447{-
448 QList<QAction *> actions;-
449 if (indexAt(position).isValid()) {-
450 QAction *action = new QAction(QFileDialog::tr("Remove"), this);-
451 if (indexAt(position).data(QUrlModel::UrlRole).toUrl().path().isEmpty())-
452 action->setEnabled(false);-
453 connect(action, SIGNAL(triggered()), this, SLOT(removeEntry()));-
454 actions.append(action);-
455 }-
456 if (actions.count() > 0)-
457 QMenu::exec(actions, mapToGlobal(position));-
458}-
459#endif // QT_NO_MENU-
460-
461/*!-
462 \internal-
463-
464 \sa showContextMenu()-
465*/-
466void QSidebar::removeEntry()-
467{-
468 QList<QModelIndex> idxs = selectionModel()->selectedIndexes();-
469 QList<QPersistentModelIndex> indexes;-
470 const int numIndexes = idxs.count();-
471 indexes.reserve(numIndexes);-
472 for (int i = 0; i < numIndexes; i++)-
473 indexes.append(idxs.at(i));-
474-
475 for (int i = 0; i < numIndexes; ++i) {-
476 if (!indexes.at(i).data(QUrlModel::UrlRole).toUrl().path().isEmpty())-
477 model()->removeRow(indexes.at(i).row());-
478 }-
479}-
480-
481/*!-
482 \internal-
483-
484 \sa goToUrl()-
485*/-
486void QSidebar::clicked(const QModelIndex &index)-
487{-
488 QUrl url = model()->index(index.row(), 0).data(QUrlModel::UrlRole).toUrl();-
489 emit goToUrl(url);-
490 selectUrl(url);-
491}-
492-
493/*!-
494 \reimp-
495 Don't automatically select something-
496 */-
497void QSidebar::focusInEvent(QFocusEvent *event)-
498{-
499 QAbstractScrollArea::focusInEvent(event);-
500 viewport()->update();-
501}-
502-
503/*!-
504 \reimp-
505 */-
506bool QSidebar::event(QEvent * event)-
507{-
508 if (event->type() == QEvent::KeyRelease) {-
509 QKeyEvent* ke = (QKeyEvent*) event;-
510 if (ke->key() == Qt::Key_Delete) {-
511 removeEntry();-
512 return true;-
513 }-
514 }-
515 return QListView::event(event);-
516}-
517-
518QT_END_NAMESPACE-
519-
520#include "moc_qsidebar_p.cpp"-
521-
522#endif-
Source codeSwitch to Preprocessed file

Generated by Squish Coco Non-Commercial 4.3.0-BETA-master-30-08-2018-4cb69e9