123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256 | // Copyright (C) 2020 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include"qfilesystemmodel_p.h"#include"qfilesystemmodel.h"#include <qabstractfileiconprovider.h>#include <qlocale.h>#include <qmimedata.h>#include <qurl.h>#include <qdebug.h>#include <QtCore/qcollator.h>#if QT_CONFIG(regularexpression)# include <QtCore/qregularexpression.h>#endif#include <algorithm>#ifdef Q_OS_WIN# include <QtCore/QVarLengthArray># include <qt_windows.h># include <shlobj.h>#endif QT_BEGIN_NAMESPACE using namespaceQt::StringLiterals;/*! \enum QFileSystemModel::Roles \value FileIconRole \value FilePathRole \value FileNameRole \value FilePermissions \value FileInfoRole The QFileInfo object for the index*//*! \class QFileSystemModel \since 4.4 \brief The QFileSystemModel class provides a data model for the local filesystem. \ingroup model-view \inmodule QtGui This class provides access to the local filesystem, providing functions for renaming and removing files and directories, and for creating new directories. In the simplest case, it can be used with a suitable display widget as part of a browser or filter. QFileSystemModel can be accessed using the standard interface provided by QAbstractItemModel, but it also provides some convenience functions that are specific to a directory model. The fileInfo(), isDir(), fileName() and filePath() functions provide information about the underlying files and directories related to items in the model. Directories can be created and removed using mkdir(), rmdir(). \section1 Example Usage A directory model that displays the contents of a default directory is usually constructed with a parent object: \snippet shareddirmodel/main.cpp 2 A tree view can be used to display the contents of the model \snippet shareddirmodel/main.cpp 4 and the contents of a particular directory can be displayed by setting the tree view's root index: \snippet shareddirmodel/main.cpp 7 The view's root index can be used to control how much of a hierarchical model is displayed. QFileSystemModel provides a convenience function that returns a suitable model index for a path to a directory within the model. \section1 Caching and Performance QFileSystemModel uses a separate thread to populate itself, so it will not cause the main thread to hang as the file system is being queried. Calls to rowCount() will return \c 0 until the model populates a directory. The thread in which the QFileSystemModel lives needs to run an event loop to process the incoming data. QFileSystemModel will not start populating itself until setRootPath() is called. This prevents any unnecessary querying of the system's root file system, such as enumerating the drives on Windows, until that point. QFileSystemModel keeps a cache with file information. The cache is automatically kept up to date using the QFileSystemWatcher. \sa {Model Classes}*//*! \fn bool QFileSystemModel::rmdir(const QModelIndex &index) Removes the directory corresponding to the model item \a index in the file system model and \b{deletes the corresponding directory from the file system}, returning true if successful. If the directory cannot be removed, false is returned. \warning This function deletes directories from the file system; it does \b{not} move them to a location where they can be recovered. \sa remove()*//*! \fn QString QFileSystemModel::fileName(const QModelIndex &index) const Returns the file name for the item stored in the model under the given \a index.*//*! \fn QIcon QFileSystemModel::fileIcon(const QModelIndex &index) const Returns the icon for the item stored in the model under the given \a index.*//*! \fn QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const Returns the QFileInfo for the item stored in the model under the given \a index.*/ QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index)const{Q_D(const QFileSystemModel);return d->node(index)->fileInfo();}/*! \fn void QFileSystemModel::rootPathChanged(const QString &newPath); This signal is emitted whenever the root path has been changed to a \a newPath.*//*! \fn void QFileSystemModel::fileRenamed(const QString &path, const QString &oldName, const QString &newName) This signal is emitted whenever a file with the \a oldName is successfully renamed to \a newName. The file is located in the directory \a path.*//*! \since 4.7 \fn void QFileSystemModel::directoryLoaded(const QString &path) This signal is emitted when the gatherer thread has finished to load the \a path.*//*! \fn bool QFileSystemModel::remove(const QModelIndex &index) Removes the model item \a index from the file system model and \b{deletes the corresponding file from the file system}, returning true if successful. If the item cannot be removed, false is returned. \warning This function deletes files from the file system; it does \b{not} move them to a location where they can be recovered. \sa rmdir()*/boolQFileSystemModel::remove(const QModelIndex &aindex){Q_D(QFileSystemModel);const QString path = d->filePath(aindex);const QFileInfo fileInfo(path);#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)// QTBUG-65683: Remove file system watchers prior to deletion to prevent// failure due to locked files on Windows.const QStringList watchedPaths = d->unwatchPathsAt(aindex);#endif// filesystemwatcher && Q_OS_WINconst bool success = (fileInfo.isFile() || fileInfo.isSymLink())?QFile::remove(path) :QDir(path).removeRecursively();#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)if(!success) d->watchPaths(watchedPaths);#endif// filesystemwatcher && Q_OS_WINreturn success;}/*! Constructs a file system model with the given \a parent.*/QFileSystemModel::QFileSystemModel(QObject *parent) :QFileSystemModel(*new QFileSystemModelPrivate, parent){}/*! \internal*/QFileSystemModel::QFileSystemModel(QFileSystemModelPrivate &dd, QObject *parent):QAbstractItemModel(dd, parent){Q_D(QFileSystemModel); d->init();}/*! Destroys this file system model.*/QFileSystemModel::~QFileSystemModel() =default;/*! \reimp*/ QModelIndex QFileSystemModel::index(int row,int column,const QModelIndex &parent)const{Q_D(const QFileSystemModel);if(row <0|| column <0|| row >=rowCount(parent) || column >=columnCount(parent))returnQModelIndex();// get the parent nodeQFileSystemModelPrivate::QFileSystemNode *parentNode = (d->indexValid(parent) ? d->node(parent) :const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&d->root));Q_ASSERT(parentNode);// now get the internal pointer for the indexconst int i = d->translateVisibleLocation(parentNode, row);if(i >= parentNode->visibleChildren.size())returnQModelIndex();const QString &childName = parentNode->visibleChildren.at(i);constQFileSystemModelPrivate::QFileSystemNode *indexNode = parentNode->children.value(childName);Q_ASSERT(indexNode);returncreateIndex(row, column, indexNode);}/*! \reimp*/ QModelIndex QFileSystemModel::sibling(int row,int column,const QModelIndex &idx)const{if(row == idx.row() && column <columnCount(idx.parent())) {// cheap sibling operation: just adjust the column:returncreateIndex(row, column, idx.internalPointer());}else{// for anything else: call the default implementation// (this could probably be optimized, too):returnQAbstractItemModel::sibling(row, column, idx);}}/*! \overload Returns the model item index for the given \a path and \a column.*/ QModelIndex QFileSystemModel::index(const QString &path,int column)const{Q_D(const QFileSystemModel);QFileSystemModelPrivate::QFileSystemNode *node = d->node(path,false);return d->index(node, column);}/*! \internal Return the QFileSystemNode that goes to index. */QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QModelIndex &index)const{if(!index.isValid())return const_cast<QFileSystemNode*>(&root);QFileSystemModelPrivate::QFileSystemNode *indexNode =static_cast<QFileSystemModelPrivate::QFileSystemNode*>(index.internalPointer());Q_ASSERT(indexNode);return indexNode;}#ifdef Q_OS_WIN32static QString qt_GetLongPathName(const QString &strShortPath){if(strShortPath.isEmpty()|| strShortPath =="."_L1 || strShortPath ==".."_L1)return strShortPath;if(strShortPath.length() ==2&& strShortPath.endsWith(u':'))return strShortPath.toUpper();const QString absPath =QDir(strShortPath).absolutePath();if(absPath.startsWith("//"_L1)|| absPath.startsWith("\\\\"_L1))// uncreturnQDir::fromNativeSeparators(absPath);if(absPath.startsWith(u'/'))returnQString();const QString inputString ="\\\\?\\"_L1 +QDir::toNativeSeparators(absPath); QVarLengthArray<TCHAR, MAX_PATH>buffer(MAX_PATH); DWORD result = ::GetLongPathName((wchar_t*)inputString.utf16(), buffer.data(), buffer.size());if(result >DWORD(buffer.size())) { buffer.resize(result); result = ::GetLongPathName((wchar_t*)inputString.utf16(), buffer.data(), buffer.size());}if(result >4) { QString longPath =QString::fromWCharArray(buffer.data() +4);// ignoring prefix longPath[0] = longPath.at(0).toUpper();// capital drive lettersreturnQDir::fromNativeSeparators(longPath);}else{returnQDir::fromNativeSeparators(strShortPath);}}staticinlinevoidchopSpaceAndDot(QString &element){if(element =="."_L1 || element ==".."_L1)return;// On Windows, "filename " and "filename" are equivalent and// "filename . " and "filename" are equivalent// "filename......." and "filename" are equivalent Task #133928// whereas "filename .txt" is still "filename .txt"while(element.endsWith(u'.') || element.endsWith(u' ')) element.chop(1);// If a file is saved as ' Foo.txt', where the leading character(s)// is an ASCII Space (0x20), it will be saved to the file system as 'Foo.txt'.while(element.startsWith(u' ')) element.remove(0,1);}#endif/*! \internal Given a path return the matching QFileSystemNode or &root if invalid*/QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QString &path,bool fetch)const{Q_Q(const QFileSystemModel);Q_UNUSED(q);if(path.isEmpty() || path ==myComputer() || path.startsWith(u':'))return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);// Construct the nodes up to the new root path if they need to be built QString absolutePath;#ifdef Q_OS_WIN32 QString longPath =qt_GetLongPathName(path);#else QString longPath = path;#endifif(longPath == rootDir.path()) absolutePath = rootDir.absolutePath();else absolutePath =QDir(longPath).absolutePath();// ### TODO can we use bool QAbstractFileEngine::caseSensitive() const? QStringList pathElements = absolutePath.split(u'/',Qt::SkipEmptyParts);if((pathElements.isEmpty())#if !defined(Q_OS_WIN)&&QDir::fromNativeSeparators(longPath) !="/"_L1 #endif)return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); QModelIndex index =QModelIndex();// start with "My Computer" QString elementPath; QChar separator = u'/'; QString trailingSeparator;#if defined(Q_OS_WIN)if(absolutePath.startsWith("//"_L1)) {// UNC path QString host ="\\\\"_L1 + pathElements.constFirst();if(absolutePath ==QDir::fromNativeSeparators(host)) absolutePath.append(u'/');if(longPath.endsWith(u'/') && !absolutePath.endsWith(u'/')) absolutePath.append(u'/');if(absolutePath.endsWith(u'/')) trailingSeparator ="\\"_L1;int r =0;auto rootNode =const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);auto it = root.children.constFind(host);if(it != root.children.cend()) { host = it.key();// Normalize case for lookup in visibleLocation()}else{if(pathElements.count() ==1&& !absolutePath.endsWith(u'/'))return rootNode; QFileInfo info(host);if(!info.exists())return rootNode; QFileSystemModelPrivate *p =const_cast<QFileSystemModelPrivate*>(this); p->addNode(rootNode, host,info); p->addVisibleFiles(rootNode,QStringList(host));} r = rootNode->visibleLocation(host); r =translateVisibleLocation(rootNode, r); index = q->index(r,0,QModelIndex()); pathElements.pop_front(); separator = u'\\'; elementPath = host; elementPath.append(separator);}else{if(!pathElements.at(0).contains(u':')) { QString rootPath =QDir(longPath).rootPath(); pathElements.prepend(rootPath);}if(pathElements.at(0).endsWith(u'/')) pathElements[0].chop(1);}#else// add the "/" item, since it is a valid path element on Unixif(absolutePath[0] == u'/') pathElements.prepend("/"_L1);#endifQFileSystemModelPrivate::QFileSystemNode *parent =node(index);for(int i =0; i < pathElements.size(); ++i) { QString element = pathElements.at(i);if(i !=0) elementPath.append(separator); elementPath.append(element);if(i == pathElements.size() -1) elementPath.append(trailingSeparator);#ifdef Q_OS_WIN// If after stripping the characters there is nothing left then we// just return the parent directory as it is assumed that the path// is referring to the parent.chopSpaceAndDot(element);// Only filenames that can't possibly exist will be end up being emptyif(element.isEmpty())return parent;#endifbool alreadyExisted = parent->children.contains(element);// we couldn't find the path element, we create a new node since we// _know_ that the path is validif(alreadyExisted) {if((parent->children.size() ==0)|| (parent->caseSensitive()&& parent->children.value(element)->fileName != element)|| (!parent->caseSensitive()&& parent->children.value(element)->fileName.toLower() != element.toLower())) alreadyExisted =false;}QFileSystemModelPrivate::QFileSystemNode *node;if(!alreadyExisted) {// Someone might call ::index("file://cookie/monster/doesn't/like/veggies"),// a path that doesn't exists, I.E. don't blindly create directories. QFileInfo info(elementPath);if(!info.exists())return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); QFileSystemModelPrivate *p =const_cast<QFileSystemModelPrivate*>(this); node = p->addNode(parent, element,info);#if QT_CONFIG(filesystemwatcher) node->populate(fileInfoGatherer->getInfo(info));#endif}else{ node = parent->children.value(element);}Q_ASSERT(node);if(!node->isVisible) {// It has been filtered outif(alreadyExisted && node->hasInformation() && !fetch)return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); QFileSystemModelPrivate *p =const_cast<QFileSystemModelPrivate*>(this); p->addVisibleFiles(parent,QStringList(element));if(!p->bypassFilters.contains(node)) p->bypassFilters[node] =1; QString dir = q->filePath(this->index(parent));if(!node->hasInformation() && fetch) { Fetching f = {std::move(dir),std::move(element), node }; p->toFetch.append(std::move(f)); p->fetchingTimer.start(0,const_cast<QFileSystemModel*>(q));}} parent = node;}return parent;}/*! \reimp*/voidQFileSystemModel::timerEvent(QTimerEvent *event){Q_D(QFileSystemModel);if(event->timerId() == d->fetchingTimer.timerId()) { d->fetchingTimer.stop();#if QT_CONFIG(filesystemwatcher)for(int i =0; i < d->toFetch.size(); ++i) {constQFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node;if(!node->hasInformation()) { d->fileInfoGatherer->fetchExtendedInformation(d->toFetch.at(i).dir,QStringList(d->toFetch.at(i).file));}else{// qDebug("yah!, you saved a little gerbil soul");}}#endif d->toFetch.clear();}}/*! Returns \c true if the model item \a index represents a directory; otherwise returns \c false.*/boolQFileSystemModel::isDir(const QModelIndex &index)const{// This function is for public usage only because it could create a file infoQ_D(const QFileSystemModel);if(!index.isValid())return true;QFileSystemModelPrivate::QFileSystemNode *n = d->node(index);if(n->hasInformation())return n->isDir();returnfileInfo(index).isDir();}/*! Returns the size in bytes of \a index. If the file does not exist, 0 is returned. */ qint64 QFileSystemModel::size(const QModelIndex &index)const{Q_D(const QFileSystemModel);if(!index.isValid())return0;return d->node(index)->size();}/*! Returns the type of file \a index such as "Directory" or "JPEG file". */ QString QFileSystemModel::type(const QModelIndex &index)const{Q_D(const QFileSystemModel);if(!index.isValid())returnQString();return d->node(index)->type();}/*! Returns the date and time (in local time) when \a index was last modified. This is an overloaded function, equivalent to calling: \code lastModified(index, QTimeZone::LocalTime); \endcode If \a index is invalid, a default constructed QDateTime is returned. */ QDateTime QFileSystemModel::lastModified(const QModelIndex &index)const{returnlastModified(index,QTimeZone::LocalTime);}/*! \since 6.6 Returns the date and time, in the time zone \a tz, when \a index was last modified. Typical arguments for \a tz are \c QTimeZone::UTC or \c QTimeZone::LocalTime. UTC does not require any conversion from the time returned by the native file system API, therefore getting the time in UTC is potentially faster. LocalTime is typically chosen if the time is shown to the user. If \a index is invalid, a default constructed QDateTime is returned. */ QDateTime QFileSystemModel::lastModified(const QModelIndex &index,const QTimeZone &tz)const{Q_D(const QFileSystemModel);if(!index.isValid())returnQDateTime();return d->node(index)->lastModified(tz);}/*! \reimp*/ QModelIndex QFileSystemModel::parent(const QModelIndex &index)const{Q_D(const QFileSystemModel);if(!d->indexValid(index))returnQModelIndex();QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index);Q_ASSERT(indexNode !=nullptr);QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;if(parentNode ==nullptr|| parentNode == &d->root)returnQModelIndex();// get the parent's rowQFileSystemModelPrivate::QFileSystemNode *grandParentNode = parentNode->parent;Q_ASSERT(grandParentNode->children.contains(parentNode->fileName));int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName));if(visualRow == -1)returnQModelIndex();returncreateIndex(visualRow,0, parentNode);}/* \internal return the index for node*/ QModelIndex QFileSystemModelPrivate::index(constQFileSystemModelPrivate::QFileSystemNode *node,int column)const{Q_Q(const QFileSystemModel);QFileSystemModelPrivate::QFileSystemNode *parentNode = (node ? node->parent :nullptr);if(node == &root || !parentNode)returnQModelIndex();// get the parent's rowQ_ASSERT(node);if(!node->isVisible)returnQModelIndex();int visualRow =translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName));return q->createIndex(visualRow, column,const_cast<QFileSystemNode*>(node));}/*! \reimp*/boolQFileSystemModel::hasChildren(const QModelIndex &parent)const{Q_D(const QFileSystemModel);if(parent.column() >0)return false;if(!parent.isValid())// drivesreturn true;constQFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);Q_ASSERT(indexNode);return(indexNode->isDir());}/*! \reimp */boolQFileSystemModel::canFetchMore(const QModelIndex &parent)const{Q_D(const QFileSystemModel);if(!d->setRootPath)return false;constQFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);return(!indexNode->populatedChildren);}/*! \reimp */voidQFileSystemModel::fetchMore(const QModelIndex &parent){Q_D(QFileSystemModel);if(!d->setRootPath)return;QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);if(indexNode->populatedChildren)return; indexNode->populatedChildren =true;#if QT_CONFIG(filesystemwatcher) d->fileInfoGatherer->list(filePath(parent));#endif}/*! \reimp*/intQFileSystemModel::rowCount(const QModelIndex &parent)const{Q_D(const QFileSystemModel);if(parent.column() >0)return0;if(!parent.isValid())return d->root.visibleChildren.size();constQFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);return parentNode->visibleChildren.size();}/*! \reimp*/intQFileSystemModel::columnCount(const QModelIndex &parent)const{return(parent.column() >0) ?0:QFileSystemModelPrivate::NumColumns;}/*! Returns the data stored under the given \a role for the item "My Computer". \sa Qt::ItemDataRole */ QVariant QFileSystemModel::myComputer(int role)const{#if QT_CONFIG(filesystemwatcher)Q_D(const QFileSystemModel);#endifswitch(role) {caseQt::DisplayRole:returnQFileSystemModelPrivate::myComputer();#if QT_CONFIG(filesystemwatcher)caseQt::DecorationRole:if(auto*provider = d->fileInfoGatherer->iconProvider())return provider->icon(QAbstractFileIconProvider::Computer);break;#endif}returnQVariant();}/*! \reimp*/ QVariant QFileSystemModel::data(const QModelIndex &index,int role)const{Q_D(const QFileSystemModel);if(!index.isValid() || index.model() !=this)returnQVariant();switch(role) {caseQt::EditRole:if(index.column() ==QFileSystemModelPrivate::NameColumn)return d->name(index);Q_FALLTHROUGH();caseQt::DisplayRole:switch(index.column()) {caseQFileSystemModelPrivate::NameColumn:return d->displayName(index);caseQFileSystemModelPrivate::SizeColumn:return d->size(index);caseQFileSystemModelPrivate::TypeColumn:return d->type(index);caseQFileSystemModelPrivate::TimeColumn:return d->time(index);default:qWarning("data: invalid display value column %d", index.column());break;}break;case FilePathRole:returnfilePath(index);case FileNameRole:return d->name(index);case FileInfoRole:returnQVariant::fromValue(fileInfo(index));caseQt::DecorationRole:if(index.column() ==QFileSystemModelPrivate::NameColumn) { QIcon icon = d->icon(index);#if QT_CONFIG(filesystemwatcher)if(icon.isNull()) {using P = QAbstractFileIconProvider;if(auto*provider = d->fileInfoGatherer->iconProvider()) icon = provider->icon(d->node(index)->isDir() ?P::Folder:P::File);}#endif// filesystemwatcherreturn icon;}break;caseQt::TextAlignmentRole:if(index.column() ==QFileSystemModelPrivate::SizeColumn)returnQVariant(Qt::AlignTrailing |Qt::AlignVCenter);break;case FilePermissions:int p =permissions(index);return p;}returnQVariant();}/*! \internal*/ QString QFileSystemModelPrivate::size(const QModelIndex &index)const{if(!index.isValid())returnQString();const QFileSystemNode *n =node(index);if(n->isDir()) {#ifdef Q_OS_MACreturn"--"_L1;#elsereturn""_L1;#endif// Windows - ""// OS X - "--"// Konqueror - "4 KB"// Nautilus - "9 items" (the number of children)}returnsize(n->size());} QString QFileSystemModelPrivate::size(qint64 bytes){returnQLocale::system().formattedDataSize(bytes);}/*! \internal*/ QString QFileSystemModelPrivate::time(const QModelIndex &index)const{if(!index.isValid())returnQString();#if QT_CONFIG(datestring)returnQLocale::system().toString(node(index)->lastModified(QTimeZone::LocalTime),QLocale::ShortFormat);#elseQ_UNUSED(index);returnQString();#endif}/* \internal*/ QString QFileSystemModelPrivate::type(const QModelIndex &index)const{if(!index.isValid())returnQString();returnnode(index)->type();}/*! \internal*/ QString QFileSystemModelPrivate::name(const QModelIndex &index)const{if(!index.isValid())returnQString(); QFileSystemNode *dirNode =node(index);if(#if QT_CONFIG(filesystemwatcher) fileInfoGatherer->resolveSymlinks() &&#endif!resolvedSymLinks.isEmpty() && dirNode->isSymLink(/* ignoreNtfsSymLinks = */true)) { QString fullPath =QDir::fromNativeSeparators(filePath(index));return resolvedSymLinks.value(fullPath, dirNode->fileName);}return dirNode->fileName;}/*! \internal*/ QString QFileSystemModelPrivate::displayName(const QModelIndex &index)const{#if defined(Q_OS_WIN) QFileSystemNode *dirNode =node(index);if(!dirNode->volumeName.isEmpty())return dirNode->volumeName;#endifreturnname(index);}/*! \internal*/ QIcon QFileSystemModelPrivate::icon(const QModelIndex &index)const{if(!index.isValid())returnQIcon();returnnode(index)->icon();}/*! \reimp*/boolQFileSystemModel::setData(const QModelIndex &idx,const QVariant &value,int role){Q_D(QFileSystemModel);if(!idx.isValid()|| idx.column() !=0|| role !=Qt::EditRole || (flags(idx) &Qt::ItemIsEditable) ==0) {return false;} QString newName = value.toString();#ifdef Q_OS_WINchopSpaceAndDot(newName);if(newName.isEmpty())return false;#endif QString oldName = idx.data().toString();if(newName == oldName)return true;const QString parentPath =filePath(parent(idx));if(newName.isEmpty() ||QDir::toNativeSeparators(newName).contains(QDir::separator()))return false;#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)// QTBUG-65683: Remove file system watchers prior to renaming to prevent// failure due to locked files on Windows.const QStringList watchedPaths = d->unwatchPathsAt(idx);#endif// filesystemwatcher && Q_OS_WINif(!QDir(parentPath).rename(oldName, newName)) {#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN) d->watchPaths(watchedPaths);#endifreturn false;}else{/* *After re-naming something we don't want the selection to change* - can't remove rows and later insert - can't quickly remove and insert - index pointer can't change because treeview doesn't use persistent index's - if this get any more complicated think of changing it to just use layoutChanged */QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx);QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName); parentNode->visibleChildren.removeAt(visibleLocation);std::unique_ptr<QFileSystemModelPrivate::QFileSystemNode>nodeToRename(parentNode->children.take(oldName)); nodeToRename->fileName = newName; nodeToRename->parent = parentNode;#if QT_CONFIG(filesystemwatcher) nodeToRename->populate(d->fileInfoGatherer->getInfo(QFileInfo(parentPath, newName)));#endif nodeToRename->isVisible =true; parentNode->children[newName] = nodeToRename.release(); parentNode->visibleChildren.insert(visibleLocation, newName); d->delayedSort(); emit fileRenamed(parentPath, oldName, newName);}return true;}/*! \reimp*/ QVariant QFileSystemModel::headerData(int section,Qt::Orientation orientation,int role)const{switch(role) {caseQt::DecorationRole:if(section ==0) {// ### TODO oh man this is ugly and doesn't even work all the way!// it is still 2 pixels off QImage pixmap(16,1,QImage::Format_ARGB32_Premultiplied); pixmap.fill(Qt::transparent);return pixmap;}break;caseQt::TextAlignmentRole:returnQt::AlignLeft;}if(orientation !=Qt::Horizontal || role !=Qt::DisplayRole)returnQAbstractItemModel::headerData(section, orientation, role); QString returnValue;switch(section) {caseQFileSystemModelPrivate::NameColumn: returnValue =tr("Name");break;caseQFileSystemModelPrivate::SizeColumn: returnValue =tr("Size");break;caseQFileSystemModelPrivate::TypeColumn: returnValue =#ifdef Q_OS_MACtr("Kind","Match OS X Finder");#elsetr("Type","All other platforms");#endifbreak;// Windows - Type// OS X - Kind// Konqueror - File Type// Nautilus - TypecaseQFileSystemModelPrivate::TimeColumn: returnValue =tr("Date Modified");break;default:returnQVariant();}return returnValue;}/*! \reimp*/Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index)const{Q_D(const QFileSystemModel);Qt::ItemFlags flags =QAbstractItemModel::flags(index);if(!index.isValid())return flags;QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index);if(d->nameFilterDisables && !d->passNameFilters(indexNode)) { flags &= ~Qt::ItemIsEnabled;// ### TODO you shouldn't be able to set this as the current item, task 119433return flags;} flags |=Qt::ItemIsDragEnabled;if(!indexNode->isDir()) flags |=Qt::ItemNeverHasChildren;if(d->readOnly)return flags;if((index.column() ==0) && indexNode->permissions() &QFile::WriteUser) { flags |=Qt::ItemIsEditable;if(indexNode->isDir()) flags |=Qt::ItemIsDropEnabled;}return flags;}/*! \internal*/voidQFileSystemModelPrivate::performDelayedSort(){Q_Q(QFileSystemModel); q->sort(sortColumn, sortOrder);}/* \internal Helper functor used by sort()*/class QFileSystemModelSorter {public:inlineQFileSystemModelSorter(int column) :sortColumn(column){ naturalCompare.setNumericMode(true); naturalCompare.setCaseSensitivity(Qt::CaseInsensitive);}boolcompareNodes(constQFileSystemModelPrivate::QFileSystemNode *l,constQFileSystemModelPrivate::QFileSystemNode *r)const{switch(sortColumn) {caseQFileSystemModelPrivate::NameColumn: {#ifndef Q_OS_MAC// place directories before filesbool left = l->isDir();bool right = r->isDir();if(left ^ right)return left;#endifreturn naturalCompare.compare(l->fileName, r->fileName) <0;}caseQFileSystemModelPrivate::SizeColumn:{// Directories go firstbool left = l->isDir();bool right = r->isDir();if(left ^ right)return left; qint64 sizeDifference = l->size() - r->size();if(sizeDifference ==0)return naturalCompare.compare(l->fileName, r->fileName) <0;return sizeDifference <0;}caseQFileSystemModelPrivate::TypeColumn:{int compare = naturalCompare.compare(l->type(), r->type());if(compare ==0)return naturalCompare.compare(l->fileName, r->fileName) <0;return compare <0;}caseQFileSystemModelPrivate::TimeColumn:{const QDateTime left = l->lastModified(QTimeZone::UTC);const QDateTime right = r->lastModified(QTimeZone::UTC);if(left == right)return naturalCompare.compare(l->fileName, r->fileName) <0;return left < right;}}Q_ASSERT(false);return false;}booloperator()(constQFileSystemModelPrivate::QFileSystemNode *l,constQFileSystemModelPrivate::QFileSystemNode *r)const{returncompareNodes(l, r);}private: QCollator naturalCompare;int sortColumn;};/* \internal Sort all of the children of parent*/voidQFileSystemModelPrivate::sortChildren(int column,const QModelIndex &parent){Q_Q(QFileSystemModel);QFileSystemModelPrivate::QFileSystemNode *indexNode =node(parent);if(indexNode->children.size() ==0)return; QList<QFileSystemModelPrivate::QFileSystemNode *> values;for(auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) {if(filtersAcceptsNode(iterator.value())) { values.append(iterator.value());}else{ iterator.value()->isVisible =false;}} QFileSystemModelSorter ms(column);std::sort(values.begin(), values.end(), ms);// First update the new visible list indexNode->visibleChildren.clear();//No more dirty item we reset our internal dirty index indexNode->dirtyChildrenIndex = -1; indexNode->visibleChildren.reserve(values.size());for(QFileSystemNode *node :std::as_const(values)) { indexNode->visibleChildren.append(node->fileName); node->isVisible =true;}if(!disableRecursiveSort) {for(int i =0; i < q->rowCount(parent); ++i) {const QModelIndex childIndex = q->index(i,0, parent);QFileSystemModelPrivate::QFileSystemNode *indexNode =node(childIndex);//Only do a recursive sort on visible nodesif(indexNode->isVisible)sortChildren(column, childIndex);}}}/*! \reimp*/voidQFileSystemModel::sort(int column,Qt::SortOrder order){Q_D(QFileSystemModel);if(d->sortOrder == order && d->sortColumn == column && !d->forceSort)return; emit layoutAboutToBeChanged(); QModelIndexList oldList =persistentIndexList(); QList<std::pair<QFileSystemModelPrivate::QFileSystemNode *,int>> oldNodes; oldNodes.reserve(oldList.size());for(const QModelIndex &oldNode : oldList) oldNodes.emplace_back(d->node(oldNode), oldNode.column());if(!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {//we sort only from where we are, don't need to sort all the model d->sortChildren(column,index(rootPath())); d->sortColumn = column; d->forceSort =false;} d->sortOrder = order; QModelIndexList newList; newList.reserve(oldNodes.size());for(constauto&[node, col]:std::as_const(oldNodes)) newList.append(d->index(node, col));changePersistentIndexList(oldList, newList); emit layoutChanged({}, VerticalSortHint);}/*! Returns a list of MIME types that can be used to describe a list of items in the model.*/ QStringList QFileSystemModel::mimeTypes()const{returnQStringList("text/uri-list"_L1);}/*! Returns an object that contains a serialized description of the specified \a indexes. The format used to describe the items corresponding to the indexes is obtained from the mimeTypes() function. If the list of indexes is empty, \nullptr is returned rather than a serialized empty list.*/ QMimeData *QFileSystemModel::mimeData(const QModelIndexList &indexes)const{ QList<QUrl> urls; QList<QModelIndex>::const_iterator it = indexes.begin();for(; it != indexes.end(); ++it)if((*it).column() ==QFileSystemModelPrivate::NameColumn) urls <<QUrl::fromLocalFile(filePath(*it)); QMimeData *data =newQMimeData(); data->setUrls(urls);return data;}/*! Handles the \a data supplied by a drag and drop operation that ended with the given \a action over the row in the model specified by the \a row and \a column and by the \a parent index. Returns true if the operation was successful. \sa supportedDropActions()*/boolQFileSystemModel::dropMimeData(const QMimeData *data,Qt::DropAction action,int row,int column,const QModelIndex &parent){Q_UNUSED(row);Q_UNUSED(column);if(!parent.isValid() ||isReadOnly())return false;bool success =true; QString to =filePath(parent) +QDir::separator(); QList<QUrl> urls = data->urls(); QList<QUrl>::const_iterator it = urls.constBegin();switch(action) {caseQt::CopyAction:for(; it != urls.constEnd(); ++it) { QString path = (*it).toLocalFile(); success =QFile::copy(path, to +QFileInfo(path).fileName()) && success;}break;caseQt::LinkAction:for(; it != urls.constEnd(); ++it) { QString path = (*it).toLocalFile(); success =QFile::link(path, to +QFileInfo(path).fileName()) && success;}break;caseQt::MoveAction:for(; it != urls.constEnd(); ++it) { QString path = (*it).toLocalFile(); success =QFile::rename(path, to +QFileInfo(path).fileName()) && success;}break;default:return false;}return success;}/*! \reimp*/Qt::DropActions QFileSystemModel::supportedDropActions()const{returnQt::CopyAction |Qt::MoveAction |Qt::LinkAction;}/*! \reimp*/ QHash<int, QByteArray>QFileSystemModel::roleNames()const{staticauto ret = [] {auto ret =QAbstractItemModelPrivate::defaultRoleNames(); ret.insert(QFileSystemModel::FileIconRole,"fileIcon"_ba);// == Qt::decoration ret.insert(QFileSystemModel::FilePathRole,"filePath"_ba); ret.insert(QFileSystemModel::FileNameRole,"fileName"_ba); ret.insert(QFileSystemModel::FilePermissions,"filePermissions"_ba); ret.insert(QFileSystemModel::FileInfoRole,"fileInfo"_ba);return ret;}();return ret;}/*! \enum QFileSystemModel::Option \since 5.14 \value DontWatchForChanges Do not add file watchers to the paths. This reduces overhead when using the model for simple tasks like line edit completion. \value DontResolveSymlinks Don't resolve symlinks in the file system model. By default, symlinks are resolved. \value DontUseCustomDirectoryIcons Always use the default directory icon. Some platforms allow the user to set a different icon. Custom icon lookup causes a big performance impact over network or removable drives. This sets the QFileIconProvider::DontUseCustomDirectoryIcons option in the icon provider accordingly. \sa resolveSymlinks*//*! \since 5.14 Sets the given \a option to be enabled if \a on is true; otherwise, clears the given \a option. Options should be set before changing properties. \sa options, testOption()*/voidQFileSystemModel::setOption(Option option,bool on){QFileSystemModel::Options previousOptions =options();setOptions(previousOptions.setFlag(option, on));}/*! \since 5.14 Returns \c true if the given \a option is enabled; otherwise, returns false. \sa options, setOption()*/boolQFileSystemModel::testOption(Option option)const{returnoptions().testFlag(option);}/*! \property QFileSystemModel::options \brief the various options that affect the model \since 5.14 By default, all options are disabled. Options should be set before changing properties. \sa setOption(), testOption()*/voidQFileSystemModel::setOptions(Options options){const Options changed = (options ^QFileSystemModel::options());if(changed.testFlag(DontResolveSymlinks))setResolveSymlinks(!options.testFlag(DontResolveSymlinks));#if QT_CONFIG(filesystemwatcher)Q_D(QFileSystemModel);if(changed.testFlag(DontWatchForChanges)) d->fileInfoGatherer->setWatching(!options.testFlag(DontWatchForChanges));#endifif(changed.testFlag(DontUseCustomDirectoryIcons)) {if(auto provider =iconProvider()) {QAbstractFileIconProvider::Options providerOptions = provider->options(); providerOptions.setFlag(QAbstractFileIconProvider::DontUseCustomDirectoryIcons, options.testFlag(QFileSystemModel::DontUseCustomDirectoryIcons)); provider->setOptions(providerOptions);}else{qWarning("Setting QFileSystemModel::DontUseCustomDirectoryIcons has no effect when no provider is used");}}}QFileSystemModel::Options QFileSystemModel::options()const{QFileSystemModel::Options result; result.setFlag(DontResolveSymlinks, !resolveSymlinks());#if QT_CONFIG(filesystemwatcher)Q_D(const QFileSystemModel); result.setFlag(DontWatchForChanges, !d->fileInfoGatherer->isWatching());#else result.setFlag(DontWatchForChanges);#endifif(auto provider =iconProvider()) { result.setFlag(DontUseCustomDirectoryIcons, provider->options().testFlag(QAbstractFileIconProvider::DontUseCustomDirectoryIcons));}return result;}/*! Returns the path of the item stored in the model under the \a index given.*/ QString QFileSystemModel::filePath(const QModelIndex &index)const{Q_D(const QFileSystemModel); QString fullPath = d->filePath(index);QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);if(dirNode->isSymLink()#if QT_CONFIG(filesystemwatcher)&& d->fileInfoGatherer->resolveSymlinks()#endif&& d->resolvedSymLinks.contains(fullPath)&& dirNode->isDir()) { QFileInfo fullPathInfo(dirNode->fileInfo());if(!dirNode->hasInformation()) fullPathInfo =QFileInfo(fullPath); QString canonicalPath = fullPathInfo.canonicalFilePath();auto*canonicalNode = d->node(fullPathInfo.canonicalFilePath(),false); QFileInfo resolvedInfo = canonicalNode->fileInfo();if(!canonicalNode->hasInformation()) resolvedInfo =QFileInfo(canonicalPath);if(resolvedInfo.exists())return resolvedInfo.filePath();}return fullPath;} QString QFileSystemModelPrivate::filePath(const QModelIndex &index)const{Q_Q(const QFileSystemModel);Q_UNUSED(q);if(!index.isValid())returnQString();Q_ASSERT(index.model() == q); QStringList path; QModelIndex idx = index;while(idx.isValid()) {QFileSystemModelPrivate::QFileSystemNode *dirNode =node(idx);if(dirNode) path.prepend(dirNode->fileName); idx = idx.parent();} QString fullPath =QDir::fromNativeSeparators(path.join(QDir::separator()));#if !defined(Q_OS_WIN)if((fullPath.size() >2) && fullPath[0] == u'/'&& fullPath[1] == u'/') fullPath = fullPath.mid(1);#elseif(fullPath.length() ==2&& fullPath.endsWith(u':')) fullPath.append(u'/');#endifreturn fullPath;}/*! Create a directory with the \a name in the \a parent model index.*/ QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent,const QString &name){Q_D(QFileSystemModel);if(!parent.isValid())return parent; QString fileName = name;#ifdef Q_OS_WINchopSpaceAndDot(fileName);if(fileName.isEmpty())returnQModelIndex();#endif QDir dir(filePath(parent));if(!dir.mkdir(fileName))returnQModelIndex();QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent); d->addNode(parentNode, fileName,QFileInfo());Q_ASSERT(parentNode->children.contains(fileName));QFileSystemModelPrivate::QFileSystemNode *node = parentNode->children[fileName];#if QT_CONFIG(filesystemwatcher) node->populate(d->fileInfoGatherer->getInfo(QFileInfo(dir.absolutePath() +QDir::separator() + fileName)));#endif d->addVisibleFiles(parentNode,QStringList(fileName));return d->index(node);}/*! Returns the complete OR-ed together combination of QFile::Permission for the \a index. */QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index)const{Q_D(const QFileSystemModel);return d->node(index)->permissions();}/*! Sets the directory that is being watched by the model to \a newPath by installing a \l{QFileSystemWatcher}{file system watcher} on it. Any changes to files and directories within this directory will be reflected in the model. If the path is changed, the rootPathChanged() signal will be emitted. \note This function does not change the structure of the model or modify the data available to views. In other words, the "root" of the model is \e not changed to include only files and directories within the directory specified by \a newPath in the file system. \sa {QTreeView::setRootIndex()}, {QtQuick::}{TreeView::rootIndex} */ QModelIndex QFileSystemModel::setRootPath(const QString &newPath){Q_D(QFileSystemModel);#ifdef Q_OS_WIN#ifdef Q_OS_WIN32 QString longNewPath =qt_GetLongPathName(newPath);#else QString longNewPath =QDir::fromNativeSeparators(newPath);#endif#else QString longNewPath = newPath;#endif//we remove .. and . from the given path if existif(!newPath.isEmpty()) longNewPath =QDir::cleanPath(longNewPath); d->setRootPath =true;//user don't ask for the root path ("") but the conversion failedif(!newPath.isEmpty() && longNewPath.isEmpty())return d->index(rootPath());if(d->rootDir.path() == longNewPath)return d->index(rootPath());auto node = d->node(longNewPath); QFileInfo newPathInfo;if(node && node->hasInformation()) newPathInfo = node->fileInfo();else newPathInfo =QFileInfo(longNewPath);bool showDrives = (longNewPath.isEmpty() || longNewPath ==QFileSystemModelPrivate::myComputer());if(!showDrives && !newPathInfo.exists())return d->index(rootPath());//We remove the watcher on the previous pathif(!rootPath().isEmpty() &&rootPath() !="."_L1) {//This remove the watcher for the old rootPath#if QT_CONFIG(filesystemwatcher) d->fileInfoGatherer->removePath(rootPath());#endif//This line "marks" the node as dirty, so the next fetchMore//call on the path will ask the gatherer to install a watcher again//But it doesn't re-fetch everything d->node(rootPath())->populatedChildren =false;}// We have a new valid root path d->rootDir =QDir(longNewPath); QModelIndex newRootIndex;if(showDrives) {// otherwise dir will become '.' d->rootDir.setPath(""_L1);}else{ newRootIndex = d->index(d->rootDir.path());}fetchMore(newRootIndex); emit rootPathChanged(longNewPath); d->forceSort =true; d->delayedSort();return newRootIndex;}/*! The currently set root path \sa rootDirectory()*/ QString QFileSystemModel::rootPath()const{Q_D(const QFileSystemModel);return d->rootDir.path();}/*! The currently set directory \sa rootPath()*/ QDir QFileSystemModel::rootDirectory()const{Q_D(const QFileSystemModel); QDir dir(d->rootDir); dir.setNameFilters(nameFilters()); dir.setFilter(filter());return dir;}/*! Sets the \a provider of file icons for the directory model.*/voidQFileSystemModel::setIconProvider(QAbstractFileIconProvider *provider){Q_D(QFileSystemModel);#if QT_CONFIG(filesystemwatcher) d->fileInfoGatherer->setIconProvider(provider);#endif d->root.updateIcon(provider,QString());}/*! Returns the file icon provider for this directory model.*/ QAbstractFileIconProvider *QFileSystemModel::iconProvider()const{#if QT_CONFIG(filesystemwatcher)Q_D(const QFileSystemModel);return d->fileInfoGatherer->iconProvider();#elsereturnnullptr;#endif}/*! Sets the directory model's filter to that specified by \a filters. Note that the filter you set should always include the QDir::AllDirs enum value, otherwise QFileSystemModel won't be able to read the directory structure. \sa QDir::Filters*/voidQFileSystemModel::setFilter(QDir::Filters filters){Q_D(QFileSystemModel);if(d->filters == filters)return;const bool changingCaseSensitivity = filters.testFlag(QDir::CaseSensitive) != d->filters.testFlag(QDir::CaseSensitive); d->filters = filters;if(changingCaseSensitivity) d->rebuildNameFilterRegexps(); d->forceSort =true; d->delayedSort();}/*! Returns the filter specified for the directory model. If a filter has not been set, the default filter is QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs. \sa QDir::Filters*/QDir::Filters QFileSystemModel::filter()const{Q_D(const QFileSystemModel);return d->filters;}/*! \property QFileSystemModel::resolveSymlinks \brief Whether the directory model should resolve symbolic links This is only relevant on Windows. By default, this property is \c true. \sa QFileSystemModel::Options*/voidQFileSystemModel::setResolveSymlinks(bool enable){#if QT_CONFIG(filesystemwatcher)Q_D(QFileSystemModel); d->fileInfoGatherer->setResolveSymlinks(enable);#elseQ_UNUSED(enable);#endif}boolQFileSystemModel::resolveSymlinks()const{#if QT_CONFIG(filesystemwatcher)Q_D(const QFileSystemModel);return d->fileInfoGatherer->resolveSymlinks();#elsereturn false;#endif}/*! \property QFileSystemModel::readOnly \brief Whether the directory model allows writing to the file system If this property is set to false, the directory model will allow renaming, copying and deleting of files and directories. This property is \c true by default*/voidQFileSystemModel::setReadOnly(bool enable){Q_D(QFileSystemModel); d->readOnly = enable;}boolQFileSystemModel::isReadOnly()const{Q_D(const QFileSystemModel);return d->readOnly;}/*! \property QFileSystemModel::nameFilterDisables \brief Whether files that don't pass the name filter are hidden or disabled This property is \c true by default*/voidQFileSystemModel::setNameFilterDisables(bool enable){Q_D(QFileSystemModel);if(d->nameFilterDisables == enable)return; d->nameFilterDisables = enable; d->forceSort =true; d->delayedSort();}boolQFileSystemModel::nameFilterDisables()const{Q_D(const QFileSystemModel);return d->nameFilterDisables;}/*! Sets the name \a filters to apply against the existing files.*/voidQFileSystemModel::setNameFilters(const QStringList &filters){#if QT_CONFIG(regularexpression)Q_D(QFileSystemModel);if(!d->bypassFilters.isEmpty()) {// update the bypass filter to only bypass the stuff that must be kept around d->bypassFilters.clear();// We guarantee that rootPath will stick around QPersistentModelIndex root(index(rootPath()));const QModelIndexList persistentList =persistentIndexList();for(constauto&persistentIndex : persistentList) {QFileSystemModelPrivate::QFileSystemNode *node = d->node(persistentIndex);while(node) {if(d->bypassFilters.contains(node))break;if(node->isDir()) d->bypassFilters[node] =true; node = node->parent;}}} d->nameFilters = filters; d->rebuildNameFilterRegexps(); d->forceSort =true; d->delayedSort();#elseQ_UNUSED(filters);#endif}/*! Returns a list of filters applied to the names in the model.*/ QStringList QFileSystemModel::nameFilters()const{#if QT_CONFIG(regularexpression)Q_D(const QFileSystemModel);return d->nameFilters;#elsereturnQStringList();#endif}/*! \reimp*/boolQFileSystemModel::event(QEvent *event){#if QT_CONFIG(filesystemwatcher)Q_D(QFileSystemModel);if(event->type() ==QEvent::LanguageChange) { d->root.retranslateStrings(d->fileInfoGatherer->iconProvider(),QString());return true;}#endifreturnQAbstractItemModel::event(event);}boolQFileSystemModel::rmdir(const QModelIndex &aindex){ QString path =filePath(aindex);const bool success =QDir().rmdir(path);#if QT_CONFIG(filesystemwatcher)if(success) { QFileSystemModelPrivate * d =const_cast<QFileSystemModelPrivate*>(d_func()); d->fileInfoGatherer->removePath(path);}#endifreturn success;}/*! \internal Performed quick listing and see if any files have been added or removed, then fetch more information on visible files. */voidQFileSystemModelPrivate::directoryChanged(const QString &directory,const QStringList &files){QFileSystemModelPrivate::QFileSystemNode *parentNode =node(directory,false);if(parentNode->children.size() ==0)return; QStringList toRemove; QStringList newFiles = files;std::sort(newFiles.begin(), newFiles.end());for(auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) {QStringList::iterator iterator =std::lower_bound(newFiles.begin(), newFiles.end(), i.value()->fileName);if((iterator == newFiles.end()) || (i.value()->fileName < *iterator)) toRemove.append(i.value()->fileName);}for(int i =0; i < toRemove.size() ; ++i )removeNode(parentNode, toRemove[i]);}#if defined(Q_OS_WIN)static QString volumeName(const QString &path){ IShellItem *item =nullptr;const QString native =QDir::toNativeSeparators(path); HRESULT hr =SHCreateItemFromParsingName(reinterpret_cast<const wchar_t*>(native.utf16()),nullptr, IID_IShellItem,reinterpret_cast<void**>(&item));if(FAILED(hr))returnQString(); LPWSTR name =nullptr; hr = item->GetDisplayName(SIGDN_NORMALDISPLAY, &name);if(FAILED(hr))returnQString(); QString result =QString::fromWCharArray(name);CoTaskMemFree(name); item->Release();return result;}#endif// Q_OS_WIN/*! \internal Adds a new file to the children of parentNode *WARNING* this will change the count of children*/QFileSystemModelPrivate::QFileSystemNode*QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode,const QString &fileName,const QFileInfo& info){// In the common case, itemLocation == count() so check there firstQFileSystemModelPrivate::QFileSystemNode *node =newQFileSystemModelPrivate::QFileSystemNode(fileName, parentNode);#if QT_CONFIG(filesystemwatcher) node->populate(info);#elseQ_UNUSED(info);#endif#if defined(Q_OS_WIN)//The parentNode is "" so we are listing the drivesif(parentNode->fileName.isEmpty()) node->volumeName =volumeName(fileName);#endifQ_ASSERT(!parentNode->children.contains(fileName)); parentNode->children.insert(fileName, node);return node;}/*! \internal File at parentNode->children(itemLocation) has been removed, remove from the lists and emit signals if necessary *WARNING* this will change the count of children and could change visibleChildren */voidQFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode,const QString& name){Q_Q(QFileSystemModel); QModelIndex parent =index(parentNode);bool indexHidden =isHiddenByFilter(parentNode, parent);int vLocation = parentNode->visibleLocation(name);if(vLocation >=0&& !indexHidden) q->beginRemoveRows(parent,translateVisibleLocation(parentNode, vLocation),translateVisibleLocation(parentNode, vLocation)); QFileSystemNode * node = parentNode->children.take(name);delete node;// cleanup sort files after removing rather then re-sorting which is O(n)if(vLocation >=0) parentNode->visibleChildren.removeAt(vLocation);if(vLocation >=0&& !indexHidden) q->endRemoveRows();}/*! \internal File at parentNode->children(itemLocation) was not visible before, but now should be and emit signals if necessary. *WARNING* this will change the visible count */voidQFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode,const QStringList &newFiles){Q_Q(QFileSystemModel); QModelIndex parent =index(parentNode);bool indexHidden =isHiddenByFilter(parentNode, parent);if(!indexHidden) { q->beginInsertRows(parent, parentNode->visibleChildren.size() , parentNode->visibleChildren.size() + newFiles.size() -1);}if(parentNode->dirtyChildrenIndex == -1) parentNode->dirtyChildrenIndex = parentNode->visibleChildren.size();for(constauto&newFile : newFiles) { parentNode->visibleChildren.append(newFile); parentNode->children.value(newFile)->isVisible =true;}if(!indexHidden) q->endInsertRows();}/*! \internal File was visible before, but now should NOT be *WARNING* this will change the visible count */voidQFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode,int vLocation){Q_Q(QFileSystemModel);if(vLocation == -1)return; QModelIndex parent =index(parentNode);bool indexHidden =isHiddenByFilter(parentNode, parent);if(!indexHidden) q->beginRemoveRows(parent,translateVisibleLocation(parentNode, vLocation),translateVisibleLocation(parentNode, vLocation)); parentNode->children.value(parentNode->visibleChildren.at(vLocation))->isVisible =false; parentNode->visibleChildren.removeAt(vLocation);if(!indexHidden) q->endRemoveRows();}/*! \internal The thread has received new information about files, update and emit dataChanged if it has actually changed. */voidQFileSystemModelPrivate::fileSystemChanged(const QString &path,const QList<std::pair<QString, QFileInfo>> &updates){#if QT_CONFIG(filesystemwatcher)Q_Q(QFileSystemModel); QList<QString> rowsToUpdate; QStringList newFiles;QFileSystemModelPrivate::QFileSystemNode *parentNode =node(path,false); QModelIndex parentIndex =index(parentNode);for(constauto&update : updates) { QString fileName = update.first;Q_ASSERT(!fileName.isEmpty()); QExtendedInformation info = fileInfoGatherer->getInfo(update.second);bool previouslyHere = parentNode->children.contains(fileName);if(!previouslyHere) {#ifdef Q_OS_WINchopSpaceAndDot(fileName);if(fileName.isEmpty())continue;#endifaddNode(parentNode, fileName, info.fileInfo());}QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName);bool isCaseSensitive = parentNode->caseSensitive();if(isCaseSensitive) {if(node->fileName != fileName)continue;}else{if(QString::compare(node->fileName,fileName,Qt::CaseInsensitive) !=0)continue;}if(isCaseSensitive) {Q_ASSERT(node->fileName == fileName);}else{ node->fileName = fileName;}if(*node != info ) { node->populate(info); bypassFilters.remove(node);// brand new information.if(filtersAcceptsNode(node)) {if(!node->isVisible) { newFiles.append(fileName);}else{ rowsToUpdate.append(fileName);}}else{if(node->isVisible) {int visibleLocation = parentNode->visibleLocation(fileName);removeVisibleFile(parentNode, visibleLocation);}else{// The file is not visible, don't do anything}}}}// bundle up all of the changed signals into as few as possible.std::sort(rowsToUpdate.begin(), rowsToUpdate.end()); QString min; QString max;for(const QString &value :std::as_const(rowsToUpdate)) {//##TODO is there a way to bundle signals with QString as the content of the list?/*if (min.isEmpty()) { min = value; if (i != rowsToUpdate.count() - 1) continue; } if (i != rowsToUpdate.count() - 1) { if ((value == min + 1 && max.isEmpty()) || value == max + 1) { max = value; continue; } }*/ max = value; min = value;int visibleMin = parentNode->visibleLocation(min);int visibleMax = parentNode->visibleLocation(max);if(visibleMin >=0&& visibleMin < parentNode->visibleChildren.size()&& parentNode->visibleChildren.at(visibleMin) == min && visibleMax >=0) {// don't use NumColumns here, a subclass might override columnCountconst int lastColumn = q->columnCount(parentIndex) -1;const QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMin),QFileSystemModelPrivate::NameColumn, parentIndex);const QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMax), lastColumn, parentIndex);// We document that emitting dataChanged with indexes that don't have the// same parent is undefined behavior.Q_ASSERT(bottom.parent() == top.parent()); emit q->dataChanged(top, bottom);}/*min = QString(); max = QString();*/}if(newFiles.size() >0) {addVisibleFiles(parentNode, newFiles);}if(newFiles.size() >0|| (sortColumn !=0&& rowsToUpdate.size() >0)) { forceSort =true;delayedSort();}#elseQ_UNUSED(path);Q_UNUSED(updates);#endif// filesystemwatcher}/*! \internal*/voidQFileSystemModelPrivate::resolvedName(const QString &fileName,const QString &resolvedName){ resolvedSymLinks[fileName] = resolvedName;}#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)// Remove file system watchers at/below the index and return a list of previously// watched files. This should be called prior to operations like rename/remove// which might fail due to watchers on platforms like Windows. The watchers// should be restored on failure. QStringList QFileSystemModelPrivate::unwatchPathsAt(const QModelIndex &index){constQFileSystemModelPrivate::QFileSystemNode *indexNode =node(index);if(indexNode ==nullptr)returnQStringList();constQt::CaseSensitivity caseSensitivity = indexNode->caseSensitive()?Qt::CaseSensitive :Qt::CaseInsensitive;const QString path = indexNode->fileInfo().absoluteFilePath(); QStringList result;constauto filter = [path, caseSensitivity] (const QString &watchedPath){const int pathSize = path.size();if(pathSize == watchedPath.size()) {return path.compare(watchedPath, caseSensitivity) ==0;}else if(watchedPath.size() > pathSize) {return watchedPath.at(pathSize) == u'/'&& watchedPath.startsWith(path, caseSensitivity);}return false;};const QStringList &watchedFiles = fileInfoGatherer->watchedFiles();std::copy_if(watchedFiles.cbegin(), watchedFiles.cend(),std::back_inserter(result), filter);const QStringList &watchedDirectories = fileInfoGatherer->watchedDirectories();std::copy_if(watchedDirectories.cbegin(), watchedDirectories.cend(),std::back_inserter(result), filter); fileInfoGatherer->unwatchPaths(result);return result;}#endif// filesystemwatcher && Q_OS_WINQFileSystemModelPrivate::QFileSystemModelPrivate()#if QT_CONFIG(filesystemwatcher):fileInfoGatherer(new QFileInfoGatherer)#endif// filesystemwatcher{}QFileSystemModelPrivate::~QFileSystemModelPrivate(){#if QT_CONFIG(filesystemwatcher) fileInfoGatherer->requestAbort();if(!fileInfoGatherer->wait(1000)) {// If the thread hangs, perhaps because the network was disconnected// while the gatherer was stat'ing a remote file, then don't block// shutting down the model (which might block a file dialog and the// main thread). Schedule the gatherer for later deletion; it's// destructor will wait for the thread to finish.auto*rawGatherer = fileInfoGatherer.release(); rawGatherer->deleteLater();}#endif// filesystemwatcher}/*! \internal*/voidQFileSystemModelPrivate::init(){ delayedSortTimer.setSingleShot(true); qRegisterMetaType<QList<std::pair<QString, QFileInfo>>>();#if QT_CONFIG(filesystemwatcher)QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::newListOfFiles,this, &QFileSystemModelPrivate::directoryChanged);QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::updates,this, &QFileSystemModelPrivate::fileSystemChanged);QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::nameResolved,this, &QFileSystemModelPrivate::resolvedName);Q_Q(QFileSystemModel); q->connect(fileInfoGatherer.get(), &QFileInfoGatherer::directoryLoaded, q, &QFileSystemModel::directoryLoaded);#endif// filesystemwatcherQObjectPrivate::connect(&delayedSortTimer, &QTimer::timeout,this, &QFileSystemModelPrivate::performDelayedSort,Qt::QueuedConnection);}/*! \internal Returns \c false if node doesn't pass the filters otherwise true QDir::Modified is not supported QDir::Drives is not supported*/boolQFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node)const{// When the model is set to only show files, then a node representing a dir// should be hidden regardless of bypassFilters.// QTBUG-74471const bool hideDirs = (filters & (QDir::Dirs |QDir::AllDirs)) ==0;const bool shouldHideDirNode = hideDirs && node->isDir();// always accept drivesif(node->parent == &root || (!shouldHideDirNode && bypassFilters.contains(node)))return true;// If we don't know anything yet don't accept itif(!node->hasInformation())return false;const bool filterPermissions = ((filters &QDir::PermissionMask)&& (filters &QDir::PermissionMask) !=QDir::PermissionMask);const bool hideFiles = !(filters &QDir::Files);const bool hideReadable = !(!filterPermissions || (filters &QDir::Readable));const bool hideWritable = !(!filterPermissions || (filters &QDir::Writable));const bool hideExecutable = !(!filterPermissions || (filters &QDir::Executable));const bool hideHidden = !(filters &QDir::Hidden);const bool hideSystem = !(filters &QDir::System);const bool hideSymlinks = (filters &QDir::NoSymLinks);const bool hideDot = (filters &QDir::NoDot);const bool hideDotDot = (filters &QDir::NoDotDot);// Note that we match the behavior of entryList and not QFileInfo on this.bool isDot = (node->fileName =="."_L1);bool isDotDot = (node->fileName ==".."_L1);if( (hideHidden && !(isDot || isDotDot) && node->isHidden())|| (hideSystem && node->isSystem())|| (hideDirs && node->isDir())|| (hideFiles && node->isFile())|| (hideSymlinks && node->isSymLink())|| (hideReadable && node->isReadable())|| (hideWritable && node->isWritable())|| (hideExecutable && node->isExecutable())|| (hideDot && isDot)|| (hideDotDot && isDotDot))return false;return nameFilterDisables ||passNameFilters(node);}/* \internal Returns \c true if node passes the name filters and should be visible. */boolQFileSystemModelPrivate::passNameFilters(const QFileSystemNode *node)const{#if QT_CONFIG(regularexpression)if(nameFilters.isEmpty())return true;// Check the name regularexpression filtersif(!(node->isDir() && (filters &QDir::AllDirs))) {constauto matchesNodeFileName = [node](const QRegularExpression &re){return node->fileName.contains(re);};returnstd::any_of(nameFiltersRegexps.begin(), nameFiltersRegexps.end(), matchesNodeFileName);}#elseQ_UNUSED(node);#endifreturn true;}#if QT_CONFIG(regularexpression)voidQFileSystemModelPrivate::rebuildNameFilterRegexps(){ nameFiltersRegexps.clear(); nameFiltersRegexps.reserve(nameFilters.size());constauto cs = (filters &QDir::CaseSensitive) ?Qt::CaseSensitive :Qt::CaseInsensitive;constauto convertWildcardToRegexp = [cs](const QString &nameFilter){returnQRegularExpression::fromWildcard(nameFilter, cs);};std::transform(nameFilters.constBegin(), nameFilters.constEnd(),std::back_inserter(nameFiltersRegexps), convertWildcardToRegexp);}#endif QT_END_NAMESPACE #include"moc_qfilesystemmodel.cpp"
|