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

source code of qtbase/src/gui/itemmodels/qfilesystemmodel_p.h