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 <QtGui/private/qtguiglobal_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
67QT_REQUIRE_CONFIG(filesystemmodel);
68
69QT_BEGIN_NAMESPACE
70
71class ExtendedInformation;
72class QFileSystemModelPrivate;
73class QFileIconProvider;
74
75#if defined(Q_OS_WIN)
76class QFileSystemModelNodePathKey : public QString
77{
78public:
79 QFileSystemModelNodePathKey() {}
80 QFileSystemModelNodePathKey(const QString &other) : QString(other) {}
81 QFileSystemModelNodePathKey(const QFileSystemModelNodePathKey &other) : QString(other) {}
82 bool operator==(const QFileSystemModelNodePathKey &other) const { return !compare(other, Qt::CaseInsensitive); }
83};
84
85Q_DECLARE_TYPEINFO(QFileSystemModelNodePathKey, Q_MOVABLE_TYPE);
86
87inline size_t qHash(const QFileSystemModelNodePathKey &key) { return qHash(key.toCaseFolded()); }
88#else // Q_OS_WIN
89typedef QString QFileSystemModelNodePathKey;
90#endif
91
92class Q_GUI_EXPORT QFileSystemModelPrivate : public QAbstractItemModelPrivate
93{
94 Q_DECLARE_PUBLIC(QFileSystemModel)
95
96public:
97 enum { NumColumns = 4 };
98
99 class QFileSystemNode
100 {
101 public:
102 Q_DISABLE_COPY_MOVE(QFileSystemNode)
103
104 explicit QFileSystemNode(const QString &filename = QString(), QFileSystemNode *p = nullptr)
105 : fileName(filename), parent(p) {}
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
116 inline qint64 size() const { if (info && !info->isDir()) return info->size(); return 0; }
117 inline QString type() const { if (info) return info->displayType; return QLatin1String(""); }
118 inline QDateTime lastModified() const { if (info) return info->lastModified(); return QDateTime(); }
119 inline QFile::Permissions permissions() const { if (info) return info->permissions(); return { }; }
120 inline bool isReadable() const { return ((permissions() & QFile::ReadUser) != 0); }
121 inline bool isWritable() const { return ((permissions() & QFile::WriteUser) != 0); }
122 inline bool isExecutable() const { return ((permissions() & QFile::ExeUser) != 0); }
123 inline bool isDir() const {
124 if (info)
125 return info->isDir();
126 if (children.count() > 0)
127 return true;
128 return false;
129 }
130 inline QFileInfo fileInfo() const { if (info) return info->fileInfo(); return QFileInfo(); }
131 inline bool isFile() const { if (info) return info->isFile(); return true; }
132 inline bool isSystem() const { if (info) return info->isSystem(); return true; }
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); }
135 inline bool caseSensitive() const { if (info) return info->isCaseSensitive(); return false; }
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
165 inline bool hasInformation() const { return info != nullptr; }
166
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()
174 inline int visibleLocation(const QString &childName) {
175 return visibleChildren.indexOf(childName);
176 }
177 void updateIcon(QAbstractFileIconProvider *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
192 void retranslateStrings(QAbstractFileIconProvider *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 */
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
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
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
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 QList<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 &);
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 QList<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};
319Q_DECLARE_TYPEINFO(QFileSystemModelPrivate::Fetching, Q_MOVABLE_TYPE);
320
321QT_END_NAMESPACE
322
323#endif
324