1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #ifndef QFILESYSTEMMODEL_P_H
41 #define QFILESYSTEMMODEL_P_H
42 
43 //
44 //  W A R N I N G
45 //  -------------
46 //
47 // This file is not part of the Qt API.  It exists purely as an
48 // implementation detail.  This header file may change from version to
49 // version without notice, or even be removed.
50 //
51 // We mean it.
52 //
53 
54 #include <QtWidgets/private/qtwidgetsglobal_p.h>
55 #include "qfilesystemmodel.h"
56 
57 #include <private/qabstractitemmodel_p.h>
58 #include <qabstractitemmodel.h>
59 #include "qfileinfogatherer_p.h"
60 #include <qpair.h>
61 #include <qdir.h>
62 #include <qicon.h>
63 #include <qfileinfo.h>
64 #include <qtimer.h>
65 #include <qhash.h>
66 
67 QT_REQUIRE_CONFIG(filesystemmodel);
68 
69 QT_BEGIN_NAMESPACE
70 
71 class ExtendedInformation;
72 class QFileSystemModelPrivate;
73 class QFileIconProvider;
74 
75 #if defined(Q_OS_WIN)
76 class QFileSystemModelNodePathKey : public QString
77 {
78 public:
QFileSystemModelNodePathKey()79     QFileSystemModelNodePathKey() {}
QFileSystemModelNodePathKey(const QString & other)80     QFileSystemModelNodePathKey(const QString &other) : QString(other) {}
QFileSystemModelNodePathKey(const QFileSystemModelNodePathKey & other)81     QFileSystemModelNodePathKey(const QFileSystemModelNodePathKey &other) : QString(other) {}
82     bool operator==(const QFileSystemModelNodePathKey &other) const { return !compare(other, Qt::CaseInsensitive); }
83 };
84 
85 Q_DECLARE_TYPEINFO(QFileSystemModelNodePathKey, Q_MOVABLE_TYPE);
86 
qHash(const QFileSystemModelNodePathKey & key)87 inline uint qHash(const QFileSystemModelNodePathKey &key) { return qHash(key.toCaseFolded()); }
88 #else // Q_OS_WIN
89 typedef QString QFileSystemModelNodePathKey;
90 #endif
91 
92 class Q_AUTOTEST_EXPORT QFileSystemModelPrivate : public QAbstractItemModelPrivate
93 {
94     Q_DECLARE_PUBLIC(QFileSystemModel)
95 
96 public:
97     enum { NumColumns = 4 };
98 
99     class QFileSystemNode
100     {
101     public:
Q_DISABLE_COPY_MOVE(QFileSystemNode)102         Q_DISABLE_COPY_MOVE(QFileSystemNode)
103 
104         explicit QFileSystemNode(const QString &filename = QString(), QFileSystemNode *p = nullptr)
105             : fileName(filename), parent(p) {}
~QFileSystemNode()106         ~QFileSystemNode() {
107             qDeleteAll(children);
108             delete info;
109         }
110 
111         QString fileName;
112 #if defined(Q_OS_WIN)
113         QString volumeName;
114 #endif
115 
size()116         inline qint64 size() const { if (info && !info->isDir()) return info->size(); return 0; }
type()117         inline QString type() const { if (info) return info->displayType; return QLatin1String(""); }
lastModified()118         inline QDateTime lastModified() const { if (info) return info->lastModified(); return QDateTime(); }
permissions()119         inline QFile::Permissions permissions() const { if (info) return info->permissions(); return { }; }
isReadable()120         inline bool isReadable() const { return ((permissions() & QFile::ReadUser) != 0); }
isWritable()121         inline bool isWritable() const { return ((permissions() & QFile::WriteUser) != 0); }
isExecutable()122         inline bool isExecutable() const { return ((permissions() & QFile::ExeUser) != 0); }
isDir()123         inline bool isDir() const {
124             if (info)
125                 return info->isDir();
126             if (children.count() > 0)
127                 return true;
128             return false;
129         }
fileInfo()130         inline QFileInfo fileInfo() const { if (info) return info->fileInfo(); return QFileInfo(); }
isFile()131         inline bool isFile() const { if (info) return info->isFile(); return true; }
isSystem()132         inline bool isSystem() const { if (info) return info->isSystem(); return true; }
isHidden()133         inline bool isHidden() const { if (info) return info->isHidden(); return false; }
134         inline bool isSymLink(bool ignoreNtfsSymLinks = false) const { return info && info->isSymLink(ignoreNtfsSymLinks); }
caseSensitive()135         inline bool caseSensitive() const { if (info) return info->isCaseSensitive(); return false; }
icon()136         inline QIcon icon() const { if (info) return info->icon; return QIcon(); }
137 
138         inline bool operator <(const QFileSystemNode &node) const {
139             if (caseSensitive() || node.caseSensitive())
140                 return fileName < node.fileName;
141             return QString::compare(fileName, node.fileName, Qt::CaseInsensitive) < 0;
142         }
143         inline bool operator >(const QString &name) const {
144             if (caseSensitive())
145                 return fileName > name;
146             return QString::compare(fileName, name, Qt::CaseInsensitive) > 0;
147         }
148         inline bool operator <(const QString &name) const {
149             if (caseSensitive())
150                 return fileName < name;
151             return QString::compare(fileName, name, Qt::CaseInsensitive) < 0;
152         }
153         inline bool operator !=(const QExtendedInformation &fileInfo) const {
154             return !operator==(fileInfo);
155         }
156         bool operator ==(const QString &name) const {
157             if (caseSensitive())
158                 return fileName == name;
159             return QString::compare(fileName, name, Qt::CaseInsensitive) == 0;
160         }
161         bool operator ==(const QExtendedInformation &fileInfo) const {
162             return info && (*info == fileInfo);
163         }
164 
hasInformation()165         inline bool hasInformation() const { return info != nullptr; }
166 
populate(const QExtendedInformation & fileInfo)167         void populate(const QExtendedInformation &fileInfo) {
168             if (!info)
169                 info = new QExtendedInformation(fileInfo.fileInfo());
170             (*info) = fileInfo;
171         }
172 
173         // children shouldn't normally be accessed directly, use node()
visibleLocation(const QString & childName)174         inline int visibleLocation(const QString &childName) {
175             return visibleChildren.indexOf(childName);
176         }
updateIcon(QFileIconProvider * iconProvider,const QString & path)177         void updateIcon(QFileIconProvider *iconProvider, const QString &path) {
178             if (info)
179                 info->icon = iconProvider->icon(QFileInfo(path));
180             for (QFileSystemNode *child : qAsConst(children)) {
181                 //On windows the root (My computer) has no path so we don't want to add a / for nothing (e.g. /C:/)
182                 if (!path.isEmpty()) {
183                     if (path.endsWith(QLatin1Char('/')))
184                         child->updateIcon(iconProvider, path + child->fileName);
185                     else
186                         child->updateIcon(iconProvider, path + QLatin1Char('/') + child->fileName);
187                 } else
188                     child->updateIcon(iconProvider, child->fileName);
189             }
190         }
191 
retranslateStrings(QFileIconProvider * iconProvider,const QString & path)192         void retranslateStrings(QFileIconProvider *iconProvider, const QString &path) {
193             if (info)
194                 info->displayType = iconProvider->type(QFileInfo(path));
195             for (QFileSystemNode *child : qAsConst(children)) {
196                 //On windows the root (My computer) has no path so we don't want to add a / for nothing (e.g. /C:/)
197                 if (!path.isEmpty()) {
198                     if (path.endsWith(QLatin1Char('/')))
199                         child->retranslateStrings(iconProvider, path + child->fileName);
200                     else
201                         child->retranslateStrings(iconProvider, path + QLatin1Char('/') + child->fileName);
202                 } else
203                     child->retranslateStrings(iconProvider, child->fileName);
204             }
205         }
206 
207         QHash<QFileSystemModelNodePathKey, QFileSystemNode *> children;
208         QList<QString> visibleChildren;
209         QExtendedInformation *info = nullptr;
210         QFileSystemNode *parent;
211         int dirtyChildrenIndex = -1;
212         bool populatedChildren = false;
213         bool isVisible = false;
214     };
215 
216     QFileSystemModelPrivate() = default;
217     void init();
218     /*
219       \internal
220 
221       Return true if index which is owned by node is hidden by the filter.
222     */
isHiddenByFilter(QFileSystemNode * indexNode,const QModelIndex & index)223     inline bool isHiddenByFilter(QFileSystemNode *indexNode, const QModelIndex &index) const
224     {
225        return (indexNode != &root && !index.isValid());
226     }
227     QFileSystemNode *node(const QModelIndex &index) const;
228     QFileSystemNode *node(const QString &path, bool fetch = true) const;
229     inline QModelIndex index(const QString &path, int column = 0) { return index(node(path), column); }
230     QModelIndex index(const QFileSystemNode *node, int column = 0) const;
231     bool filtersAcceptsNode(const QFileSystemNode *node) const;
232     bool passNameFilters(const QFileSystemNode *node) const;
233     void removeNode(QFileSystemNode *parentNode, const QString &name);
234     QFileSystemNode* addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo &info);
235     void addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles);
236     void removeVisibleFile(QFileSystemNode *parentNode, int visibleLocation);
237     void sortChildren(int column, const QModelIndex &parent);
238 
translateVisibleLocation(QFileSystemNode * parent,int row)239     inline int translateVisibleLocation(QFileSystemNode *parent, int row) const {
240         if (sortOrder != Qt::AscendingOrder) {
241             if (parent->dirtyChildrenIndex == -1)
242                 return parent->visibleChildren.count() - row - 1;
243 
244             if (row < parent->dirtyChildrenIndex)
245                 return parent->dirtyChildrenIndex - row - 1;
246         }
247 
248         return row;
249     }
250 
myComputer()251     inline static QString myComputer() {
252         // ### TODO We should query the system to find out what the string should be
253         // XP == "My Computer",
254         // Vista == "Computer",
255         // OS X == "Computer" (sometime user generated) "Benjamin's PowerBook G4"
256 #ifdef Q_OS_WIN
257         return QFileSystemModel::tr("My Computer");
258 #else
259         return QFileSystemModel::tr("Computer");
260 #endif
261     }
262 
delayedSort()263     inline void delayedSort() {
264         if (!delayedSortTimer.isActive())
265             delayedSortTimer.start(0);
266     }
267 
268     QIcon icon(const QModelIndex &index) const;
269     QString name(const QModelIndex &index) const;
270     QString displayName(const QModelIndex &index) const;
271     QString filePath(const QModelIndex &index) const;
272     QString size(const QModelIndex &index) const;
273     static QString size(qint64 bytes);
274     QString type(const QModelIndex &index) const;
275     QString time(const QModelIndex &index) const;
276 
277     void _q_directoryChanged(const QString &directory, const QStringList &list);
278     void _q_performDelayedSort();
279     void _q_fileSystemChanged(const QString &path, const QVector<QPair<QString, QFileInfo> > &);
280     void _q_resolvedName(const QString &fileName, const QString &resolvedName);
281 
282     QDir rootDir;
283 #if QT_CONFIG(filesystemwatcher)
284 #  ifdef Q_OS_WIN
285     QStringList unwatchPathsAt(const QModelIndex &);
watchPaths(const QStringList & paths)286     void watchPaths(const QStringList &paths) { fileInfoGatherer.watchPaths(paths); }
287 #  endif // Q_OS_WIN
288     QFileInfoGatherer fileInfoGatherer;
289 #endif // filesystemwatcher
290     QTimer delayedSortTimer;
291     QHash<const QFileSystemNode*, bool> bypassFilters;
292 #if QT_CONFIG(regularexpression)
293     QStringList nameFilters;
294 #endif
295     QHash<QString, QString> resolvedSymLinks;
296 
297     QFileSystemNode root;
298 
299     struct Fetching {
300         QString dir;
301         QString file;
302         const QFileSystemNode *node;
303     };
304     QVector<Fetching> toFetch;
305 
306     QBasicTimer fetchingTimer;
307 
308     QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs;
309     int sortColumn = 0;
310     Qt::SortOrder sortOrder = Qt::AscendingOrder;
311     bool forceSort = true;
312     bool readOnly = true;
313     bool setRootPath = false;
314     bool nameFilterDisables = true; // false on windows, true on mac and unix
315     // This flag is an optimization for QFileDialog. It enables a sort which is
316     // not recursive, meaning we sort only what we see.
317     bool disableRecursiveSort = false;
318 };
319 Q_DECLARE_TYPEINFO(QFileSystemModelPrivate::Fetching, Q_MOVABLE_TYPE);
320 
321 QT_END_NAMESPACE
322 
323 #endif
324