File indexing completed on 2024-05-19 04:56:03
0001 /** 0002 * \file filesystemmodel.cpp 0003 * \cond 0004 * Taken from Qt Git, revision e73bd4a 0005 * qtbase/src/widgets/dialogs/qfilesystemmodel.cpp 0006 * Adapted for Kid3 with the following changes: 0007 * - Remove Q prefix from class names 0008 * - Remove QT_..._CONFIG, QT_..._NAMESPACE, Q_..._EXPORT... 0009 * - Allow compilation without Qt private headers (USE_QT_PRIVATE_HEADERS) 0010 * - Remove moc includes 0011 * - Remove dependencies to Qt5::Widgets 0012 * - Do not display a message box from setData(), this will crash without GUI 0013 */ 0014 /**************************************************************************** 0015 ** 0016 ** Copyright (C) 2016 The Qt Company Ltd. 0017 ** Contact: https://www.qt.io/licensing/ 0018 ** 0019 ** This file is part of the QtWidgets module of the Qt Toolkit. 0020 ** 0021 ** $QT_BEGIN_LICENSE:LGPL$ 0022 ** Commercial License Usage 0023 ** Licensees holding valid commercial Qt licenses may use this file in 0024 ** accordance with the commercial license agreement provided with the 0025 ** Software or, alternatively, in accordance with the terms contained in 0026 ** a written agreement between you and The Qt Company. For licensing terms 0027 ** and conditions see https://www.qt.io/terms-conditions. For further 0028 ** information use the contact form at https://www.qt.io/contact-us. 0029 ** 0030 ** GNU Lesser General Public License Usage 0031 ** Alternatively, this file may be used under the terms of the GNU Lesser 0032 ** General Public License version 3 as published by the Free Software 0033 ** Foundation and appearing in the file LICENSE.LGPL3 included in the 0034 ** packaging of this file. Please review the following information to 0035 ** ensure the GNU Lesser General Public License version 3 requirements 0036 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 0037 ** 0038 ** GNU General Public License Usage 0039 ** Alternatively, this file may be used under the terms of the GNU 0040 ** General Public License version 2.0 or (at your option) the GNU General 0041 ** Public license version 3 or any later version approved by the KDE Free 0042 ** Qt Foundation. The licenses are as published by the Free Software 0043 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 0044 ** included in the packaging of this file. Please review the following 0045 ** information to ensure the GNU General Public License requirements will 0046 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and 0047 ** https://www.gnu.org/licenses/gpl-3.0.html. 0048 ** 0049 ** $QT_END_LICENSE$ 0050 ** 0051 ****************************************************************************/ 0052 0053 #include "filesystemmodel.h" 0054 #include <qlocale.h> 0055 #include <qmimedata.h> 0056 #include <qurl.h> 0057 #include <qdebug.h> 0058 #include <qcoreevent.h> 0059 #include <QtCore/qcollator.h> 0060 #include <QRegularExpression> 0061 0062 #include <algorithm> 0063 #include <memory> 0064 0065 #ifdef Q_OS_WIN 0066 # include <QtCore/QVarLengthArray> 0067 # include <qt_windows.h> 0068 #endif 0069 0070 #include "abstractfiledecorationprovider.h" 0071 0072 /*! 0073 \enum FileSystemModel::Roles 0074 \value FileIconRole 0075 \value FilePathRole 0076 \value FileNameRole 0077 \value FilePermissions 0078 */ 0079 0080 /*! 0081 \class FileSystemModel 0082 \since 4.4 0083 0084 \brief The QFileSystemModel class provides a data model for the local filesystem. 0085 0086 \ingroup model-view 0087 \inmodule QtWidgets 0088 0089 This class provides access to the local filesystem, providing functions 0090 for renaming and removing files and directories, and for creating new 0091 directories. In the simplest case, it can be used with a suitable display 0092 widget as part of a browser or filter. 0093 0094 QFileSystemModel can be accessed using the standard interface provided by 0095 QAbstractItemModel, but it also provides some convenience functions that are 0096 specific to a directory model. 0097 The fileInfo(), isDir(), fileName() and filePath() functions provide information 0098 about the underlying files and directories related to items in the model. 0099 Directories can be created and removed using mkdir(), rmdir(). 0100 0101 \note QFileSystemModel requires an instance of \l QApplication. 0102 0103 \section1 Example Usage 0104 0105 A directory model that displays the contents of a default directory 0106 is usually constructed with a parent object: 0107 0108 \snippet shareddirmodel/main.cpp 2 0109 0110 A tree view can be used to display the contents of the model 0111 0112 \snippet shareddirmodel/main.cpp 4 0113 0114 and the contents of a particular directory can be displayed by 0115 setting the tree view's root index: 0116 0117 \snippet shareddirmodel/main.cpp 7 0118 0119 The view's root index can be used to control how much of a 0120 hierarchical model is displayed. QFileSystemModel provides a convenience 0121 function that returns a suitable model index for a path to a 0122 directory within the model. 0123 0124 \section1 Caching and Performance 0125 0126 QFileSystemModel will not fetch any files or directories until setRootPath() 0127 is called. This will prevent any unnecessary querying on the file system 0128 until that point such as listing the drives on Windows. 0129 0130 Unlike QDirModel, QFileSystemModel uses a separate thread to populate 0131 itself so it will not cause the main thread to hang as the file system 0132 is being queried. Calls to rowCount() will return 0 until the model 0133 populates a directory. 0134 0135 QFileSystemModel keeps a cache with file information. The cache is 0136 automatically kept up to date using the QFileSystemWatcher. 0137 0138 \sa {Model Classes} 0139 */ 0140 0141 /*! 0142 \fn bool QFileSystemModel::rmdir(const QModelIndex &index) 0143 0144 Removes the directory corresponding to the model item \a index in the 0145 file system model and \b{deletes the corresponding directory from the 0146 file system}, returning true if successful. If the directory cannot be 0147 removed, false is returned. 0148 0149 \warning This function deletes directories from the file system; it does 0150 \b{not} move them to a location where they can be recovered. 0151 0152 \sa remove() 0153 */ 0154 0155 /*! 0156 \fn QIcon QFileSystemModel::fileName(const QModelIndex &index) const 0157 0158 Returns the file name for the item stored in the model under the given 0159 \a index. 0160 */ 0161 0162 /*! 0163 \fn QIcon QFileSystemModel::fileIcon(const QModelIndex &index) const 0164 0165 Returns the icon for the item stored in the model under the given 0166 \a index. 0167 */ 0168 0169 /*! 0170 \fn QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const 0171 0172 Returns the QFileInfo for the item stored in the model under the given 0173 \a index. 0174 */ 0175 QFileInfo FileSystemModel::fileInfo(const QModelIndex &index) const 0176 { 0177 Q_D(const FileSystemModel); 0178 return d->node(index)->fileInfo(); 0179 } 0180 0181 /*! 0182 \fn void QFileSystemModel::rootPathChanged(const QString &newPath); 0183 0184 This signal is emitted whenever the root path has been changed to a \a newPath. 0185 */ 0186 0187 /*! 0188 \fn void QFileSystemModel::fileRenamed(const QString &path, const QString &oldName, const QString &newName) 0189 0190 This signal is emitted whenever a file with the \a oldName is successfully 0191 renamed to \a newName. The file is located in the directory \a path. 0192 */ 0193 0194 /*! 0195 \since 4.7 0196 \fn void QFileSystemModel::directoryLoaded(const QString &path) 0197 0198 This signal is emitted when the gatherer thread has finished to load the \a path. 0199 0200 */ 0201 0202 /*! 0203 \fn bool QFileSystemModel::remove(const QModelIndex &index) 0204 0205 Removes the model item \a index from the file system model and \b{deletes the 0206 corresponding file from the file system}, returning true if successful. If the 0207 item cannot be removed, false is returned. 0208 0209 \warning This function deletes files from the file system; it does \b{not} 0210 move them to a location where they can be recovered. 0211 0212 \sa rmdir() 0213 */ 0214 0215 bool FileSystemModel::remove(const QModelIndex &aindex) 0216 { 0217 Q_D(FileSystemModel); 0218 0219 const QString path = d->filePath(aindex); 0220 const QFileInfo fileInfo(path); 0221 const bool success = fileInfo.isFile() || fileInfo.isSymLink() 0222 ? QFile::remove(path) : QDir(path).removeRecursively(); 0223 #ifndef QT_NO_FILESYSTEMWATCHER 0224 if (success) { 0225 auto dptr = const_cast<FileSystemModelPrivate*>(d_func()); 0226 dptr->fileInfoGatherer.removePath(path); 0227 } 0228 #endif 0229 return success; 0230 } 0231 0232 /*! 0233 Constructs a file system model with the given \a parent. 0234 */ 0235 FileSystemModel::FileSystemModel(QObject *parent) 0236 #ifdef USE_QT_PRIVATE_HEADERS 0237 : QAbstractItemModel(*new FileSystemModelPrivate, parent) 0238 #else 0239 : QAbstractItemModel(parent), 0240 d_ptr(new FileSystemModelPrivate(this)) 0241 #endif 0242 { 0243 Q_D(FileSystemModel); 0244 d->init(); 0245 } 0246 0247 /*! 0248 \internal 0249 */ 0250 FileSystemModel::FileSystemModel(FileSystemModelPrivate &dd, QObject *parent) 0251 #if defined(USE_QT_PRIVATE_HEADERS) 0252 : QAbstractItemModel(dd, parent) 0253 #else 0254 : QAbstractItemModel(parent), 0255 d_ptr(&dd) 0256 #endif 0257 { 0258 Q_D(FileSystemModel); 0259 d->init(); 0260 } 0261 0262 /*! 0263 Destroys this file system model. 0264 */ 0265 FileSystemModel::~FileSystemModel() 0266 { 0267 } 0268 0269 /*! 0270 \reimp 0271 */ 0272 QModelIndex FileSystemModel::index(int row, int column, const QModelIndex &parent) const 0273 { 0274 Q_D(const FileSystemModel); 0275 if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent)) 0276 return QModelIndex(); 0277 0278 // get the parent node 0279 FileSystemModelPrivate::FileSystemNode *parentNode = d->indexValid(parent) ? d->node(parent) : 0280 const_cast<FileSystemModelPrivate::FileSystemNode*>(&d->root); 0281 Q_ASSERT(parentNode); 0282 0283 // now get the internal pointer for the index 0284 const int i = d->translateVisibleLocation(parentNode, row); 0285 if (i >= parentNode->visibleChildren.size()) 0286 return QModelIndex(); 0287 const QString &childName = parentNode->visibleChildren.at(i); 0288 const FileSystemModelPrivate::FileSystemNode *indexNode = parentNode->children.value(childName); 0289 Q_ASSERT(indexNode); 0290 0291 return createIndex(row, column, const_cast<FileSystemModelPrivate::FileSystemNode*>(indexNode)); 0292 } 0293 0294 /*! 0295 \reimp 0296 */ 0297 QModelIndex FileSystemModel::sibling(int row, int column, const QModelIndex &idx) const 0298 { 0299 if (row == idx.row() && column < FileSystemModelPrivate::NumColumns) { 0300 // cheap sibling operation: just adjust the column: 0301 return createIndex(row, column, idx.internalPointer()); 0302 } 0303 // for anything else: call the default implementation 0304 // (this could probably be optimized, too): 0305 return QAbstractItemModel::sibling(row, column, idx); 0306 } 0307 0308 /*! 0309 \overload 0310 0311 Returns the model item index for the given \a path and \a column. 0312 */ 0313 QModelIndex FileSystemModel::index(const QString &path, int column) const 0314 { 0315 Q_D(const FileSystemModel); 0316 FileSystemModelPrivate::FileSystemNode *node = d->node(path, false); 0317 return d->index(node, column); 0318 } 0319 0320 /*! 0321 \internal 0322 0323 Return the QFileSystemNode that goes to index. 0324 */ 0325 FileSystemModelPrivate::FileSystemNode *FileSystemModelPrivate::node(const QModelIndex &index) const 0326 { 0327 if (!index.isValid()) 0328 return const_cast<FileSystemNode*>(&root); 0329 auto indexNode = static_cast<FileSystemModelPrivate::FileSystemNode*>(index.internalPointer()); 0330 Q_ASSERT(indexNode); 0331 return indexNode; 0332 } 0333 0334 #ifdef Q_OS_WIN32 0335 static QString qt_GetLongPathName(const QString &strShortPath) 0336 { 0337 if (strShortPath.isEmpty() 0338 || strShortPath == QLatin1String(".") || strShortPath == QLatin1String("..")) 0339 return strShortPath; 0340 if (strShortPath.length() == 2 && strShortPath.endsWith(QLatin1Char(':'))) 0341 return strShortPath.toUpper(); 0342 const QString absPath = QDir(strShortPath).absolutePath(); 0343 if (absPath.startsWith(QLatin1String("//")) 0344 || absPath.startsWith(QLatin1String("\\\\"))) // unc 0345 return QDir::fromNativeSeparators(absPath); 0346 if (absPath.startsWith(QLatin1Char('/'))) 0347 return QString(); 0348 const QString inputString = QLatin1String("\\\\?\\") + QDir::toNativeSeparators(absPath); 0349 // Kid3 WCHAR instead of TCHAR, GetLongPathNameW instead of GetLongPathName 0350 QVarLengthArray<WCHAR, MAX_PATH> buffer(MAX_PATH); 0351 DWORD result = ::GetLongPathNameW((wchar_t*)inputString.utf16(), 0352 buffer.data(), 0353 buffer.size()); 0354 if (result > DWORD(buffer.size())) { 0355 buffer.resize(result); 0356 result = ::GetLongPathNameW((wchar_t*)inputString.utf16(), 0357 buffer.data(), 0358 buffer.size()); 0359 } 0360 if (result > 4) { 0361 QString longPath = QString::fromWCharArray(buffer.data() + 4); // ignoring prefix 0362 longPath[0] = longPath.at(0).toUpper(); // capital drive letters 0363 return QDir::fromNativeSeparators(longPath); 0364 } else { 0365 return QDir::fromNativeSeparators(strShortPath); 0366 } 0367 } 0368 #endif 0369 0370 /*! 0371 \internal 0372 0373 Given a path return the matching QFileSystemNode or &root if invalid 0374 */ 0375 FileSystemModelPrivate::FileSystemNode *FileSystemModelPrivate::node(const QString &path, bool fetch) const 0376 { 0377 Q_Q(const FileSystemModel); 0378 Q_UNUSED(q); 0379 if (path.isEmpty() || path == myComputer() || path.startsWith(QLatin1Char(':'))) 0380 return const_cast<FileSystemModelPrivate::FileSystemNode*>(&root); 0381 0382 // Construct the nodes up to the new root path if they need to be built 0383 QString absolutePath; 0384 #ifdef Q_OS_WIN32 0385 QString longPath = qt_GetLongPathName(path); 0386 #else 0387 QString longPath = path; 0388 #endif 0389 if (longPath == rootDir.path()) 0390 absolutePath = rootDir.absolutePath(); 0391 else 0392 absolutePath = QDir(longPath).absolutePath(); 0393 0394 // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const? 0395 #if QT_VERSION >= 0x050e00 0396 QStringList pathElements = absolutePath.split(QLatin1Char('/'), Qt::SkipEmptyParts); 0397 #else 0398 QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts); 0399 #endif 0400 if (pathElements.isEmpty() 0401 #if !defined(Q_OS_WIN) 0402 && QDir::fromNativeSeparators(longPath) != QLatin1String("/") 0403 #endif 0404 ) 0405 return const_cast<FileSystemModelPrivate::FileSystemNode*>(&root); 0406 auto index = QModelIndex(); // start with "My Computer" 0407 QString elementPath; 0408 QChar separator = QLatin1Char('/'); 0409 QString trailingSeparator; 0410 #if defined(Q_OS_WIN) 0411 if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path 0412 QString host = QLatin1String("\\\\") + pathElements.constFirst(); 0413 if (absolutePath == QDir::fromNativeSeparators(host)) 0414 absolutePath.append(QLatin1Char('/')); 0415 if (longPath.endsWith(QLatin1Char('/')) && !absolutePath.endsWith(QLatin1Char('/'))) 0416 absolutePath.append(QLatin1Char('/')); 0417 if (absolutePath.endsWith(QLatin1Char('/'))) 0418 trailingSeparator = QLatin1String("\\"); 0419 int r = 0; 0420 FileSystemModelPrivate::FileSystemNode *rootNode = const_cast<FileSystemModelPrivate::FileSystemNode*>(&root); 0421 if (!root.children.contains(host.toLower())) { 0422 if (pathElements.count() == 1 && !absolutePath.endsWith(QLatin1Char('/'))) 0423 return rootNode; 0424 QFileInfo info(host); 0425 if (!info.exists()) 0426 return rootNode; 0427 FileSystemModelPrivate *p = const_cast<FileSystemModelPrivate*>(this); 0428 p->addNode(rootNode, host,info); 0429 p->addVisibleFiles(rootNode, QStringList(host)); 0430 } 0431 r = rootNode->visibleLocation(host); 0432 r = translateVisibleLocation(rootNode, r); 0433 index = q->index(r, 0, QModelIndex()); 0434 pathElements.pop_front(); 0435 separator = QLatin1Char('\\'); 0436 elementPath = host; 0437 elementPath.append(separator); 0438 } else { 0439 if (!pathElements.at(0).contains(QLatin1Char(':'))) { 0440 QString rootPath = QDir(longPath).rootPath(); 0441 pathElements.prepend(rootPath); 0442 } 0443 if (pathElements.at(0).endsWith(QLatin1Char('/'))) 0444 pathElements[0].chop(1); 0445 } 0446 #else 0447 // add the "/" item, since it is a valid path element on Unix 0448 if (absolutePath[0] == QLatin1Char('/')) 0449 pathElements.prepend(QLatin1String("/")); 0450 #endif 0451 0452 FileSystemModelPrivate::FileSystemNode *parent = node(index); 0453 0454 for (int i = 0; i < pathElements.count(); ++i) { 0455 QString element = pathElements.at(i); 0456 if (i != 0) 0457 elementPath.append(separator); 0458 elementPath.append(element); 0459 if (i == pathElements.count() - 1) 0460 elementPath.append(trailingSeparator); 0461 #ifdef Q_OS_WIN 0462 // On Windows, "filename " and "filename" are equivalent and 0463 // "filename . " and "filename" are equivalent 0464 // "filename......." and "filename" are equivalent Task #133928 0465 // whereas "filename .txt" is still "filename .txt" 0466 // If after stripping the characters there is nothing left then we 0467 // just return the parent directory as it is assumed that the path 0468 // is referring to the parent 0469 while (element.endsWith(QLatin1Char('.')) || element.endsWith(QLatin1Char(' '))) 0470 element.chop(1); 0471 // Only filenames that can't possibly exist will be end up being empty 0472 if (element.isEmpty()) 0473 return parent; 0474 #endif 0475 bool alreadyExisted = parent->children.contains(element); 0476 0477 // we couldn't find the path element, we create a new node since we 0478 // _know_ that the path is valid 0479 if (alreadyExisted) { 0480 if (parent->children.count() == 0 0481 || (parent->caseSensitive() 0482 && parent->children.value(element)->fileName != element) 0483 || (!parent->caseSensitive() 0484 && parent->children.value(element)->fileName.toLower() != element.toLower())) 0485 alreadyExisted = false; 0486 } 0487 0488 FileSystemModelPrivate::FileSystemNode *node; 0489 if (!alreadyExisted) { 0490 // Someone might call ::index("file://cookie/monster/doesn't/like/veggies"), 0491 // a path that doesn't exists, I.E. don't blindly create directories. 0492 QFileInfo info(elementPath); 0493 if (!info.exists()) 0494 return const_cast<FileSystemModelPrivate::FileSystemNode*>(&root); 0495 auto p = const_cast<FileSystemModelPrivate*>(this); 0496 node = p->addNode(parent, element,info); 0497 #ifndef QT_NO_FILESYSTEMWATCHER 0498 node->populate(fileInfoGatherer.getInfo(info)); 0499 #endif 0500 } else { 0501 node = parent->children.value(element); 0502 } 0503 0504 Q_ASSERT(node); 0505 if (!node->isVisible) { 0506 // It has been filtered out 0507 if (alreadyExisted && node->hasInformation() && !fetch) 0508 return const_cast<FileSystemModelPrivate::FileSystemNode*>(&root); 0509 0510 auto p = const_cast<FileSystemModelPrivate*>(this); 0511 p->addVisibleFiles(parent, QStringList(element)); 0512 if (!p->bypassFilters.contains(node)) 0513 p->bypassFilters[node] = true; 0514 QString dir = q->filePath(this->index(parent)); 0515 if (!node->hasInformation() && fetch) { 0516 Fetching f = { std::move(dir), std::move(element), node }; 0517 p->toFetch.append(std::move(f)); 0518 p->fetchingTimer.start(0, const_cast<FileSystemModel*>(q)); 0519 } 0520 } 0521 parent = node; 0522 } 0523 0524 return parent; 0525 } 0526 0527 /*! 0528 \reimp 0529 */ 0530 void FileSystemModel::timerEvent(QTimerEvent *event) 0531 { 0532 Q_D(FileSystemModel); 0533 if (event->timerId() == d->fetchingTimer.timerId()) { 0534 d->fetchingTimer.stop(); 0535 #ifndef QT_NO_FILESYSTEMWATCHER 0536 for (int i = 0; i < d->toFetch.count(); ++i) { 0537 if (const FileSystemModelPrivate::FileSystemNode *node = d->toFetch.at(i).node; 0538 !node->hasInformation()) { 0539 d->fileInfoGatherer.fetchExtendedInformation(d->toFetch.at(i).dir, 0540 QStringList(d->toFetch.at(i).file)); 0541 } else { 0542 // qDebug("yah!, you saved a little gerbil soul"); 0543 } 0544 } 0545 #endif 0546 d->toFetch.clear(); 0547 } 0548 } 0549 0550 /*! 0551 Returns \c true if the model item \a index represents a directory; 0552 otherwise returns \c false. 0553 */ 0554 bool FileSystemModel::isDir(const QModelIndex &index) const 0555 { 0556 // This function is for public usage only because it could create a file info 0557 Q_D(const FileSystemModel); 0558 if (!index.isValid()) 0559 return true; 0560 if (FileSystemModelPrivate::FileSystemNode *n = d->node(index); 0561 n->hasInformation()) 0562 return n->isDir(); 0563 return fileInfo(index).isDir(); 0564 } 0565 0566 /*! 0567 Returns the size in bytes of \a index. If the file does not exist, 0 is returned. 0568 */ 0569 qint64 FileSystemModel::size(const QModelIndex &index) const 0570 { 0571 Q_D(const FileSystemModel); 0572 if (!index.isValid()) 0573 return 0; 0574 return d->node(index)->size(); 0575 } 0576 0577 /*! 0578 Returns the type of file \a index such as "Directory" or "JPEG file". 0579 */ 0580 QString FileSystemModel::type(const QModelIndex &index) const 0581 { 0582 Q_D(const FileSystemModel); 0583 if (!index.isValid()) 0584 return QString(); 0585 return d->node(index)->type(); 0586 } 0587 0588 /*! 0589 Returns the date and time when \a index was last modified. 0590 */ 0591 QDateTime FileSystemModel::lastModified(const QModelIndex &index) const 0592 { 0593 Q_D(const FileSystemModel); 0594 if (!index.isValid()) 0595 return QDateTime(); 0596 return d->node(index)->lastModified(); 0597 } 0598 0599 /*! 0600 \reimp 0601 */ 0602 QModelIndex FileSystemModel::parent(const QModelIndex &index) const 0603 { 0604 Q_D(const FileSystemModel); 0605 if (!d->indexValid(index)) 0606 return QModelIndex(); 0607 0608 FileSystemModelPrivate::FileSystemNode *indexNode = d->node(index); 0609 Q_ASSERT(indexNode != nullptr); 0610 FileSystemModelPrivate::FileSystemNode *parentNode = indexNode->parent; 0611 if (parentNode == nullptr || parentNode == &d->root) 0612 return QModelIndex(); 0613 0614 // get the parent's row 0615 FileSystemModelPrivate::FileSystemNode *grandParentNode = parentNode->parent; 0616 Q_ASSERT(grandParentNode->children.contains(parentNode->fileName)); 0617 int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName)); 0618 if (visualRow == -1) 0619 return QModelIndex(); 0620 return createIndex(visualRow, 0, parentNode); 0621 } 0622 0623 /* 0624 \internal 0625 0626 return the index for node 0627 */ 0628 QModelIndex FileSystemModelPrivate::index(const FileSystemModelPrivate::FileSystemNode *node, int column) const 0629 { 0630 Q_Q(const FileSystemModel); 0631 FileSystemModelPrivate::FileSystemNode *parentNode = node ? node->parent : nullptr; 0632 if (node == &root || !parentNode) 0633 return QModelIndex(); 0634 0635 // get the parent's row 0636 Q_ASSERT(node); 0637 if (!node->isVisible) 0638 return QModelIndex(); 0639 0640 int visualRow = translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName)); 0641 return q->createIndex(visualRow, column, const_cast<FileSystemNode*>(node)); 0642 } 0643 0644 /*! 0645 \reimp 0646 */ 0647 bool FileSystemModel::hasChildren(const QModelIndex &parent) const 0648 { 0649 Q_D(const FileSystemModel); 0650 if (parent.column() > 0) 0651 return false; 0652 0653 if (!parent.isValid()) // drives 0654 return true; 0655 0656 const FileSystemModelPrivate::FileSystemNode *indexNode = d->node(parent); 0657 Q_ASSERT(indexNode); 0658 return indexNode->isDir(); 0659 } 0660 0661 /*! 0662 \reimp 0663 */ 0664 bool FileSystemModel::canFetchMore(const QModelIndex &parent) const 0665 { 0666 Q_D(const FileSystemModel); 0667 const FileSystemModelPrivate::FileSystemNode *indexNode = d->node(parent); 0668 return !indexNode->populatedChildren; 0669 } 0670 0671 /*! 0672 \reimp 0673 */ 0674 void FileSystemModel::fetchMore(const QModelIndex &parent) 0675 { 0676 Q_D(FileSystemModel); 0677 if (!d->setRootPath) 0678 return; 0679 FileSystemModelPrivate::FileSystemNode *indexNode = d->node(parent); 0680 if (indexNode->populatedChildren) 0681 return; 0682 indexNode->populatedChildren = true; 0683 #ifndef QT_NO_FILESYSTEMWATCHER 0684 d->fileInfoGatherer.list(filePath(parent)); 0685 #endif 0686 } 0687 0688 /*! 0689 \reimp 0690 */ 0691 int FileSystemModel::rowCount(const QModelIndex &parent) const 0692 { 0693 Q_D(const FileSystemModel); 0694 if (parent.column() > 0) 0695 return 0; 0696 0697 if (!parent.isValid()) 0698 return d->root.visibleChildren.count(); 0699 0700 const FileSystemModelPrivate::FileSystemNode *parentNode = d->node(parent); 0701 return parentNode->visibleChildren.count(); 0702 } 0703 0704 /*! 0705 \reimp 0706 */ 0707 int FileSystemModel::columnCount(const QModelIndex &parent) const 0708 { 0709 return parent.column() > 0 ? 0 : FileSystemModelPrivate::NumColumns; 0710 } 0711 0712 /*! 0713 Returns the data stored under the given \a role for the item "My Computer". 0714 0715 \sa Qt::ItemDataRole 0716 */ 0717 QVariant FileSystemModel::myComputer(int role) const 0718 { 0719 #ifndef QT_NO_FILESYSTEMWATCHER 0720 Q_D(const FileSystemModel); 0721 #endif 0722 switch (role) { 0723 case Qt::DisplayRole: 0724 return FileSystemModelPrivate::myComputer(); 0725 #ifndef QT_NO_FILESYSTEMWATCHER 0726 case Qt::DecorationRole: 0727 return d->fileInfoGatherer.decorationProvider() 0728 ? d->fileInfoGatherer.decorationProvider()->computerDecoration() 0729 : QVariant(); 0730 #endif 0731 } 0732 return QVariant(); 0733 } 0734 0735 /*! 0736 \reimp 0737 */ 0738 QVariant FileSystemModel::data(const QModelIndex &index, int role) const 0739 { 0740 Q_D(const FileSystemModel); 0741 if (!index.isValid() || index.model() != this) 0742 return QVariant(); 0743 0744 switch (role) { 0745 case Qt::EditRole: 0746 case Qt::DisplayRole: 0747 switch (index.column()) { 0748 case 0: return d->displayName(index); 0749 case 1: return d->size(index); 0750 case 2: return d->type(index); 0751 case 3: return d->time(index); 0752 default: 0753 qWarning("data: invalid display value column %d", index.column()); 0754 break; 0755 } 0756 break; 0757 case FilePathRole: 0758 return filePath(index); 0759 case FileNameRole: 0760 return d->name(index); 0761 case Qt::DecorationRole: 0762 if (index.column() == 0) { 0763 QVariant icon = d->icon(index); 0764 #ifndef QT_NO_FILESYSTEMWATCHER 0765 if (icon.isNull()) { 0766 if (d->node(index)->isDir()) 0767 icon = d->fileInfoGatherer.decorationProvider() 0768 ? d->fileInfoGatherer.decorationProvider()->folderDecoration() 0769 : QVariant(); 0770 else 0771 icon = d->fileInfoGatherer.decorationProvider() 0772 ? d->fileInfoGatherer.decorationProvider()->fileDecoration() 0773 : QVariant(); 0774 } 0775 #endif // QT_NO_FILESYSTEMWATCHER 0776 return icon; 0777 } 0778 break; 0779 case Qt::TextAlignmentRole: 0780 if (index.column() == 1) 0781 return QVariant(Qt::AlignTrailing | Qt::AlignVCenter); 0782 break; 0783 case FilePermissions: 0784 int p = permissions(index); 0785 return p; 0786 } 0787 0788 return QVariant(); 0789 } 0790 0791 /*! 0792 \internal 0793 */ 0794 QString FileSystemModelPrivate::size(const QModelIndex &index) const 0795 { 0796 if (!index.isValid()) 0797 return QString(); 0798 const FileSystemNode *n = node(index); 0799 if (n->isDir()) { 0800 #ifdef Q_OS_MAC 0801 return QLatin1String("--"); 0802 #else 0803 return QLatin1String(""); 0804 #endif 0805 // Windows - "" 0806 // OS X - "--" 0807 // Konqueror - "4 KB" 0808 // Nautilus - "9 items" (the number of children) 0809 } 0810 return size(n->size()); 0811 } 0812 0813 QString FileSystemModelPrivate::size(qint64 bytes) 0814 { 0815 // According to the Si standard KB is 1000 bytes, KiB is 1024 0816 // but on windows sizes are calculated by dividing by 1024 so we do what they do. 0817 constexpr qint64 kb = 1024; 0818 constexpr qint64 mb = 1024 * kb; 0819 constexpr qint64 gb = 1024 * mb; 0820 constexpr qint64 tb = 1024 * gb; 0821 if (bytes >= tb) 0822 return FileSystemModel::tr("%1 TB").arg(QLocale().toString(static_cast<qreal>(bytes) / tb, 'f', 3)); 0823 if (bytes >= gb) 0824 return FileSystemModel::tr("%1 GB").arg(QLocale().toString(static_cast<qreal>(bytes) / gb, 'f', 2)); 0825 if (bytes >= mb) 0826 return FileSystemModel::tr("%1 MB").arg(QLocale().toString(static_cast<qreal>(bytes) / mb, 'f', 1)); 0827 if (bytes >= kb) 0828 return FileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb)); 0829 return FileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes)); 0830 } 0831 0832 /*! 0833 \internal 0834 */ 0835 QString FileSystemModelPrivate::time(const QModelIndex &index) const 0836 { 0837 if (!index.isValid()) 0838 return QString(); 0839 #ifndef QT_NO_DATESTRING 0840 return QLocale::system().toString(node(index)->lastModified(), QLocale::ShortFormat); 0841 #else 0842 Q_UNUSED(index); 0843 return QString(); 0844 #endif 0845 } 0846 0847 /* 0848 \internal 0849 */ 0850 QString FileSystemModelPrivate::type(const QModelIndex &index) const 0851 { 0852 if (!index.isValid()) 0853 return QString(); 0854 return node(index)->type(); 0855 } 0856 0857 /*! 0858 \internal 0859 */ 0860 QString FileSystemModelPrivate::name(const QModelIndex &index) const 0861 { 0862 if (!index.isValid()) 0863 return QString(); 0864 FileSystemNode *dirNode = node(index); 0865 if ( 0866 #ifndef QT_NO_FILESYSTEMWATCHER 0867 fileInfoGatherer.resolveSymlinks() && 0868 #endif 0869 !resolvedSymLinks.isEmpty() && dirNode->isSymLink(/* ignoreNtfsSymLinks = */ true)) { 0870 QString fullPath = QDir::fromNativeSeparators(filePath(index)); 0871 return resolvedSymLinks.value(fullPath, dirNode->fileName); 0872 } 0873 return dirNode->fileName; 0874 } 0875 0876 /*! 0877 \internal 0878 */ 0879 QString FileSystemModelPrivate::displayName(const QModelIndex &index) const 0880 { 0881 #if defined(Q_OS_WIN) 0882 FileSystemNode *dirNode = node(index); 0883 if (!dirNode->volumeName.isNull()) 0884 return dirNode->volumeName + QLatin1String(" (") + name(index) + QLatin1Char(')'); 0885 #endif 0886 return name(index); 0887 } 0888 0889 /*! 0890 \internal 0891 */ 0892 QVariant FileSystemModelPrivate::icon(const QModelIndex &index) const 0893 { 0894 if (!index.isValid()) 0895 return QVariant(); 0896 return node(index)->icon(); 0897 } 0898 0899 /*! 0900 \reimp 0901 */ 0902 bool FileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role) 0903 { 0904 Q_D(FileSystemModel); 0905 if (!idx.isValid() 0906 || idx.column() != 0 0907 || role != Qt::EditRole 0908 || (flags(idx) & Qt::ItemIsEditable) == 0) { 0909 return false; 0910 } 0911 0912 QString newName = value.toString(); 0913 QString oldName = idx.data().toString(); 0914 if (newName == oldName) 0915 return true; 0916 0917 if (const QString parentPath = filePath(parent(idx)); 0918 newName.isEmpty() 0919 || QDir::toNativeSeparators(newName).contains(QDir::separator()) 0920 || !QDir(parentPath).rename(oldName, newName)) { 0921 emit fileRenameFailed(parentPath, oldName, newName); 0922 return false; 0923 } else { 0924 /* 0925 *After re-naming something we don't want the selection to change* 0926 - can't remove rows and later insert 0927 - can't quickly remove and insert 0928 - index pointer can't change because treeview doesn't use persistant index's 0929 0930 - if this get any more complicated think of changing it to just 0931 use layoutChanged 0932 */ 0933 0934 FileSystemModelPrivate::FileSystemNode *indexNode = d->node(idx); 0935 FileSystemModelPrivate::FileSystemNode *parentNode = indexNode->parent; 0936 int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName); 0937 0938 parentNode->visibleChildren.removeAt(visibleLocation); 0939 std::unique_ptr<FileSystemModelPrivate::FileSystemNode> nodeToRename(parentNode->children.take(oldName)); 0940 nodeToRename->fileName = newName; 0941 nodeToRename->parent = parentNode; 0942 #ifndef QT_NO_FILESYSTEMWATCHER 0943 nodeToRename->populate(d->fileInfoGatherer.getInfo(QFileInfo(parentPath, newName))); 0944 if (nodeToRename->isDir()) { 0945 // Update watcher when directory is renamed 0946 d->fileInfoGatherer.removePath(parentPath + QLatin1Char('/') + oldName); 0947 d->fileInfoGatherer.addPath(parentPath + QLatin1Char('/') + newName); 0948 } 0949 #endif 0950 nodeToRename->isVisible = true; 0951 parentNode->children[newName] = nodeToRename.release(); 0952 parentNode->visibleChildren.insert(visibleLocation, newName); 0953 0954 d->delayedSort(); 0955 emit fileRenamed(parentPath, oldName, newName); 0956 } 0957 return true; 0958 } 0959 0960 /*! 0961 \reimp 0962 */ 0963 QVariant FileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const 0964 { 0965 switch (role) { 0966 case Qt::DecorationRole: 0967 if (section == 0) { 0968 Q_D(const FileSystemModel); 0969 return d->fileInfoGatherer.decorationProvider() 0970 ? d->fileInfoGatherer.decorationProvider()->headerDecoration() 0971 : QVariant(); 0972 } 0973 break; 0974 case Qt::TextAlignmentRole: 0975 return Qt::AlignLeft; 0976 } 0977 0978 if (orientation != Qt::Horizontal || role != Qt::DisplayRole) 0979 return QAbstractItemModel::headerData(section, orientation, role); 0980 0981 QString returnValue; 0982 switch (section) { 0983 case 0: returnValue = tr("Name"); 0984 break; 0985 case 1: returnValue = tr("Size"); 0986 break; 0987 case 2: returnValue = 0988 #ifdef Q_OS_MAC 0989 tr("Kind", "Match OS X Finder"); 0990 #else 0991 tr("Type", "All other platforms"); 0992 #endif 0993 break; 0994 // Windows - Type 0995 // OS X - Kind 0996 // Konqueror - File Type 0997 // Nautilus - Type 0998 case 3: returnValue = tr("Date Modified"); 0999 break; 1000 default: return QVariant(); 1001 } 1002 return returnValue; 1003 } 1004 1005 /*! 1006 \reimp 1007 */ 1008 Qt::ItemFlags FileSystemModel::flags(const QModelIndex &index) const 1009 { 1010 Q_D(const FileSystemModel); 1011 Qt::ItemFlags flags = QAbstractItemModel::flags(index); 1012 if (!index.isValid()) 1013 return flags; 1014 1015 FileSystemModelPrivate::FileSystemNode *indexNode = d->node(index); 1016 if (d->nameFilterDisables && !d->passNameFilters(indexNode)) { 1017 flags &= ~Qt::ItemIsEnabled; 1018 // ### TODO you shouldn't be able to set this as the current item, task 119433 1019 return flags; 1020 } 1021 1022 flags |= Qt::ItemIsDragEnabled; 1023 if (d->readOnly) 1024 return flags; 1025 if (index.column() == 0 && indexNode->permissions() & QFile::WriteUser) { 1026 flags |= Qt::ItemIsEditable; 1027 if (indexNode->isDir()) 1028 flags |= Qt::ItemIsDropEnabled; 1029 else 1030 flags |= Qt::ItemNeverHasChildren; 1031 } 1032 return flags; 1033 } 1034 1035 /*! 1036 \internal 1037 */ 1038 void FileSystemModelPrivate::_q_performDelayedSort() 1039 { 1040 Q_Q(FileSystemModel); 1041 q->sort(sortColumn, sortOrder); 1042 } 1043 1044 1045 /* 1046 \internal 1047 Helper functor used by sort() 1048 */ 1049 class FileSystemModelSorter 1050 { 1051 public: 1052 explicit FileSystemModelSorter(int column, bool ignorePunctuation = false) : sortColumn(column) 1053 { 1054 naturalCompare.setIgnorePunctuation(ignorePunctuation); 1055 naturalCompare.setNumericMode(true); 1056 naturalCompare.setCaseSensitivity(Qt::CaseInsensitive); 1057 } 1058 1059 bool compareNodes(const FileSystemModelPrivate::FileSystemNode *l, 1060 const FileSystemModelPrivate::FileSystemNode *r) const 1061 { 1062 switch (sortColumn) { 1063 case 0: { 1064 #ifndef Q_OS_MAC 1065 // place directories before files 1066 bool left = l->isDir(); 1067 bool right = r->isDir(); 1068 if (left ^ right) 1069 return left; 1070 #endif 1071 return naturalCompare.compare(l->fileName, r->fileName) < 0; 1072 } 1073 case 1: 1074 { 1075 // Directories go first 1076 bool left = l->isDir(); 1077 bool right = r->isDir(); 1078 if (left ^ right) 1079 return left; 1080 1081 qint64 sizeDifference = l->size() - r->size(); 1082 if (sizeDifference == 0) 1083 return naturalCompare.compare(l->fileName, r->fileName) < 0; 1084 1085 return sizeDifference < 0; 1086 } 1087 case 2: 1088 { 1089 int compare = naturalCompare.compare(l->type(), r->type()); 1090 if (compare == 0) 1091 return naturalCompare.compare(l->fileName, r->fileName) < 0; 1092 1093 return compare < 0; 1094 } 1095 case 3: 1096 { 1097 if (l->lastModified() == r->lastModified()) 1098 return naturalCompare.compare(l->fileName, r->fileName) < 0; 1099 1100 return l->lastModified() < r->lastModified(); 1101 } 1102 } 1103 Q_ASSERT(false); 1104 return false; 1105 } 1106 1107 bool operator()(const FileSystemModelPrivate::FileSystemNode *l, 1108 const FileSystemModelPrivate::FileSystemNode *r) const 1109 { 1110 return compareNodes(l, r); 1111 } 1112 1113 1114 private: 1115 QCollator naturalCompare; 1116 int sortColumn; 1117 }; 1118 1119 /* 1120 \internal 1121 1122 Sort all of the children of parent 1123 */ 1124 void FileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent) 1125 { 1126 Q_Q(FileSystemModel); 1127 FileSystemModelPrivate::FileSystemNode *indexNode = node(parent); 1128 if (indexNode->children.count() == 0) 1129 return; 1130 1131 QVector<FileSystemModelPrivate::FileSystemNode*> values; 1132 1133 for (auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) { 1134 if (filtersAcceptsNode(iterator.value())) { 1135 values.append(iterator.value()); 1136 } else { 1137 iterator.value()->isVisible = false; 1138 } 1139 } 1140 FileSystemModelSorter ms(column, sortIgnoringPunctuation); 1141 std::sort(values.begin(), values.end(), ms); 1142 // First update the new visible list 1143 indexNode->visibleChildren.clear(); 1144 //No more dirty item we reset our internal dirty index 1145 indexNode->dirtyChildrenIndex = -1; 1146 const int numValues = values.count(); 1147 indexNode->visibleChildren.reserve(numValues); 1148 for (int i = 0; i < numValues; ++i) { 1149 indexNode->visibleChildren.append(values.at(i)->fileName); 1150 values.at(i)->isVisible = true; 1151 } 1152 1153 if (!disableRecursiveSort) { 1154 for (int i = 0; i < q->rowCount(parent); ++i) { 1155 const QModelIndex childIndex = q->index(i, 0, parent); 1156 //Only do a recursive sort on visible nodes 1157 if (FileSystemModelPrivate::FileSystemNode *childIndexNode = node(childIndex); 1158 childIndexNode->isVisible) 1159 sortChildren(column, childIndex); 1160 } 1161 } 1162 } 1163 1164 /*! 1165 \reimp 1166 */ 1167 void FileSystemModel::sort(int column, Qt::SortOrder order) 1168 { 1169 Q_D(FileSystemModel); 1170 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort) 1171 return; 1172 1173 emit layoutAboutToBeChanged(); 1174 QModelIndexList oldList = persistentIndexList(); 1175 QVector<QPair<FileSystemModelPrivate::FileSystemNode*, int> > oldNodes; 1176 const int nodeCount = oldList.count(); 1177 oldNodes.reserve(nodeCount); 1178 for (int i = 0; i < nodeCount; ++i) { 1179 const QModelIndex &oldNode = oldList.at(i); 1180 QPair<FileSystemModelPrivate::FileSystemNode*, int> pair(d->node(oldNode), oldNode.column()); 1181 oldNodes.append(pair); 1182 } 1183 1184 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) { 1185 //we sort only from where we are, don't need to sort all the model 1186 d->sortChildren(column, index(rootPath())); 1187 d->sortColumn = column; 1188 d->forceSort = false; 1189 } 1190 d->sortOrder = order; 1191 1192 QModelIndexList newList; 1193 const int numOldNodes = oldNodes.size(); 1194 newList.reserve(numOldNodes); 1195 for (int i = 0; i < numOldNodes; ++i) { 1196 const QPair<FileSystemModelPrivate::FileSystemNode*, int> &oldNode = oldNodes.at(i); 1197 newList.append(d->index(oldNode.first, oldNode.second)); 1198 } 1199 changePersistentIndexList(oldList, newList); 1200 emit layoutChanged(); 1201 } 1202 1203 /*! 1204 Returns a list of MIME types that can be used to describe a list of items 1205 in the model. 1206 */ 1207 QStringList FileSystemModel::mimeTypes() const 1208 { 1209 return QStringList(QLatin1String("text/uri-list")); 1210 } 1211 1212 /*! 1213 Returns an object that contains a serialized description of the specified 1214 \a indexes. The format used to describe the items corresponding to the 1215 indexes is obtained from the mimeTypes() function. 1216 1217 If the list of indexes is empty, 0 is returned rather than a serialized 1218 empty list. 1219 */ 1220 QMimeData *FileSystemModel::mimeData(const QModelIndexList &indexes) const 1221 { 1222 QList<QUrl> urls; 1223 for (QList<QModelIndex>::const_iterator it = indexes.begin(); 1224 it != indexes.end(); ++it) 1225 if (it->column() == 0) 1226 urls << QUrl::fromLocalFile(filePath(*it)); 1227 auto data = new QMimeData(); 1228 data->setUrls(urls); 1229 return data; 1230 } 1231 1232 /*! 1233 Handles the \a data supplied by a drag and drop operation that ended with 1234 the given \a action over the row in the model specified by the \a row and 1235 \a column and by the \a parent index. 1236 1237 \sa supportedDropActions() 1238 */ 1239 bool FileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, 1240 int row, int column, const QModelIndex &parent) 1241 { 1242 Q_UNUSED(row); 1243 Q_UNUSED(column); 1244 if (!parent.isValid() || isReadOnly()) 1245 return false; 1246 1247 bool success = true; 1248 QString to = filePath(parent) + QDir::separator(); 1249 1250 QList<QUrl> urls = data->urls(); 1251 QList<QUrl>::const_iterator it = urls.constBegin(); 1252 1253 switch (action) { 1254 case Qt::CopyAction: 1255 for (; it != urls.constEnd(); ++it) { 1256 QString path = it->toLocalFile(); 1257 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success; 1258 } 1259 break; 1260 case Qt::LinkAction: 1261 for (; it != urls.constEnd(); ++it) { 1262 QString path = it->toLocalFile(); 1263 success = QFile::link(path, to + QFileInfo(path).fileName()) && success; 1264 } 1265 break; 1266 case Qt::MoveAction: 1267 for (; it != urls.constEnd(); ++it) { 1268 QString path = it->toLocalFile(); 1269 success = QFile::rename(path, to + QFileInfo(path).fileName()) && success; 1270 } 1271 break; 1272 default: 1273 return false; 1274 } 1275 1276 return success; 1277 } 1278 1279 /*! 1280 \reimp 1281 */ 1282 Qt::DropActions FileSystemModel::supportedDropActions() const 1283 { 1284 return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; 1285 } 1286 1287 /*! 1288 Returns the path of the item stored in the model under the 1289 \a index given. 1290 */ 1291 QString FileSystemModel::filePath(const QModelIndex &index) const 1292 { 1293 Q_D(const FileSystemModel); 1294 QString fullPath = d->filePath(index); 1295 if (FileSystemModelPrivate::FileSystemNode *dirNode = d->node(index); 1296 dirNode->isSymLink() 1297 #ifndef QT_NO_FILESYSTEMWATCHER 1298 && d->fileInfoGatherer.resolveSymlinks() 1299 #endif 1300 && d->resolvedSymLinks.contains(fullPath) 1301 && dirNode->isDir()) { 1302 QFileInfo resolvedInfo(fullPath); 1303 resolvedInfo = QFileInfo(resolvedInfo.canonicalFilePath()); 1304 if (resolvedInfo.exists()) 1305 return resolvedInfo.filePath(); 1306 } 1307 return fullPath; 1308 } 1309 1310 QString FileSystemModelPrivate::filePath(const QModelIndex &index) const 1311 { 1312 Q_Q(const FileSystemModel); 1313 Q_UNUSED(q); 1314 if (!index.isValid()) 1315 return QString(); 1316 Q_ASSERT(index.model() == q); 1317 1318 QStringList path; 1319 QModelIndex idx = index; 1320 while (idx.isValid()) { 1321 if (FileSystemModelPrivate::FileSystemNode *dirNode = node(idx)) 1322 path.prepend(dirNode->fileName); 1323 idx = idx.parent(); 1324 } 1325 QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator())); 1326 #if !defined(Q_OS_WIN) 1327 if (fullPath.length() > 2 && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/')) 1328 fullPath = fullPath.mid(1); 1329 #else 1330 if (fullPath.length() == 2 && fullPath.endsWith(QLatin1Char(':'))) 1331 fullPath.append(QLatin1Char('/')); 1332 #endif 1333 return fullPath; 1334 } 1335 1336 /*! 1337 Create a directory with the \a name in the \a parent model index. 1338 */ 1339 QModelIndex FileSystemModel::mkdir(const QModelIndex &parent, const QString &name) 1340 { 1341 Q_D(FileSystemModel); 1342 if (!parent.isValid()) 1343 return parent; 1344 1345 QDir dir(filePath(parent)); 1346 if (!dir.mkdir(name)) 1347 return QModelIndex(); 1348 FileSystemModelPrivate::FileSystemNode *parentNode = d->node(parent); 1349 d->addNode(parentNode, name, QFileInfo()); 1350 Q_ASSERT(parentNode->children.contains(name)); 1351 FileSystemModelPrivate::FileSystemNode *node = parentNode->children[name]; 1352 #ifndef QT_NO_FILESYSTEMWATCHER 1353 node->populate(d->fileInfoGatherer.getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name))); 1354 #endif 1355 d->addVisibleFiles(parentNode, QStringList(name)); 1356 return d->index(node); 1357 } 1358 1359 /*! 1360 Returns the complete OR-ed together combination of QFile::Permission for the \a index. 1361 */ 1362 QFile::Permissions FileSystemModel::permissions(const QModelIndex &index) const 1363 { 1364 Q_D(const FileSystemModel); 1365 return d->node(index)->permissions(); 1366 } 1367 1368 /*! 1369 Free resources used by file system model and reset root path to default. 1370 The model state will be as after creation, but all properties except the 1371 root path remain the same. 1372 */ 1373 void FileSystemModel::clear() 1374 { 1375 Q_D(FileSystemModel); 1376 beginResetModel(); 1377 d->clear(); 1378 endResetModel(); 1379 } 1380 1381 /*! 1382 Sets the directory that is being watched by the model to \a newPath by 1383 installing a \l{QFileSystemWatcher}{file system watcher} on it. Any 1384 changes to files and directories within this directory will be 1385 reflected in the model. 1386 1387 If the path is changed, the rootPathChanged() signal will be emitted. 1388 1389 \note This function does not change the structure of the model or 1390 modify the data available to views. In other words, the "root" of 1391 the model is \e not changed to include only files and directories 1392 within the directory specified by \a newPath in the file system. 1393 */ 1394 QModelIndex FileSystemModel::setRootPath(const QString &newPath) 1395 { 1396 Q_D(FileSystemModel); 1397 #ifdef Q_OS_WIN 1398 #ifdef Q_OS_WIN32 1399 QString longNewPath = qt_GetLongPathName(newPath); 1400 #else 1401 QString longNewPath = QDir::fromNativeSeparators(newPath); 1402 #endif 1403 #else 1404 QString longNewPath = newPath; 1405 #endif 1406 QDir newPathDir(longNewPath); 1407 //we remove .. and . from the given path if exist 1408 if (!newPath.isEmpty()) { 1409 longNewPath = QDir::cleanPath(longNewPath); 1410 newPathDir.setPath(longNewPath); 1411 } 1412 1413 d->setRootPath = true; 1414 1415 //user don't ask for the root path ("") but the conversion failed 1416 if (!newPath.isEmpty() && longNewPath.isEmpty()) 1417 return d->index(rootPath()); 1418 1419 if (d->rootDir.path() == longNewPath) 1420 return d->index(rootPath()); 1421 1422 bool showDrives = longNewPath.isEmpty() || longNewPath == d->myComputer(); 1423 if (!showDrives && !newPathDir.exists()) 1424 return d->index(rootPath()); 1425 1426 //We remove the watcher on the previous path 1427 if (!rootPath().isEmpty() && rootPath() != QLatin1String(".")) { 1428 //This remove the watcher for the old rootPath 1429 #ifndef QT_NO_FILESYSTEMWATCHER 1430 d->fileInfoGatherer.removePath(rootPath()); 1431 #endif 1432 //This line "marks" the node as dirty, so the next fetchMore 1433 //call on the path will ask the gatherer to install a watcher again 1434 //But it doesn't re-fetch everything 1435 d->node(rootPath())->populatedChildren = false; 1436 } 1437 1438 // We have a new valid root path 1439 d->rootDir = newPathDir; 1440 QModelIndex newRootIndex; 1441 if (showDrives) { 1442 // otherwise dir will become '.' 1443 d->rootDir.setPath(QLatin1String("")); 1444 } else { 1445 newRootIndex = d->index(newPathDir.path()); 1446 } 1447 fetchMore(newRootIndex); 1448 emit rootPathChanged(longNewPath); 1449 d->forceSort = true; 1450 d->delayedSort(); 1451 return newRootIndex; 1452 } 1453 1454 /*! 1455 The currently set root path 1456 1457 \sa rootDirectory() 1458 */ 1459 QString FileSystemModel::rootPath() const 1460 { 1461 Q_D(const FileSystemModel); 1462 return d->rootDir.path(); 1463 } 1464 1465 /*! 1466 The currently set directory 1467 1468 \sa rootPath() 1469 */ 1470 QDir FileSystemModel::rootDirectory() const 1471 { 1472 Q_D(const FileSystemModel); 1473 QDir dir(d->rootDir); 1474 dir.setNameFilters(nameFilters()); 1475 dir.setFilter(filter()); 1476 return dir; 1477 } 1478 1479 /*! 1480 Sets the \a provider of file icons for the directory model. 1481 */ 1482 void FileSystemModel::setDecorationProvider(AbstractFileDecorationProvider *provider) 1483 { 1484 Q_D(FileSystemModel); 1485 #ifndef QT_NO_FILESYSTEMWATCHER 1486 d->fileInfoGatherer.setDecorationProvider(provider); 1487 #endif 1488 d->root.updateIcon(provider, QString()); 1489 } 1490 1491 /*! 1492 Returns the file icon provider for this directory model. 1493 */ 1494 AbstractFileDecorationProvider *FileSystemModel::decorationProvider() const 1495 { 1496 #ifndef QT_NO_FILESYSTEMWATCHER 1497 Q_D(const FileSystemModel); 1498 return d->fileInfoGatherer.decorationProvider(); 1499 #else 1500 return 0; 1501 #endif 1502 } 1503 1504 /*! 1505 Sets the directory model's filter to that specified by \a filters. 1506 1507 Note that the filter you set should always include the QDir::AllDirs enum value, 1508 otherwise QFileSystemModel won't be able to read the directory structure. 1509 1510 \sa QDir::Filters 1511 */ 1512 void FileSystemModel::setFilter(QDir::Filters filters) 1513 { 1514 Q_D(FileSystemModel); 1515 if (d->filters == filters) 1516 return; 1517 d->filters = filters; 1518 // CaseSensitivity might have changed 1519 setNameFilters(nameFilters()); 1520 d->forceSort = true; 1521 d->delayedSort(); 1522 } 1523 1524 /*! 1525 Returns the filter specified for the directory model. 1526 1527 If a filter has not been set, the default filter is QDir::AllEntries | 1528 QDir::NoDotAndDotDot | QDir::AllDirs. 1529 1530 \sa QDir::Filters 1531 */ 1532 QDir::Filters FileSystemModel::filter() const 1533 { 1534 Q_D(const FileSystemModel); 1535 return d->filters; 1536 } 1537 1538 /*! 1539 \property QFileSystemModel::resolveSymlinks 1540 \brief Whether the directory model should resolve symbolic links 1541 1542 This is only relevant on Windows. 1543 1544 By default, this property is \c true. 1545 */ 1546 void FileSystemModel::setResolveSymlinks(bool enable) 1547 { 1548 #ifndef QT_NO_FILESYSTEMWATCHER 1549 Q_D(FileSystemModel); 1550 d->fileInfoGatherer.setResolveSymlinks(enable); 1551 #else 1552 Q_UNUSED(enable) 1553 #endif 1554 } 1555 1556 bool FileSystemModel::resolveSymlinks() const 1557 { 1558 #ifndef QT_NO_FILESYSTEMWATCHER 1559 Q_D(const FileSystemModel); 1560 return d->fileInfoGatherer.resolveSymlinks(); 1561 #else 1562 return false; 1563 #endif 1564 } 1565 1566 /*! 1567 \property QFileSystemModel::readOnly 1568 \brief Whether the directory model allows writing to the file system 1569 1570 If this property is set to false, the directory model will allow renaming, copying 1571 and deleting of files and directories. 1572 1573 This property is \c true by default 1574 */ 1575 void FileSystemModel::setReadOnly(bool enable) 1576 { 1577 Q_D(FileSystemModel); 1578 d->readOnly = enable; 1579 } 1580 1581 bool FileSystemModel::isReadOnly() const 1582 { 1583 Q_D(const FileSystemModel); 1584 return d->readOnly; 1585 } 1586 1587 /*! 1588 \property QFileSystemModel::nameFilterDisables 1589 \brief Whether files that don't pass the name filter are hidden or disabled 1590 1591 This property is \c true by default 1592 */ 1593 void FileSystemModel::setNameFilterDisables(bool enable) 1594 { 1595 Q_D(FileSystemModel); 1596 if (d->nameFilterDisables == enable) 1597 return; 1598 d->nameFilterDisables = enable; 1599 d->forceSort = true; 1600 d->delayedSort(); 1601 } 1602 1603 bool FileSystemModel::nameFilterDisables() const 1604 { 1605 Q_D(const FileSystemModel); 1606 return d->nameFilterDisables; 1607 } 1608 1609 /*! 1610 Sets the name \a filters to apply against the existing files. 1611 */ 1612 void FileSystemModel::setNameFilters(const QStringList &filters) 1613 { 1614 // Prep the regexp's ahead of time 1615 #ifndef QT_NO_REGEXP 1616 Q_D(FileSystemModel); 1617 1618 if (!d->bypassFilters.isEmpty()) { 1619 // update the bypass filter to only bypass the stuff that must be kept around 1620 d->bypassFilters.clear(); 1621 // We guarantee that rootPath will stick around 1622 QPersistentModelIndex root(index(rootPath())); 1623 const QModelIndexList persistentList = persistentIndexList(); 1624 for (const auto &persistentIndex : persistentList) { 1625 FileSystemModelPrivate::FileSystemNode *node = d->node(persistentIndex); 1626 while (node) { 1627 if (d->bypassFilters.contains(node)) 1628 break; 1629 if (node->isDir()) 1630 d->bypassFilters[node] = true; 1631 node = node->parent; 1632 } 1633 } 1634 } 1635 1636 d->nameFilters = filters; 1637 d->forceSort = true; 1638 d->delayedSort(); 1639 #endif 1640 } 1641 1642 /*! 1643 Returns a list of filters applied to the names in the model. 1644 */ 1645 QStringList FileSystemModel::nameFilters() const 1646 { 1647 #ifndef QT_NO_REGEXP 1648 Q_D(const FileSystemModel); 1649 return d->nameFilters; 1650 #else 1651 return QStringList(); 1652 #endif 1653 } 1654 1655 /*! 1656 \brief Whether punctuation characters and symbols are ignored when sorting, 1657 \c false by default. 1658 \sa QCollator::setIgnorePunctuation 1659 */ 1660 void FileSystemModel::setSortIgnoringPunctuation(bool ignore) 1661 { 1662 Q_D(FileSystemModel); 1663 d->sortIgnoringPunctuation = ignore; 1664 } 1665 1666 bool FileSystemModel::sortIgnoringPunctuation() const 1667 { 1668 Q_D(const FileSystemModel); 1669 return d->sortIgnoringPunctuation; 1670 } 1671 1672 /*! 1673 \reimp 1674 */ 1675 bool FileSystemModel::event(QEvent *event) 1676 { 1677 #ifndef QT_NO_FILESYSTEMWATCHER 1678 Q_D(FileSystemModel); 1679 if (event->type() == QEvent::LanguageChange) { 1680 d->root.retranslateStrings(d->fileInfoGatherer.decorationProvider(), QString()); 1681 return true; 1682 } 1683 #endif 1684 return QAbstractItemModel::event(event); 1685 } 1686 1687 bool FileSystemModel::rmdir(const QModelIndex &aindex) 1688 { 1689 QString path = filePath(aindex); 1690 const bool success = QDir().rmdir(path); 1691 #ifndef QT_NO_FILESYSTEMWATCHER 1692 if (success) { 1693 auto d = const_cast<FileSystemModelPrivate*>(d_func()); 1694 d->fileInfoGatherer.removePath(path); 1695 } 1696 #endif 1697 return success; 1698 } 1699 1700 /*! 1701 \internal 1702 1703 Performed quick listing and see if any files have been added or removed, 1704 then fetch more information on visible files. 1705 */ 1706 void FileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files) 1707 { 1708 FileSystemModelPrivate::FileSystemNode *parentNode = node(directory, false); 1709 if (parentNode->children.count() == 0) 1710 return; 1711 QStringList toRemove; 1712 QStringList newFiles = files; 1713 std::sort(newFiles.begin(), newFiles.end()); 1714 for (auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) { 1715 if (QStringList::iterator iterator = std::lower_bound( 1716 newFiles.begin(), newFiles.end(), i.value()->fileName); 1717 iterator == newFiles.end() || i.value()->fileName < *iterator) 1718 toRemove.append(i.value()->fileName); 1719 } 1720 for (int i = 0 ; i < toRemove.count() ; ++i ) 1721 removeNode(parentNode, toRemove[i]); 1722 } 1723 1724 /*! 1725 \internal 1726 1727 Adds a new file to the children of parentNode 1728 1729 *WARNING* this will change the count of children 1730 */ 1731 FileSystemModelPrivate::FileSystemNode* FileSystemModelPrivate::addNode(FileSystemNode *parentNode, const QString &fileName, const QFileInfo& info) 1732 { 1733 // In the common case, itemLocation == count() so check there first 1734 auto node = new FileSystemModelPrivate::FileSystemNode(fileName, parentNode); 1735 #ifndef QT_NO_FILESYSTEMWATCHER 1736 node->populate(ExtendedInformation(info)); 1737 #else 1738 Q_UNUSED(info) 1739 #endif 1740 #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) 1741 //The parentNode is "" so we are listing the drives 1742 if (parentNode->fileName.isEmpty()) { 1743 wchar_t name[MAX_PATH + 1]; 1744 //GetVolumeInformation requires to add trailing backslash 1745 const QString nodeName = fileName + QLatin1String("\\"); 1746 // Kid3 GetVolumeInformationW instead of GetVolumeInformation 1747 BOOL success = ::GetVolumeInformationW((wchar_t *)(nodeName.utf16()), 1748 name, MAX_PATH + 1, NULL, 0, NULL, NULL, 0); 1749 if (success && name[0]) 1750 node->volumeName = QString::fromWCharArray(name); 1751 } 1752 #endif 1753 Q_ASSERT(!parentNode->children.contains(fileName)); 1754 parentNode->children.insert(fileName, node); 1755 return node; 1756 } 1757 1758 /*! 1759 \internal 1760 1761 File at parentNode->children(itemLocation) has been removed, remove from the lists 1762 and emit signals if necessary 1763 1764 *WARNING* this will change the count of children and could change visibleChildren 1765 */ 1766 void FileSystemModelPrivate::removeNode(FileSystemModelPrivate::FileSystemNode *parentNode, const QString& name) 1767 { 1768 Q_Q(FileSystemModel); 1769 QModelIndex parent = index(parentNode); 1770 bool indexHidden = isHiddenByFilter(parentNode, parent); 1771 1772 int vLocation = parentNode->visibleLocation(name); 1773 if (vLocation >= 0 && !indexHidden) 1774 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation), 1775 translateVisibleLocation(parentNode, vLocation)); 1776 FileSystemNode * node = parentNode->children.take(name); 1777 #ifndef QT_NO_FILESYSTEMWATCHER 1778 if (node->info && node->info->isDir()) { 1779 // Remove watched path when directory is removed or renamed 1780 fileInfoGatherer.removePath(node->info->fileInfo().filePath()); 1781 } 1782 #endif 1783 delete node; 1784 // cleanup sort files after removing rather then re-sorting which is O(n) 1785 if (vLocation >= 0) 1786 parentNode->visibleChildren.removeAt(vLocation); 1787 if (vLocation >= 0 && !indexHidden) 1788 q->endRemoveRows(); 1789 } 1790 1791 /*! 1792 \internal 1793 1794 File at parentNode->children(itemLocation) was not visible before, but now should be 1795 and emit signals if necessary. 1796 1797 *WARNING* this will change the visible count 1798 */ 1799 void FileSystemModelPrivate::addVisibleFiles(FileSystemNode *parentNode, const QStringList &newFiles) 1800 { 1801 Q_Q(FileSystemModel); 1802 QModelIndex parent = index(parentNode); 1803 bool indexHidden = isHiddenByFilter(parentNode, parent); 1804 if (!indexHidden) { 1805 q->beginInsertRows(parent, parentNode->visibleChildren.count() , parentNode->visibleChildren.count() + newFiles.count() - 1); 1806 } 1807 1808 if (parentNode->dirtyChildrenIndex == -1) 1809 parentNode->dirtyChildrenIndex = parentNode->visibleChildren.count(); 1810 1811 for (const auto &newFile : newFiles) { 1812 parentNode->visibleChildren.append(newFile); 1813 parentNode->children.value(newFile)->isVisible = true; 1814 } 1815 if (!indexHidden) 1816 q->endInsertRows(); 1817 } 1818 1819 /*! 1820 \internal 1821 1822 File was visible before, but now should NOT be 1823 1824 *WARNING* this will change the visible count 1825 */ 1826 void FileSystemModelPrivate::removeVisibleFile(FileSystemNode *parentNode, int vLocation) 1827 { 1828 Q_Q(FileSystemModel); 1829 if (vLocation == -1) 1830 return; 1831 QModelIndex parent = index(parentNode); 1832 bool indexHidden = isHiddenByFilter(parentNode, parent); 1833 if (!indexHidden) 1834 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation), 1835 translateVisibleLocation(parentNode, vLocation)); 1836 parentNode->children.value(parentNode->visibleChildren.at(vLocation))->isVisible = false; 1837 parentNode->visibleChildren.removeAt(vLocation); 1838 if (!indexHidden) 1839 q->endRemoveRows(); 1840 } 1841 1842 /*! 1843 \internal 1844 1845 The thread has received new information about files, 1846 update and emit dataChanged if it has actually changed. 1847 */ 1848 void FileSystemModelPrivate::_q_fileSystemChanged(const QString &path, const QVector<QPair<QString, QFileInfo> > &updates) 1849 { 1850 #ifndef QT_NO_FILESYSTEMWATCHER 1851 Q_Q(FileSystemModel); 1852 QVector<QString> rowsToUpdate; 1853 QStringList newFiles; 1854 FileSystemModelPrivate::FileSystemNode *parentNode = node(path, false); 1855 QModelIndex parentIndex = index(parentNode); 1856 for (const auto &update : updates) { 1857 QString fileName = update.first; 1858 Q_ASSERT(!fileName.isEmpty()); 1859 ExtendedInformation info = fileInfoGatherer.getInfo(update.second); 1860 if (bool previouslyHere = parentNode->children.contains(fileName); 1861 !previouslyHere) { 1862 addNode(parentNode, fileName, info.fileInfo()); 1863 } 1864 FileSystemModelPrivate::FileSystemNode * node = parentNode->children.value(fileName); 1865 bool isCaseSensitive = parentNode->caseSensitive(); 1866 if (isCaseSensitive) { 1867 if (node->fileName != fileName) 1868 continue; 1869 } else { 1870 if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0) 1871 continue; 1872 } 1873 if (isCaseSensitive) { 1874 Q_ASSERT(node->fileName == fileName); 1875 } else { 1876 node->fileName = fileName; 1877 } 1878 1879 if (*node != info ) { 1880 node->populate(info); 1881 bypassFilters.remove(node); 1882 // brand new information. 1883 if (filtersAcceptsNode(node)) { 1884 if (!node->isVisible) { 1885 newFiles.append(fileName); 1886 } else { 1887 rowsToUpdate.append(fileName); 1888 } 1889 } else { 1890 if (node->isVisible) { 1891 int visibleLocation = parentNode->visibleLocation(fileName); 1892 removeVisibleFile(parentNode, visibleLocation); 1893 } else { 1894 // The file is not visible, don't do anything 1895 } 1896 } 1897 } 1898 } 1899 1900 // bundle up all of the changed signals into as few as possible. 1901 std::sort(rowsToUpdate.begin(), rowsToUpdate.end()); 1902 QString min; 1903 QString max; 1904 for (int i = 0; i < rowsToUpdate.count(); ++i) { 1905 QString value = rowsToUpdate.at(i); 1906 //##TODO is there a way to bundle signals with QString as the content of the list? 1907 /*if (min.isEmpty()) { 1908 min = value; 1909 if (i != rowsToUpdate.count() - 1) 1910 continue; 1911 } 1912 if (i != rowsToUpdate.count() - 1) { 1913 if ((value == min + 1 && max.isEmpty()) || value == max + 1) { 1914 max = value; 1915 continue; 1916 } 1917 }*/ 1918 max = value; 1919 min = value; 1920 int visibleMin = parentNode->visibleLocation(min); 1921 int visibleMax = parentNode->visibleLocation(max); 1922 if (visibleMin >= 0 1923 && visibleMin < parentNode->visibleChildren.count() 1924 && parentNode->visibleChildren.at(visibleMin) == min 1925 && visibleMax >= 0) { 1926 QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMin), 0, parentIndex); 1927 QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMax), 3, parentIndex); 1928 emit q->dataChanged(bottom, top); 1929 } 1930 1931 /*min = QString(); 1932 max = QString();*/ 1933 } 1934 1935 if (newFiles.count() > 0) { 1936 addVisibleFiles(parentNode, newFiles); 1937 } 1938 1939 if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) { 1940 forceSort = true; 1941 delayedSort(); 1942 } 1943 #else 1944 Q_UNUSED(path) 1945 Q_UNUSED(updates) 1946 #endif // !QT_NO_FILESYSTEMWATCHER 1947 } 1948 1949 /*! 1950 \internal 1951 */ 1952 void FileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName) 1953 { 1954 resolvedSymLinks[fileName] = resolvedName; 1955 } 1956 1957 void FileSystemModelPrivate::clear() 1958 { 1959 forceSort = true; 1960 setRootPath = false; 1961 rootDir.setPath(QString::fromLatin1(".")); 1962 #ifndef QT_NO_FILESYSTEMWATCHER 1963 fileInfoGatherer.clear(); 1964 #endif 1965 delayedSortTimer.stop(); 1966 bypassFilters.clear(); 1967 resolvedSymLinks.clear(); 1968 root.clear(); 1969 fetchingTimer.stop(); 1970 toFetch.clear(); 1971 } 1972 1973 /*! 1974 \internal 1975 */ 1976 void FileSystemModelPrivate::init() 1977 { 1978 Q_Q(FileSystemModel); 1979 qRegisterMetaType<QVector<QPair<QString,QFileInfo> > >(); 1980 #ifndef QT_NO_FILESYSTEMWATCHER 1981 q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(QString,QStringList)), 1982 q, SLOT(_q_directoryChanged(QString,QStringList))); 1983 q->connect(&fileInfoGatherer, SIGNAL(updates(QString,QVector<QPair<QString,QFileInfo> >)), 1984 q, SLOT(_q_fileSystemChanged(QString,QVector<QPair<QString,QFileInfo> >))); 1985 q->connect(&fileInfoGatherer, SIGNAL(nameResolved(QString,QString)), 1986 q, SLOT(_q_resolvedName(QString,QString))); 1987 q->connect(&fileInfoGatherer, SIGNAL(directoryLoaded(QString)), 1988 q, SIGNAL(directoryLoaded(QString))); 1989 #endif // !QT_NO_FILESYSTEMWATCHER 1990 q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection); 1991 1992 #if QT_VERSION >= 0x050f00 1993 roleNames.insert(FileSystemModel::FileIconRole, QByteArrayLiteral("fileIcon")); // == Qt::decoration 1994 #else 1995 roleNames.insertMulti(FileSystemModel::FileIconRole, QByteArrayLiteral("fileIcon")); // == Qt::decoration 1996 #endif 1997 roleNames.insert(FileSystemModel::FilePathRole, QByteArrayLiteral("filePath")); 1998 roleNames.insert(FileSystemModel::FileNameRole, QByteArrayLiteral("fileName")); 1999 roleNames.insert(FileSystemModel::FilePermissions, QByteArrayLiteral("filePermissions")); 2000 } 2001 2002 #ifndef USE_QT_PRIVATE_HEADERS 2003 QHash<int, QByteArray> FileSystemModel::roleNames() const 2004 { 2005 Q_D(const FileSystemModel); 2006 return d->roleNames; 2007 } 2008 #endif 2009 2010 #if QT_VERSION < 0x050f00 2011 QString FileSystemModel::wildcardToRegularExpression(const QString &pattern) 2012 { 2013 const int wclen = pattern.size(); 2014 QString rx; 2015 rx.reserve(wclen + wclen / 16); 2016 int i = 0; 2017 const QChar *wc = pattern.data(); 2018 2019 #ifdef Q_OS_WIN 2020 const QLatin1Char nativePathSeparator('\\'); 2021 const QLatin1String starEscape("[^/\\\\]*"); 2022 const QLatin1String questionMarkEscape("[^/\\\\]"); 2023 #else 2024 const QLatin1Char nativePathSeparator('/'); 2025 const QLatin1String starEscape("[^/]*"); 2026 const QLatin1String questionMarkEscape("[^/]"); 2027 #endif 2028 2029 while (i < wclen) { 2030 const QChar c = wc[i++]; 2031 switch (c.unicode()) { 2032 case '*': 2033 rx += starEscape; 2034 break; 2035 case '?': 2036 rx += questionMarkEscape; 2037 break; 2038 case '\\': 2039 #ifdef Q_OS_WIN 2040 case '/': 2041 rx += QLatin1String("[/\\\\]"); 2042 break; 2043 #endif 2044 case '$': 2045 case '(': 2046 case ')': 2047 case '+': 2048 case '.': 2049 case '^': 2050 case '{': 2051 case '|': 2052 case '}': 2053 rx += QLatin1Char('\\'); 2054 rx += c; 2055 break; 2056 case '[': 2057 rx += c; 2058 // Support for the [!abc] or [!a-c] syntax 2059 if (i < wclen) { 2060 if (wc[i] == QLatin1Char('!')) { 2061 rx += QLatin1Char('^'); 2062 ++i; 2063 } 2064 2065 if (i < wclen && wc[i] == QLatin1Char(']')) 2066 rx += wc[i++]; 2067 2068 while (i < wclen && wc[i] != QLatin1Char(']')) { 2069 // The '/' appearing in a character class invalidates the 2070 // regular expression parsing. It also concerns '\\' on 2071 // Windows OS types. 2072 if (wc[i] == QLatin1Char('/') || wc[i] == nativePathSeparator) 2073 return rx; 2074 if (wc[i] == QLatin1Char('\\')) 2075 rx += QLatin1Char('\\'); 2076 rx += wc[i++]; 2077 } 2078 } 2079 break; 2080 default: 2081 rx += c; 2082 break; 2083 } 2084 } 2085 2086 return rx; 2087 } 2088 #endif 2089 2090 /*! 2091 \internal 2092 2093 Returns \c false if node doesn't pass the filters otherwise true 2094 2095 QDir::Modified is not supported 2096 QDir::Drives is not supported 2097 */ 2098 bool FileSystemModelPrivate::filtersAcceptsNode(const FileSystemNode *node) const 2099 { 2100 // always accept drives 2101 if (node->parent == &root || bypassFilters.contains(node)) 2102 return true; 2103 2104 // If we don't know anything yet don't accept it 2105 if (!node->hasInformation()) 2106 return false; 2107 2108 const bool filterPermissions = ((filters & QDir::PermissionMask) 2109 && (filters & QDir::PermissionMask) != QDir::PermissionMask); 2110 const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs)); 2111 const bool hideFiles = !(filters & QDir::Files); 2112 const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable)); 2113 const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable)); 2114 const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable)); 2115 const bool hideHidden = !(filters & QDir::Hidden); 2116 const bool hideSystem = !(filters & QDir::System); 2117 const bool hideSymlinks = (filters & QDir::NoSymLinks); 2118 const bool hideDot = (filters & QDir::NoDot); 2119 const bool hideDotDot = (filters & QDir::NoDotDot); 2120 2121 // Note that we match the behavior of entryList and not QFileInfo on this. 2122 bool isDot = node->fileName == QLatin1String("."); 2123 bool isDotDot = node->fileName == QLatin1String(".."); 2124 if ( (hideHidden && !(isDot || isDotDot) && node->isHidden()) 2125 || (hideSystem && node->isSystem()) 2126 || (hideDirs && node->isDir()) 2127 || (hideFiles && node->isFile()) 2128 || (hideSymlinks && node->isSymLink()) 2129 || (hideReadable && node->isReadable()) 2130 || (hideWritable && node->isWritable()) 2131 || (hideExecutable && node->isExecutable()) 2132 || (hideDot && isDot) 2133 || (hideDotDot && isDotDot)) 2134 return false; 2135 2136 return nameFilterDisables || passNameFilters(node); 2137 } 2138 2139 /* 2140 \internal 2141 2142 Returns \c true if node passes the name filters and should be visible. 2143 */ 2144 bool FileSystemModelPrivate::passNameFilters(const FileSystemNode *node) const 2145 { 2146 #ifndef QT_NO_REGEXP 2147 if (nameFilters.isEmpty()) 2148 return true; 2149 2150 // Check the name regularexpression filters 2151 if (!(node->isDir() && (filters & QDir::AllDirs))) { 2152 auto reOptions = filters & QDir::CaseSensitive 2153 ? QRegularExpression::NoPatternOption 2154 : QRegularExpression::CaseInsensitiveOption; 2155 for (const auto &nameFilter : nameFilters) { 2156 #if QT_VERSION >= 0x050f00 2157 auto rx = QRegularExpression( 2158 QRegularExpression::wildcardToRegularExpression(nameFilter), 2159 reOptions); 2160 #else 2161 auto rx = QRegularExpression( 2162 FileSystemModel::wildcardToRegularExpression(nameFilter), 2163 reOptions); 2164 #endif 2165 if (auto match = rx.match(node->fileName); match.hasMatch()) 2166 return true; 2167 } 2168 return false; 2169 } 2170 #endif 2171 return true; 2172 } 2173 /** \endcond */