Mercurial > octave
comparison libgui/languages/build_ts/octave-qt/qfilesystemmodel.cpp @ 31537:5ceb4bfcdb0f stable
add tools and files for updating the gui's language files for translation
* libgui/languages/build_ts/README.md: readme for updating language files
* libgui/languages/build_ts/octave-qsci: QScintilla source files for
languages without translation provided by QScintilla
* libgui/languages/build_ts/octave-qt: Qt source files for languages
without translation provided by Qt
author | Torsten Lilge <ttl-octave@mailbox.org> |
---|---|
date | Thu, 24 Nov 2022 06:48:25 +0100 |
parents | |
children | dd5ece3664ed |
comparison
equal
deleted
inserted
replaced
31535:4b80982e0af8 | 31537:5ceb4bfcdb0f |
---|---|
1 /**************************************************************************** | |
2 ** | |
3 ** Copyright (C) 2015 The Qt Company Ltd. | |
4 ** Contact: http://www.qt.io/licensing/ | |
5 ** | |
6 ** This file is part of the QtGui 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 http://www.qt.io/terms-conditions. For further | |
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free | |
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and | |
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the | |
22 ** following information to ensure the GNU Lesser General Public License | |
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and | |
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
25 ** | |
26 ** As a special exception, The Qt Company gives you certain additional | |
27 ** rights. These rights are described in The Qt Company LGPL Exception | |
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
29 ** | |
30 ** GNU General Public License Usage | |
31 ** Alternatively, this file may be used under the terms of the GNU | |
32 ** General Public License version 3.0 as published by the Free Software | |
33 ** Foundation and appearing in the file LICENSE.GPL included in the | |
34 ** packaging of this file. Please review the following information to | |
35 ** ensure the GNU General Public License version 3.0 requirements will be | |
36 ** met: http://www.gnu.org/copyleft/gpl.html. | |
37 ** | |
38 ** $QT_END_LICENSE$ | |
39 ** | |
40 ****************************************************************************/ | |
41 | |
42 #include "qfilesystemmodel_p.h" | |
43 #include "qfilesystemmodel.h" | |
44 #include <qlocale.h> | |
45 #include <qmime.h> | |
46 #include <qurl.h> | |
47 #include <qdebug.h> | |
48 #include <qmessagebox.h> | |
49 #include <qapplication.h> | |
50 | |
51 #ifdef Q_OS_WIN | |
52 #include <qt_windows.h> | |
53 #endif | |
54 #ifdef Q_OS_WIN32 | |
55 #include <QtCore/QVarLengthArray> | |
56 #endif | |
57 | |
58 QT_BEGIN_NAMESPACE | |
59 | |
60 #ifndef QT_NO_FILESYSTEMMODEL | |
61 | |
62 /*! | |
63 \enum QFileSystemModel::Roles | |
64 \value FileIconRole | |
65 \value FilePathRole | |
66 \value FileNameRole | |
67 \value FilePermissions | |
68 */ | |
69 | |
70 /*! | |
71 \class QFileSystemModel | |
72 \since 4.4 | |
73 | |
74 \brief The QFileSystemModel class provides a data model for the local filesystem. | |
75 | |
76 \ingroup model-view | |
77 | |
78 This class provides access to the local filesystem, providing functions | |
79 for renaming and removing files and directories, and for creating new | |
80 directories. In the simplest case, it can be used with a suitable display | |
81 widget as part of a browser or filter. | |
82 | |
83 QFileSystemModel can be accessed using the standard interface provided by | |
84 QAbstractItemModel, but it also provides some convenience functions that are | |
85 specific to a directory model. | |
86 The fileInfo(), isDir(), name(), and path() functions provide information | |
87 about the underlying files and directories related to items in the model. | |
88 Directories can be created and removed using mkdir(), rmdir(). | |
89 | |
90 \note QFileSystemModel requires an instance of a GUI application. | |
91 | |
92 \section1 Example Usage | |
93 | |
94 A directory model that displays the contents of a default directory | |
95 is usually constructed with a parent object: | |
96 | |
97 \snippet doc/src/snippets/shareddirmodel/main.cpp 2 | |
98 | |
99 A tree view can be used to display the contents of the model | |
100 | |
101 \snippet doc/src/snippets/shareddirmodel/main.cpp 4 | |
102 | |
103 and the contents of a particular directory can be displayed by | |
104 setting the tree view's root index: | |
105 | |
106 \snippet doc/src/snippets/shareddirmodel/main.cpp 7 | |
107 | |
108 The view's root index can be used to control how much of a | |
109 hierarchical model is displayed. QDirModel provides a convenience | |
110 function that returns a suitable model index for a path to a | |
111 directory within the model. | |
112 | |
113 \section1 Caching and Performance | |
114 | |
115 QFileSystemModel will not fetch any files or directories until setRootPath() | |
116 is called. This will prevent any unnecessary querying on the file system | |
117 until that point such as listing the drives on Windows. | |
118 | |
119 Unlike QDirModel, QFileSystemModel uses a separate thread to populate | |
120 itself so it will not cause the main thread to hang as the file system | |
121 is being queried. Calls to rowCount() will return 0 until the model | |
122 populates a directory. | |
123 | |
124 QFileSystemModel keeps a cache with file information. The cache is | |
125 automatically kept up to date using the QFileSystemWatcher. | |
126 | |
127 \sa {Model Classes} | |
128 */ | |
129 | |
130 /*! | |
131 \fn bool QFileSystemModel::rmdir(const QModelIndex &index) const | |
132 | |
133 Removes the directory corresponding to the model item \a index in the | |
134 file system model and \bold{deletes the corresponding directory from the | |
135 file system}, returning true if successful. If the directory cannot be | |
136 removed, false is returned. | |
137 | |
138 \warning This function deletes directories from the file system; it does | |
139 \bold{not} move them to a location where they can be recovered. | |
140 | |
141 \sa remove() | |
142 */ | |
143 | |
144 /*! | |
145 \fn QIcon QFileSystemModel::fileName(const QModelIndex &index) const | |
146 | |
147 Returns the file name for the item stored in the model under the given | |
148 \a index. | |
149 */ | |
150 | |
151 /*! | |
152 \fn QIcon QFileSystemModel::fileIcon(const QModelIndex &index) const | |
153 | |
154 Returns the icon for the item stored in the model under the given | |
155 \a index. | |
156 */ | |
157 | |
158 /*! | |
159 \fn QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const | |
160 | |
161 Returns the QFileInfo for the item stored in the model under the given | |
162 \a index. | |
163 */ | |
164 | |
165 /*! | |
166 \fn void QFileSystemModel::rootPathChanged(const QString &newPath); | |
167 | |
168 This signal is emitted whenever the root path has been changed to a \a newPath. | |
169 */ | |
170 | |
171 /*! | |
172 \fn void QFileSystemModel::fileRenamed(const QString &path, const QString &oldName, const QString &newName) | |
173 | |
174 This signal is emitted whenever a file with the \a oldName is successfully | |
175 renamed to \a newName. The file is located in in the directory \a path. | |
176 */ | |
177 | |
178 /*! | |
179 \since 4.7 | |
180 \fn void QFileSystemModel::directoryLoaded(const QString &path) | |
181 | |
182 This signal is emitted when the gatherer thread has finished to load the \a path. | |
183 | |
184 */ | |
185 | |
186 /*! | |
187 \fn bool QFileSystemModel::remove(const QModelIndex &index) const | |
188 | |
189 Removes the model item \a index from the file system model and \bold{deletes the | |
190 corresponding file from the file system}, returning true if successful. If the | |
191 item cannot be removed, false is returned. | |
192 | |
193 \warning This function deletes files from the file system; it does \bold{not} | |
194 move them to a location where they can be recovered. | |
195 | |
196 \sa rmdir() | |
197 */ | |
198 | |
199 bool QFileSystemModel::remove(const QModelIndex &aindex) const | |
200 { | |
201 //### TODO optim | |
202 QString path = filePath(aindex); | |
203 QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func()); | |
204 d->fileInfoGatherer.removePath(path); | |
205 QDirIterator it(path, | |
206 QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot, | |
207 QDirIterator::Subdirectories); | |
208 QStringList children; | |
209 while (it.hasNext()) | |
210 children.prepend(it.next()); | |
211 children.append(path); | |
212 | |
213 bool error = false; | |
214 for (int i = 0; i < children.count(); ++i) { | |
215 QFileInfo info(children.at(i)); | |
216 QModelIndex modelIndex = index(children.at(i)); | |
217 if (info.isDir()) { | |
218 QDir dir; | |
219 if (children.at(i) != path) | |
220 error |= remove(modelIndex); | |
221 error |= rmdir(modelIndex); | |
222 } else { | |
223 error |= QFile::remove(filePath(modelIndex)); | |
224 } | |
225 } | |
226 return error; | |
227 } | |
228 | |
229 /*! | |
230 Constructs a file system model with the given \a parent. | |
231 */ | |
232 QFileSystemModel::QFileSystemModel(QObject *parent) | |
233 : QAbstractItemModel(*new QFileSystemModelPrivate, parent) | |
234 { | |
235 Q_D(QFileSystemModel); | |
236 d->init(); | |
237 } | |
238 | |
239 /*! | |
240 \internal | |
241 */ | |
242 QFileSystemModel::QFileSystemModel(QFileSystemModelPrivate &dd, QObject *parent) | |
243 : QAbstractItemModel(dd, parent) | |
244 { | |
245 Q_D(QFileSystemModel); | |
246 d->init(); | |
247 } | |
248 | |
249 /*! | |
250 Destroys this file system model. | |
251 */ | |
252 QFileSystemModel::~QFileSystemModel() | |
253 { | |
254 } | |
255 | |
256 /*! | |
257 \reimp | |
258 */ | |
259 QModelIndex QFileSystemModel::index(int row, int column, const QModelIndex &parent) const | |
260 { | |
261 Q_D(const QFileSystemModel); | |
262 if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent)) | |
263 return QModelIndex(); | |
264 | |
265 // get the parent node | |
266 QFileSystemModelPrivate::QFileSystemNode *parentNode = (d->indexValid(parent) ? d->node(parent) : | |
267 const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&d->root)); | |
268 Q_ASSERT(parentNode); | |
269 | |
270 // now get the internal pointer for the index | |
271 QString childName = parentNode->visibleChildren[d->translateVisibleLocation(parentNode, row)]; | |
272 const QFileSystemModelPrivate::QFileSystemNode *indexNode = parentNode->children.value(childName); | |
273 Q_ASSERT(indexNode); | |
274 | |
275 return createIndex(row, column, const_cast<QFileSystemModelPrivate::QFileSystemNode*>(indexNode)); | |
276 } | |
277 | |
278 /*! | |
279 \overload | |
280 | |
281 Returns the model item index for the given \a path and \a column. | |
282 */ | |
283 QModelIndex QFileSystemModel::index(const QString &path, int column) const | |
284 { | |
285 Q_D(const QFileSystemModel); | |
286 QFileSystemModelPrivate::QFileSystemNode *node = d->node(path, false); | |
287 QModelIndex idx = d->index(node); | |
288 if (idx.column() != column) | |
289 idx = idx.sibling(idx.row(), column); | |
290 return idx; | |
291 } | |
292 | |
293 /*! | |
294 \internal | |
295 | |
296 Return the QFileSystemNode that goes to index. | |
297 */ | |
298 QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QModelIndex &index) const | |
299 { | |
300 if (!index.isValid()) | |
301 return const_cast<QFileSystemNode*>(&root); | |
302 QFileSystemModelPrivate::QFileSystemNode *indexNode = static_cast<QFileSystemModelPrivate::QFileSystemNode*>(index.internalPointer()); | |
303 Q_ASSERT(indexNode); | |
304 return indexNode; | |
305 } | |
306 | |
307 #ifdef Q_OS_WIN32 | |
308 static QString qt_GetLongPathName(const QString &strShortPath) | |
309 { | |
310 if (strShortPath.isEmpty() | |
311 || strShortPath == QLatin1String(".") || strShortPath == QLatin1String("..")) | |
312 return strShortPath; | |
313 if (strShortPath.length() == 2 && strShortPath.endsWith(QLatin1Char(':'))) | |
314 return strShortPath.toUpper(); | |
315 const QString absPath = QDir(strShortPath).absolutePath(); | |
316 if (absPath.startsWith(QLatin1String("//")) | |
317 || absPath.startsWith(QLatin1String("\\\\"))) // unc | |
318 return QDir::fromNativeSeparators(absPath); | |
319 if (absPath.startsWith(QLatin1Char('/'))) | |
320 return QString(); | |
321 const QString inputString = QLatin1String("\\\\?\\") + QDir::toNativeSeparators(absPath); | |
322 QVarLengthArray<TCHAR, MAX_PATH> buffer(MAX_PATH); | |
323 DWORD result = ::GetLongPathName((wchar_t*)inputString.utf16(), | |
324 buffer.data(), | |
325 buffer.size()); | |
326 if (result > DWORD(buffer.size())) { | |
327 buffer.resize(result); | |
328 result = ::GetLongPathName((wchar_t*)inputString.utf16(), | |
329 buffer.data(), | |
330 buffer.size()); | |
331 } | |
332 if (result > 4) { | |
333 QString longPath = QString::fromWCharArray(buffer.data() + 4); // ignoring prefix | |
334 longPath[0] = longPath.at(0).toUpper(); // capital drive letters | |
335 return QDir::fromNativeSeparators(longPath); | |
336 } else { | |
337 return QDir::fromNativeSeparators(strShortPath); | |
338 } | |
339 } | |
340 #endif | |
341 | |
342 /*! | |
343 \internal | |
344 | |
345 Given a path return the matching QFileSystemNode or &root if invalid | |
346 */ | |
347 QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QString &path, bool fetch) const | |
348 { | |
349 Q_Q(const QFileSystemModel); | |
350 Q_UNUSED(q); | |
351 if (path.isEmpty() || path == myComputer() || path.startsWith(QLatin1Char(':'))) | |
352 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); | |
353 | |
354 // Construct the nodes up to the new root path if they need to be built | |
355 QString absolutePath; | |
356 #ifdef Q_OS_WIN32 | |
357 QString longPath = qt_GetLongPathName(path); | |
358 #else | |
359 QString longPath = path; | |
360 #endif | |
361 if (longPath == rootDir.path()) | |
362 absolutePath = rootDir.absolutePath(); | |
363 else | |
364 absolutePath = QDir(longPath).absolutePath(); | |
365 | |
366 // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const? | |
367 QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts); | |
368 if ((pathElements.isEmpty()) | |
369 #if (!defined(Q_OS_WIN) || defined(Q_OS_WINCE)) && !defined(Q_OS_SYMBIAN) | |
370 && QDir::fromNativeSeparators(longPath) != QLatin1String("/") | |
371 #endif | |
372 ) | |
373 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); | |
374 QModelIndex index = QModelIndex(); // start with "My Computer" | |
375 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) | |
376 if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path | |
377 QString host = QLatin1String("\\\\") + pathElements.first(); | |
378 if (absolutePath == QDir::fromNativeSeparators(host)) | |
379 absolutePath.append(QLatin1Char('/')); | |
380 if (longPath.endsWith(QLatin1Char('/')) && !absolutePath.endsWith(QLatin1Char('/'))) | |
381 absolutePath.append(QLatin1Char('/')); | |
382 int r = 0; | |
383 QFileSystemModelPrivate::QFileSystemNode *rootNode = const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); | |
384 if (!root.children.contains(host.toLower())) { | |
385 if (pathElements.count() == 1 && !absolutePath.endsWith(QLatin1Char('/'))) | |
386 return rootNode; | |
387 QFileInfo info(host); | |
388 if (!info.exists()) | |
389 return rootNode; | |
390 QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this); | |
391 p->addNode(rootNode, host,info); | |
392 p->addVisibleFiles(rootNode, QStringList(host)); | |
393 } | |
394 r = rootNode->visibleLocation(host); | |
395 r = translateVisibleLocation(rootNode, r); | |
396 index = q->index(r, 0, QModelIndex()); | |
397 pathElements.pop_front(); | |
398 } else | |
399 #endif | |
400 | |
401 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) | |
402 { | |
403 if (!pathElements.at(0).contains(QLatin1String(":"))) { | |
404 // The reason we express it like this instead of with anonymous, temporary | |
405 // variables, is to workaround a compiler crash with Q_CC_NOKIAX86. | |
406 QString rootPath = QDir(longPath).rootPath(); | |
407 pathElements.prepend(rootPath); | |
408 } | |
409 if (pathElements.at(0).endsWith(QLatin1Char('/'))) | |
410 pathElements[0].chop(1); | |
411 } | |
412 #else | |
413 // add the "/" item, since it is a valid path element on Unix | |
414 if (absolutePath[0] == QLatin1Char('/')) | |
415 pathElements.prepend(QLatin1String("/")); | |
416 #endif | |
417 | |
418 QFileSystemModelPrivate::QFileSystemNode *parent = node(index); | |
419 | |
420 for (int i = 0; i < pathElements.count(); ++i) { | |
421 QString element = pathElements.at(i); | |
422 #ifdef Q_OS_WIN | |
423 // On Windows, "filename......." and "filename" are equivalent Task #133928 | |
424 while (element.endsWith(QLatin1Char('.'))) | |
425 element.chop(1); | |
426 #endif | |
427 bool alreadyExisted = parent->children.contains(element); | |
428 | |
429 // we couldn't find the path element, we create a new node since we | |
430 // _know_ that the path is valid | |
431 if (alreadyExisted) { | |
432 if ((parent->children.count() == 0) | |
433 || (parent->caseSensitive() | |
434 && parent->children.value(element)->fileName != element) | |
435 || (!parent->caseSensitive() | |
436 && parent->children.value(element)->fileName.toLower() != element.toLower())) | |
437 alreadyExisted = false; | |
438 } | |
439 | |
440 QFileSystemModelPrivate::QFileSystemNode *node; | |
441 if (!alreadyExisted) { | |
442 // Someone might call ::index("file://cookie/monster/doesn't/like/veggies"), | |
443 // a path that doesn't exists, I.E. don't blindly create directories. | |
444 QFileInfo info(absolutePath); | |
445 if (!info.exists()) | |
446 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); | |
447 QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this); | |
448 node = p->addNode(parent, element,info); | |
449 #ifndef QT_NO_FILESYSTEMWATCHER | |
450 node->populate(fileInfoGatherer.getInfo(info)); | |
451 #endif | |
452 } else { | |
453 node = parent->children.value(element); | |
454 } | |
455 | |
456 Q_ASSERT(node); | |
457 if (!node->isVisible) { | |
458 // It has been filtered out | |
459 if (alreadyExisted && node->hasInformation() && !fetch) | |
460 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); | |
461 | |
462 QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this); | |
463 p->addVisibleFiles(parent, QStringList(element)); | |
464 if (!p->bypassFilters.contains(node)) | |
465 p->bypassFilters[node] = 1; | |
466 QString dir = q->filePath(this->index(parent)); | |
467 if (!node->hasInformation() && fetch) { | |
468 Fetching f; | |
469 f.dir = dir; | |
470 f.file = element; | |
471 f.node = node; | |
472 p->toFetch.append(f); | |
473 p->fetchingTimer.start(0, const_cast<QFileSystemModel*>(q)); | |
474 } | |
475 } | |
476 parent = node; | |
477 } | |
478 | |
479 return parent; | |
480 } | |
481 | |
482 /*! | |
483 \reimp | |
484 */ | |
485 void QFileSystemModel::timerEvent(QTimerEvent *event) | |
486 { | |
487 Q_D(QFileSystemModel); | |
488 if (event->timerId() == d->fetchingTimer.timerId()) { | |
489 d->fetchingTimer.stop(); | |
490 #ifndef QT_NO_FILESYSTEMWATCHER | |
491 for (int i = 0; i < d->toFetch.count(); ++i) { | |
492 const QFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node; | |
493 if (!node->hasInformation()) { | |
494 d->fileInfoGatherer.fetchExtendedInformation(d->toFetch.at(i).dir, | |
495 QStringList(d->toFetch.at(i).file)); | |
496 } else { | |
497 // qDebug() << "yah!, you saved a little gerbil soul"; | |
498 } | |
499 } | |
500 #endif | |
501 d->toFetch.clear(); | |
502 } | |
503 } | |
504 | |
505 /*! | |
506 Returns true if the model item \a index represents a directory; | |
507 otherwise returns false. | |
508 */ | |
509 bool QFileSystemModel::isDir(const QModelIndex &index) const | |
510 { | |
511 // This function is for public usage only because it could create a file info | |
512 Q_D(const QFileSystemModel); | |
513 if (!index.isValid()) | |
514 return true; | |
515 QFileSystemModelPrivate::QFileSystemNode *n = d->node(index); | |
516 if (n->hasInformation()) | |
517 return n->isDir(); | |
518 return fileInfo(index).isDir(); | |
519 } | |
520 | |
521 /*! | |
522 Returns the size in bytes of \a index. If the file does not exist, 0 is returned. | |
523 */ | |
524 qint64 QFileSystemModel::size(const QModelIndex &index) const | |
525 { | |
526 Q_D(const QFileSystemModel); | |
527 if (!index.isValid()) | |
528 return 0; | |
529 return d->node(index)->size(); | |
530 } | |
531 | |
532 /*! | |
533 Returns the type of file \a index such as "Directory" or "JPEG file". | |
534 */ | |
535 QString QFileSystemModel::type(const QModelIndex &index) const | |
536 { | |
537 Q_D(const QFileSystemModel); | |
538 if (!index.isValid()) | |
539 return QString(); | |
540 return d->node(index)->type(); | |
541 } | |
542 | |
543 /*! | |
544 Returns the date and time when \a index was last modified. | |
545 */ | |
546 QDateTime QFileSystemModel::lastModified(const QModelIndex &index) const | |
547 { | |
548 Q_D(const QFileSystemModel); | |
549 if (!index.isValid()) | |
550 return QDateTime(); | |
551 return d->node(index)->lastModified(); | |
552 } | |
553 | |
554 /*! | |
555 \reimp | |
556 */ | |
557 QModelIndex QFileSystemModel::parent(const QModelIndex &index) const | |
558 { | |
559 Q_D(const QFileSystemModel); | |
560 if (!d->indexValid(index)) | |
561 return QModelIndex(); | |
562 | |
563 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index); | |
564 Q_ASSERT(indexNode != 0); | |
565 QFileSystemModelPrivate::QFileSystemNode *parentNode = (indexNode ? indexNode->parent : 0); | |
566 if (parentNode == 0 || parentNode == &d->root) | |
567 return QModelIndex(); | |
568 | |
569 // get the parent's row | |
570 QFileSystemModelPrivate::QFileSystemNode *grandParentNode = parentNode->parent; | |
571 Q_ASSERT(grandParentNode->children.contains(parentNode->fileName)); | |
572 int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName)); | |
573 if (visualRow == -1) | |
574 return QModelIndex(); | |
575 return createIndex(visualRow, 0, parentNode); | |
576 } | |
577 | |
578 /* | |
579 \internal | |
580 | |
581 return the index for node | |
582 */ | |
583 QModelIndex QFileSystemModelPrivate::index(const QFileSystemModelPrivate::QFileSystemNode *node) const | |
584 { | |
585 Q_Q(const QFileSystemModel); | |
586 QFileSystemModelPrivate::QFileSystemNode *parentNode = (node ? node->parent : 0); | |
587 if (node == &root || !parentNode) | |
588 return QModelIndex(); | |
589 | |
590 // get the parent's row | |
591 Q_ASSERT(node); | |
592 if (!node->isVisible) | |
593 return QModelIndex(); | |
594 | |
595 int visualRow = translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName)); | |
596 return q->createIndex(visualRow, 0, const_cast<QFileSystemNode*>(node)); | |
597 } | |
598 | |
599 /*! | |
600 \reimp | |
601 */ | |
602 bool QFileSystemModel::hasChildren(const QModelIndex &parent) const | |
603 { | |
604 Q_D(const QFileSystemModel); | |
605 if (parent.column() > 0) | |
606 return false; | |
607 | |
608 if (!parent.isValid()) // drives | |
609 return true; | |
610 | |
611 const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent); | |
612 Q_ASSERT(indexNode); | |
613 return (indexNode->isDir()); | |
614 } | |
615 | |
616 /*! | |
617 \reimp | |
618 */ | |
619 bool QFileSystemModel::canFetchMore(const QModelIndex &parent) const | |
620 { | |
621 Q_D(const QFileSystemModel); | |
622 const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent); | |
623 return (!indexNode->populatedChildren); | |
624 } | |
625 | |
626 /*! | |
627 \reimp | |
628 */ | |
629 void QFileSystemModel::fetchMore(const QModelIndex &parent) | |
630 { | |
631 Q_D(QFileSystemModel); | |
632 if (!d->setRootPath) | |
633 return; | |
634 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent); | |
635 if (indexNode->populatedChildren) | |
636 return; | |
637 indexNode->populatedChildren = true; | |
638 d->fileInfoGatherer.list(filePath(parent)); | |
639 } | |
640 | |
641 /*! | |
642 \reimp | |
643 */ | |
644 int QFileSystemModel::rowCount(const QModelIndex &parent) const | |
645 { | |
646 Q_D(const QFileSystemModel); | |
647 if (parent.column() > 0) | |
648 return 0; | |
649 | |
650 if (!parent.isValid()) | |
651 return d->root.visibleChildren.count(); | |
652 | |
653 const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent); | |
654 return parentNode->visibleChildren.count(); | |
655 } | |
656 | |
657 /*! | |
658 \reimp | |
659 */ | |
660 int QFileSystemModel::columnCount(const QModelIndex &parent) const | |
661 { | |
662 return (parent.column() > 0) ? 0 : 4; | |
663 } | |
664 | |
665 /*! | |
666 Returns the data stored under the given \a role for the item "My Computer". | |
667 | |
668 \sa Qt::ItemDataRole | |
669 */ | |
670 QVariant QFileSystemModel::myComputer(int role) const | |
671 { | |
672 Q_D(const QFileSystemModel); | |
673 switch (role) { | |
674 case Qt::DisplayRole: | |
675 return d->myComputer(); | |
676 case Qt::DecorationRole: | |
677 return d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Computer); | |
678 } | |
679 return QVariant(); | |
680 } | |
681 | |
682 /*! | |
683 \reimp | |
684 */ | |
685 QVariant QFileSystemModel::data(const QModelIndex &index, int role) const | |
686 { | |
687 Q_D(const QFileSystemModel); | |
688 if (!index.isValid() || index.model() != this) | |
689 return QVariant(); | |
690 | |
691 switch (role) { | |
692 case Qt::EditRole: | |
693 case Qt::DisplayRole: | |
694 switch (index.column()) { | |
695 case 0: return d->displayName(index); | |
696 case 1: return d->size(index); | |
697 case 2: return d->type(index); | |
698 case 3: return d->time(index); | |
699 default: | |
700 qWarning("data: invalid display value column %d", index.column()); | |
701 break; | |
702 } | |
703 break; | |
704 case FilePathRole: | |
705 return filePath(index); | |
706 case FileNameRole: | |
707 return d->name(index); | |
708 case Qt::DecorationRole: | |
709 if (index.column() == 0) { | |
710 QIcon icon = d->icon(index); | |
711 if (icon.isNull()) { | |
712 if (d->node(index)->isDir()) | |
713 icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Folder); | |
714 else | |
715 icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::File); | |
716 } | |
717 return icon; | |
718 } | |
719 break; | |
720 case Qt::TextAlignmentRole: | |
721 if (index.column() == 1) | |
722 return Qt::AlignRight; | |
723 break; | |
724 case FilePermissions: | |
725 int p = permissions(index); | |
726 return p; | |
727 } | |
728 | |
729 return QVariant(); | |
730 } | |
731 | |
732 /*! | |
733 \internal | |
734 */ | |
735 QString QFileSystemModelPrivate::size(const QModelIndex &index) const | |
736 { | |
737 if (!index.isValid()) | |
738 return QString(); | |
739 const QFileSystemNode *n = node(index); | |
740 if (n->isDir()) { | |
741 #ifdef Q_OS_MAC | |
742 return QLatin1String("--"); | |
743 #else | |
744 return QLatin1String(""); | |
745 #endif | |
746 // Windows - "" | |
747 // OS X - "--" | |
748 // Konqueror - "4 KB" | |
749 // Nautilus - "9 items" (the number of children) | |
750 } | |
751 return size(n->size()); | |
752 } | |
753 | |
754 QString QFileSystemModelPrivate::size(qint64 bytes) | |
755 { | |
756 // According to the Si standard KB is 1000 bytes, KiB is 1024 | |
757 // but on windows sizes are calculated by dividing by 1024 so we do what they do. | |
758 const qint64 kb = 1024; | |
759 const qint64 mb = 1024 * kb; | |
760 const qint64 gb = 1024 * mb; | |
761 const qint64 tb = 1024 * gb; | |
762 if (bytes >= tb) | |
763 return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3)); | |
764 if (bytes >= gb) | |
765 return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2)); | |
766 if (bytes >= mb) | |
767 return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1)); | |
768 if (bytes >= kb) | |
769 return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb)); | |
770 return QFileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes)); | |
771 } | |
772 | |
773 /*! | |
774 \internal | |
775 */ | |
776 QString QFileSystemModelPrivate::time(const QModelIndex &index) const | |
777 { | |
778 if (!index.isValid()) | |
779 return QString(); | |
780 #ifndef QT_NO_DATESTRING | |
781 return node(index)->lastModified().toString(Qt::SystemLocaleDate); | |
782 #else | |
783 Q_UNUSED(index); | |
784 return QString(); | |
785 #endif | |
786 } | |
787 | |
788 /* | |
789 \internal | |
790 */ | |
791 QString QFileSystemModelPrivate::type(const QModelIndex &index) const | |
792 { | |
793 if (!index.isValid()) | |
794 return QString(); | |
795 return node(index)->type(); | |
796 } | |
797 | |
798 /*! | |
799 \internal | |
800 */ | |
801 QString QFileSystemModelPrivate::name(const QModelIndex &index) const | |
802 { | |
803 if (!index.isValid()) | |
804 return QString(); | |
805 QFileSystemNode *dirNode = node(index); | |
806 if (fileInfoGatherer.resolveSymlinks() && !resolvedSymLinks.isEmpty() && | |
807 dirNode->isSymLink(/* ignoreNtfsSymLinks = */ true)) { | |
808 QString fullPath = QDir::fromNativeSeparators(filePath(index)); | |
809 if (resolvedSymLinks.contains(fullPath)) | |
810 return resolvedSymLinks[fullPath]; | |
811 } | |
812 return dirNode->fileName; | |
813 } | |
814 | |
815 /*! | |
816 \internal | |
817 */ | |
818 QString QFileSystemModelPrivate::displayName(const QModelIndex &index) const | |
819 { | |
820 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) | |
821 QFileSystemNode *dirNode = node(index); | |
822 if (!dirNode->volumeName.isNull()) | |
823 return dirNode->volumeName + QLatin1String(" (") + name(index) + QLatin1Char(')'); | |
824 #endif | |
825 return name(index); | |
826 } | |
827 | |
828 /*! | |
829 \internal | |
830 */ | |
831 QIcon QFileSystemModelPrivate::icon(const QModelIndex &index) const | |
832 { | |
833 if (!index.isValid()) | |
834 return QIcon(); | |
835 return node(index)->icon(); | |
836 } | |
837 | |
838 /*! | |
839 \reimp | |
840 */ | |
841 bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role) | |
842 { | |
843 Q_D(QFileSystemModel); | |
844 if (!idx.isValid() | |
845 || idx.column() != 0 | |
846 || role != Qt::EditRole | |
847 || (flags(idx) & Qt::ItemIsEditable) == 0) { | |
848 return false; | |
849 } | |
850 | |
851 QString newName = value.toString(); | |
852 QString oldName = idx.data().toString(); | |
853 if (newName == idx.data().toString()) | |
854 return true; | |
855 | |
856 if (newName.isEmpty() | |
857 || QDir::toNativeSeparators(newName).contains(QDir::separator()) | |
858 || !QDir(filePath(parent(idx))).rename(oldName, newName)) { | |
859 #ifndef QT_NO_MESSAGEBOX | |
860 QMessageBox::information(0, QFileSystemModel::tr("Invalid filename"), | |
861 QFileSystemModel::tr("<b>The name \"%1\" can not be used.</b><p>Try using another name, with fewer characters or no punctuations marks.") | |
862 .arg(newName), | |
863 QMessageBox::Ok); | |
864 #endif // QT_NO_MESSAGEBOX | |
865 return false; | |
866 } else { | |
867 /* | |
868 *After re-naming something we don't want the selection to change* | |
869 - can't remove rows and later insert | |
870 - can't quickly remove and insert | |
871 - index pointer can't change because treeview doesn't use persistant index's | |
872 | |
873 - if this get any more complicated think of changing it to just | |
874 use layoutChanged | |
875 */ | |
876 | |
877 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx); | |
878 QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent; | |
879 int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName); | |
880 | |
881 d->addNode(parentNode, newName,indexNode->info->fileInfo()); | |
882 parentNode->visibleChildren.removeAt(visibleLocation); | |
883 QFileSystemModelPrivate::QFileSystemNode * oldValue = parentNode->children.value(oldName); | |
884 parentNode->children[newName] = oldValue; | |
885 QFileInfo info(d->rootDir, newName); | |
886 oldValue->fileName = newName; | |
887 oldValue->parent = parentNode; | |
888 oldValue->populate(d->fileInfoGatherer.getInfo(info)); | |
889 oldValue->isVisible = true; | |
890 | |
891 parentNode->children.remove(oldName); | |
892 parentNode->visibleChildren.insert(visibleLocation, newName); | |
893 | |
894 d->delayedSort(); | |
895 emit fileRenamed(filePath(idx.parent()), oldName, newName); | |
896 } | |
897 return true; | |
898 } | |
899 | |
900 /*! | |
901 \reimp | |
902 */ | |
903 QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const | |
904 { | |
905 switch (role) { | |
906 case Qt::DecorationRole: | |
907 if (section == 0) { | |
908 // ### TODO oh man this is ugly and doesn't even work all the way! | |
909 // it is still 2 pixels off | |
910 QImage pixmap(16, 1, QImage::Format_Mono); | |
911 pixmap.fill(0); | |
912 pixmap.setAlphaChannel(pixmap.createAlphaMask()); | |
913 return pixmap; | |
914 } | |
915 break; | |
916 case Qt::TextAlignmentRole: | |
917 return Qt::AlignLeft; | |
918 } | |
919 | |
920 if (orientation != Qt::Horizontal || role != Qt::DisplayRole) | |
921 return QAbstractItemModel::headerData(section, orientation, role); | |
922 | |
923 QString returnValue; | |
924 switch (section) { | |
925 case 0: returnValue = tr("Name"); | |
926 break; | |
927 case 1: returnValue = tr("Size"); | |
928 break; | |
929 case 2: returnValue = | |
930 #ifdef Q_OS_MAC | |
931 tr("Kind", "Match OS X Finder"); | |
932 #else | |
933 tr("Type", "All other platforms"); | |
934 #endif | |
935 break; | |
936 // Windows - Type | |
937 // OS X - Kind | |
938 // Konqueror - File Type | |
939 // Nautilus - Type | |
940 case 3: returnValue = tr("Date Modified"); | |
941 break; | |
942 default: return QVariant(); | |
943 } | |
944 return returnValue; | |
945 } | |
946 | |
947 /*! | |
948 \reimp | |
949 */ | |
950 Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const | |
951 { | |
952 Q_D(const QFileSystemModel); | |
953 Qt::ItemFlags flags = QAbstractItemModel::flags(index); | |
954 if (!index.isValid()) | |
955 return flags; | |
956 | |
957 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index); | |
958 if (d->nameFilterDisables && !d->passNameFilters(indexNode)) { | |
959 flags &= ~Qt::ItemIsEnabled; | |
960 // ### TODO you shouldn't be able to set this as the current item, task 119433 | |
961 return flags; | |
962 } | |
963 | |
964 flags |= Qt::ItemIsDragEnabled; | |
965 if (d->readOnly) | |
966 return flags; | |
967 if ((index.column() == 0) && indexNode->permissions() & QFile::WriteUser) { | |
968 flags |= Qt::ItemIsEditable; | |
969 if (indexNode->isDir()) | |
970 flags |= Qt::ItemIsDropEnabled; | |
971 } | |
972 return flags; | |
973 } | |
974 | |
975 /*! | |
976 \internal | |
977 */ | |
978 void QFileSystemModelPrivate::_q_performDelayedSort() | |
979 { | |
980 Q_Q(QFileSystemModel); | |
981 q->sort(sortColumn, sortOrder); | |
982 } | |
983 | |
984 static inline QChar getNextChar(const QString &s, int location) | |
985 { | |
986 return (location < s.length()) ? s.at(location) : QChar(); | |
987 } | |
988 | |
989 /*! | |
990 Natural number sort, skips spaces. | |
991 | |
992 Examples: | |
993 1, 2, 10, 55, 100 | |
994 01.jpg, 2.jpg, 10.jpg | |
995 | |
996 Note on the algorithm: | |
997 Only as many characters as necessary are looked at and at most they all | |
998 are looked at once. | |
999 | |
1000 Slower then QString::compare() (of course) | |
1001 */ | |
1002 int QFileSystemModelPrivate::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs) | |
1003 { | |
1004 for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) { | |
1005 // skip spaces, tabs and 0's | |
1006 QChar c1 = getNextChar(s1, l1); | |
1007 while (c1.isSpace()) | |
1008 c1 = getNextChar(s1, ++l1); | |
1009 QChar c2 = getNextChar(s2, l2); | |
1010 while (c2.isSpace()) | |
1011 c2 = getNextChar(s2, ++l2); | |
1012 | |
1013 if (c1.isDigit() && c2.isDigit()) { | |
1014 while (c1.digitValue() == 0) | |
1015 c1 = getNextChar(s1, ++l1); | |
1016 while (c2.digitValue() == 0) | |
1017 c2 = getNextChar(s2, ++l2); | |
1018 | |
1019 int lookAheadLocation1 = l1; | |
1020 int lookAheadLocation2 = l2; | |
1021 int currentReturnValue = 0; | |
1022 // find the last digit, setting currentReturnValue as we go if it isn't equal | |
1023 for ( | |
1024 QChar lookAhead1 = c1, lookAhead2 = c2; | |
1025 (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); | |
1026 lookAhead1 = getNextChar(s1, ++lookAheadLocation1), | |
1027 lookAhead2 = getNextChar(s2, ++lookAheadLocation2) | |
1028 ) { | |
1029 bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit(); | |
1030 bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit(); | |
1031 if (!is1ADigit && !is2ADigit) | |
1032 break; | |
1033 if (!is1ADigit) | |
1034 return -1; | |
1035 if (!is2ADigit) | |
1036 return 1; | |
1037 if (currentReturnValue == 0) { | |
1038 if (lookAhead1 < lookAhead2) { | |
1039 currentReturnValue = -1; | |
1040 } else if (lookAhead1 > lookAhead2) { | |
1041 currentReturnValue = 1; | |
1042 } | |
1043 } | |
1044 } | |
1045 if (currentReturnValue != 0) | |
1046 return currentReturnValue; | |
1047 } | |
1048 | |
1049 if (cs == Qt::CaseInsensitive) { | |
1050 if (!c1.isLower()) c1 = c1.toLower(); | |
1051 if (!c2.isLower()) c2 = c2.toLower(); | |
1052 } | |
1053 int r = QString::localeAwareCompare(c1, c2); | |
1054 if (r < 0) | |
1055 return -1; | |
1056 if (r > 0) | |
1057 return 1; | |
1058 } | |
1059 // The two strings are the same (02 == 2) so fall back to the normal sort | |
1060 return QString::compare(s1, s2, cs); | |
1061 } | |
1062 | |
1063 /* | |
1064 \internal | |
1065 Helper functor used by sort() | |
1066 */ | |
1067 class QFileSystemModelSorter | |
1068 { | |
1069 public: | |
1070 inline QFileSystemModelSorter(int column) : sortColumn(column) {} | |
1071 | |
1072 bool compareNodes(const QFileSystemModelPrivate::QFileSystemNode *l, | |
1073 const QFileSystemModelPrivate::QFileSystemNode *r) const | |
1074 { | |
1075 switch (sortColumn) { | |
1076 case 0: { | |
1077 #ifndef Q_OS_MAC | |
1078 // place directories before files | |
1079 bool left = l->isDir(); | |
1080 bool right = r->isDir(); | |
1081 if (left ^ right) | |
1082 return left; | |
1083 #endif | |
1084 return QFileSystemModelPrivate::naturalCompare(l->fileName, | |
1085 r->fileName, Qt::CaseInsensitive) < 0; | |
1086 } | |
1087 case 1: | |
1088 // Directories go first | |
1089 if (l->isDir() && !r->isDir()) | |
1090 return true; | |
1091 return l->size() < r->size(); | |
1092 case 2: | |
1093 return l->type() < r->type(); | |
1094 case 3: | |
1095 return l->lastModified() < r->lastModified(); | |
1096 } | |
1097 Q_ASSERT(false); | |
1098 return false; | |
1099 } | |
1100 | |
1101 bool operator()(const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &l, | |
1102 const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &r) const | |
1103 { | |
1104 return compareNodes(l.first, r.first); | |
1105 } | |
1106 | |
1107 | |
1108 private: | |
1109 int sortColumn; | |
1110 }; | |
1111 | |
1112 /* | |
1113 \internal | |
1114 | |
1115 Sort all of the children of parent | |
1116 */ | |
1117 void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent) | |
1118 { | |
1119 Q_Q(QFileSystemModel); | |
1120 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent); | |
1121 if (indexNode->children.count() == 0) | |
1122 return; | |
1123 | |
1124 QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > values; | |
1125 QHash<QString, QFileSystemNode *>::const_iterator iterator; | |
1126 int i = 0; | |
1127 for(iterator = indexNode->children.begin() ; iterator != indexNode->children.end() ; ++iterator) { | |
1128 if (filtersAcceptsNode(iterator.value())) { | |
1129 values.append(QPair<QFileSystemModelPrivate::QFileSystemNode*, int>((iterator.value()), i)); | |
1130 } else { | |
1131 iterator.value()->isVisible = false; | |
1132 } | |
1133 i++; | |
1134 } | |
1135 QFileSystemModelSorter ms(column); | |
1136 qStableSort(values.begin(), values.end(), ms); | |
1137 // First update the new visible list | |
1138 indexNode->visibleChildren.clear(); | |
1139 //No more dirty item we reset our internal dirty index | |
1140 indexNode->dirtyChildrenIndex = -1; | |
1141 for (int i = 0; i < values.count(); ++i) { | |
1142 indexNode->visibleChildren.append(values.at(i).first->fileName); | |
1143 values.at(i).first->isVisible = true; | |
1144 } | |
1145 | |
1146 if (!disableRecursiveSort) { | |
1147 for (int i = 0; i < q->rowCount(parent); ++i) { | |
1148 const QModelIndex childIndex = q->index(i, 0, parent); | |
1149 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(childIndex); | |
1150 //Only do a recursive sort on visible nodes | |
1151 if (indexNode->isVisible) | |
1152 sortChildren(column, childIndex); | |
1153 } | |
1154 } | |
1155 } | |
1156 | |
1157 /*! | |
1158 \reimp | |
1159 */ | |
1160 void QFileSystemModel::sort(int column, Qt::SortOrder order) | |
1161 { | |
1162 Q_D(QFileSystemModel); | |
1163 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort) | |
1164 return; | |
1165 | |
1166 emit layoutAboutToBeChanged(); | |
1167 QModelIndexList oldList = persistentIndexList(); | |
1168 QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > oldNodes; | |
1169 for (int i = 0; i < oldList.count(); ++i) { | |
1170 QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldList.at(i)), oldList.at(i).column()); | |
1171 oldNodes.append(pair); | |
1172 } | |
1173 | |
1174 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) { | |
1175 //we sort only from where we are, don't need to sort all the model | |
1176 d->sortChildren(column, index(rootPath())); | |
1177 d->sortColumn = column; | |
1178 d->forceSort = false; | |
1179 } | |
1180 d->sortOrder = order; | |
1181 | |
1182 QModelIndexList newList; | |
1183 for (int i = 0; i < oldNodes.count(); ++i) { | |
1184 QModelIndex idx = d->index(oldNodes.at(i).first); | |
1185 idx = idx.sibling(idx.row(), oldNodes.at(i).second); | |
1186 newList.append(idx); | |
1187 } | |
1188 changePersistentIndexList(oldList, newList); | |
1189 emit layoutChanged(); | |
1190 } | |
1191 | |
1192 /*! | |
1193 Returns a list of MIME types that can be used to describe a list of items | |
1194 in the model. | |
1195 */ | |
1196 QStringList QFileSystemModel::mimeTypes() const | |
1197 { | |
1198 return QStringList(QLatin1String("text/uri-list")); | |
1199 } | |
1200 | |
1201 /*! | |
1202 Returns an object that contains a serialized description of the specified | |
1203 \a indexes. The format used to describe the items corresponding to the | |
1204 indexes is obtained from the mimeTypes() function. | |
1205 | |
1206 If the list of indexes is empty, 0 is returned rather than a serialized | |
1207 empty list. | |
1208 */ | |
1209 QMimeData *QFileSystemModel::mimeData(const QModelIndexList &indexes) const | |
1210 { | |
1211 QList<QUrl> urls; | |
1212 QList<QModelIndex>::const_iterator it = indexes.begin(); | |
1213 for (; it != indexes.end(); ++it) | |
1214 if ((*it).column() == 0) | |
1215 urls << QUrl::fromLocalFile(filePath(*it)); | |
1216 QMimeData *data = new QMimeData(); | |
1217 data->setUrls(urls); | |
1218 return data; | |
1219 } | |
1220 | |
1221 /*! | |
1222 Handles the \a data supplied by a drag and drop operation that ended with | |
1223 the given \a action over the row in the model specified by the \a row and | |
1224 \a column and by the \a parent index. | |
1225 | |
1226 \sa supportedDropActions() | |
1227 */ | |
1228 bool QFileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, | |
1229 int row, int column, const QModelIndex &parent) | |
1230 { | |
1231 Q_UNUSED(row); | |
1232 Q_UNUSED(column); | |
1233 if (!parent.isValid() || isReadOnly()) | |
1234 return false; | |
1235 | |
1236 bool success = true; | |
1237 QString to = filePath(parent) + QDir::separator(); | |
1238 | |
1239 QList<QUrl> urls = data->urls(); | |
1240 QList<QUrl>::const_iterator it = urls.constBegin(); | |
1241 | |
1242 switch (action) { | |
1243 case Qt::CopyAction: | |
1244 for (; it != urls.constEnd(); ++it) { | |
1245 QString path = (*it).toLocalFile(); | |
1246 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success; | |
1247 } | |
1248 break; | |
1249 case Qt::LinkAction: | |
1250 for (; it != urls.constEnd(); ++it) { | |
1251 QString path = (*it).toLocalFile(); | |
1252 success = QFile::link(path, to + QFileInfo(path).fileName()) && success; | |
1253 } | |
1254 break; | |
1255 case Qt::MoveAction: | |
1256 for (; it != urls.constEnd(); ++it) { | |
1257 QString path = (*it).toLocalFile(); | |
1258 success = QFile::rename(path, to + QFileInfo(path).fileName()) && success; | |
1259 } | |
1260 break; | |
1261 default: | |
1262 return false; | |
1263 } | |
1264 | |
1265 return success; | |
1266 } | |
1267 | |
1268 /*! | |
1269 \reimp | |
1270 */ | |
1271 Qt::DropActions QFileSystemModel::supportedDropActions() const | |
1272 { | |
1273 return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; | |
1274 } | |
1275 | |
1276 /*! | |
1277 Returns the path of the item stored in the model under the | |
1278 \a index given. | |
1279 */ | |
1280 QString QFileSystemModel::filePath(const QModelIndex &index) const | |
1281 { | |
1282 Q_D(const QFileSystemModel); | |
1283 QString fullPath = d->filePath(index); | |
1284 QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index); | |
1285 if (dirNode->isSymLink() && d->fileInfoGatherer.resolveSymlinks() | |
1286 && d->resolvedSymLinks.contains(fullPath) | |
1287 && dirNode->isDir()) { | |
1288 QFileInfo resolvedInfo(fullPath); | |
1289 resolvedInfo = resolvedInfo.canonicalFilePath(); | |
1290 if (resolvedInfo.exists()) | |
1291 return resolvedInfo.filePath(); | |
1292 } | |
1293 return fullPath; | |
1294 } | |
1295 | |
1296 QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const | |
1297 { | |
1298 Q_Q(const QFileSystemModel); | |
1299 Q_UNUSED(q); | |
1300 if (!index.isValid()) | |
1301 return QString(); | |
1302 Q_ASSERT(index.model() == q); | |
1303 | |
1304 QStringList path; | |
1305 QModelIndex idx = index; | |
1306 while (idx.isValid()) { | |
1307 QFileSystemModelPrivate::QFileSystemNode *dirNode = node(idx); | |
1308 if (dirNode) | |
1309 path.prepend(dirNode->fileName); | |
1310 idx = idx.parent(); | |
1311 } | |
1312 QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator())); | |
1313 #if !defined(Q_OS_WIN) || defined(Q_OS_WINCE) | |
1314 if ((fullPath.length() > 2) && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/')) | |
1315 fullPath = fullPath.mid(1); | |
1316 #endif | |
1317 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) | |
1318 if (fullPath.length() == 2 && fullPath.endsWith(QLatin1Char(':'))) | |
1319 fullPath.append(QLatin1Char('/')); | |
1320 #endif | |
1321 return fullPath; | |
1322 } | |
1323 | |
1324 /*! | |
1325 Create a directory with the \a name in the \a parent model index. | |
1326 */ | |
1327 QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent, const QString &name) | |
1328 { | |
1329 Q_D(QFileSystemModel); | |
1330 if (!parent.isValid()) | |
1331 return parent; | |
1332 | |
1333 QDir dir(filePath(parent)); | |
1334 if (!dir.mkdir(name)) | |
1335 return QModelIndex(); | |
1336 QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent); | |
1337 d->addNode(parentNode, name, QFileInfo()); | |
1338 Q_ASSERT(parentNode->children.contains(name)); | |
1339 QFileSystemModelPrivate::QFileSystemNode *node = parentNode->children[name]; | |
1340 node->populate(d->fileInfoGatherer.getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name))); | |
1341 d->addVisibleFiles(parentNode, QStringList(name)); | |
1342 return d->index(node); | |
1343 } | |
1344 | |
1345 /*! | |
1346 Returns the complete OR-ed together combination of QFile::Permission for the \a index. | |
1347 */ | |
1348 QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index) const | |
1349 { | |
1350 Q_D(const QFileSystemModel); | |
1351 QFile::Permissions p = d->node(index)->permissions(); | |
1352 if (d->readOnly) { | |
1353 p ^= (QFile::WriteOwner | QFile::WriteUser | |
1354 | QFile::WriteGroup | QFile::WriteOther); | |
1355 } | |
1356 return p; | |
1357 } | |
1358 | |
1359 /*! | |
1360 Sets the directory that is being watched by the model to \a newPath by | |
1361 installing a \l{QFileSystemWatcher}{file system watcher} on it. Any | |
1362 changes to files and directories within this directory will be | |
1363 reflected in the model. | |
1364 | |
1365 If the path is changed, the rootPathChanged() signal will be emitted. | |
1366 | |
1367 \note This function does not change the structure of the model or | |
1368 modify the data available to views. In other words, the "root" of | |
1369 the model is \e not changed to include only files and directories | |
1370 within the directory specified by \a newPath in the file system. | |
1371 */ | |
1372 QModelIndex QFileSystemModel::setRootPath(const QString &newPath) | |
1373 { | |
1374 Q_D(QFileSystemModel); | |
1375 #ifdef Q_OS_WIN | |
1376 #ifdef Q_OS_WIN32 | |
1377 QString longNewPath = qt_GetLongPathName(newPath); | |
1378 #else | |
1379 QString longNewPath = QDir::fromNativeSeparators(newPath); | |
1380 #endif | |
1381 #else | |
1382 QString longNewPath = newPath; | |
1383 #endif | |
1384 QDir newPathDir(longNewPath); | |
1385 //we remove .. and . from the given path if exist | |
1386 if (!newPath.isEmpty()) { | |
1387 longNewPath = QDir::cleanPath(longNewPath); | |
1388 newPathDir.setPath(longNewPath); | |
1389 } | |
1390 | |
1391 d->setRootPath = true; | |
1392 | |
1393 //user don't ask for the root path ("") but the conversion failed | |
1394 if (!newPath.isEmpty() && longNewPath.isEmpty()) | |
1395 return d->index(rootPath()); | |
1396 | |
1397 if (d->rootDir.path() == longNewPath) | |
1398 return d->index(rootPath()); | |
1399 | |
1400 bool showDrives = (longNewPath.isEmpty() || longNewPath == d->myComputer()); | |
1401 if (!showDrives && !newPathDir.exists()) | |
1402 return d->index(rootPath()); | |
1403 | |
1404 //We remove the watcher on the previous path | |
1405 if (!rootPath().isEmpty() && rootPath() != QLatin1String(".")) { | |
1406 //This remove the watcher for the old rootPath | |
1407 d->fileInfoGatherer.removePath(rootPath()); | |
1408 //This line "marks" the node as dirty, so the next fetchMore | |
1409 //call on the path will ask the gatherer to install a watcher again | |
1410 //But it doesn't re-fetch everything | |
1411 d->node(rootPath())->populatedChildren = false; | |
1412 } | |
1413 | |
1414 // We have a new valid root path | |
1415 d->rootDir = newPathDir; | |
1416 QModelIndex newRootIndex; | |
1417 if (showDrives) { | |
1418 // otherwise dir will become '.' | |
1419 d->rootDir.setPath(QLatin1String("")); | |
1420 } else { | |
1421 newRootIndex = d->index(newPathDir.path()); | |
1422 } | |
1423 fetchMore(newRootIndex); | |
1424 emit rootPathChanged(longNewPath); | |
1425 d->forceSort = true; | |
1426 d->delayedSort(); | |
1427 return newRootIndex; | |
1428 } | |
1429 | |
1430 /*! | |
1431 The currently set root path | |
1432 | |
1433 \sa rootDirectory() | |
1434 */ | |
1435 QString QFileSystemModel::rootPath() const | |
1436 { | |
1437 Q_D(const QFileSystemModel); | |
1438 return d->rootDir.path(); | |
1439 } | |
1440 | |
1441 /*! | |
1442 The currently set directory | |
1443 | |
1444 \sa rootPath() | |
1445 */ | |
1446 QDir QFileSystemModel::rootDirectory() const | |
1447 { | |
1448 Q_D(const QFileSystemModel); | |
1449 QDir dir(d->rootDir); | |
1450 dir.setNameFilters(nameFilters()); | |
1451 dir.setFilter(filter()); | |
1452 return dir; | |
1453 } | |
1454 | |
1455 /*! | |
1456 Sets the \a provider of file icons for the directory model. | |
1457 */ | |
1458 void QFileSystemModel::setIconProvider(QFileIconProvider *provider) | |
1459 { | |
1460 Q_D(QFileSystemModel); | |
1461 d->fileInfoGatherer.setIconProvider(provider); | |
1462 d->root.updateIcon(provider, QString()); | |
1463 } | |
1464 | |
1465 /*! | |
1466 Returns the file icon provider for this directory model. | |
1467 */ | |
1468 QFileIconProvider *QFileSystemModel::iconProvider() const | |
1469 { | |
1470 Q_D(const QFileSystemModel); | |
1471 return d->fileInfoGatherer.iconProvider(); | |
1472 } | |
1473 | |
1474 /*! | |
1475 Sets the directory model's filter to that specified by \a filters. | |
1476 | |
1477 Note that the filter you set should always include the QDir::AllDirs enum value, | |
1478 otherwise QFileSystemModel won't be able to read the directory structure. | |
1479 | |
1480 \sa QDir::Filters | |
1481 */ | |
1482 void QFileSystemModel::setFilter(QDir::Filters filters) | |
1483 { | |
1484 Q_D(QFileSystemModel); | |
1485 if (d->filters == filters) | |
1486 return; | |
1487 d->filters = filters; | |
1488 // CaseSensitivity might have changed | |
1489 setNameFilters(nameFilters()); | |
1490 d->forceSort = true; | |
1491 d->delayedSort(); | |
1492 } | |
1493 | |
1494 /*! | |
1495 Returns the filter specified for the directory model. | |
1496 | |
1497 If a filter has not been set, the default filter is QDir::AllEntries | | |
1498 QDir::NoDotAndDotDot | QDir::AllDirs. | |
1499 | |
1500 \sa QDir::Filters | |
1501 */ | |
1502 QDir::Filters QFileSystemModel::filter() const | |
1503 { | |
1504 Q_D(const QFileSystemModel); | |
1505 return d->filters; | |
1506 } | |
1507 | |
1508 /*! | |
1509 \property QFileSystemModel::resolveSymlinks | |
1510 \brief Whether the directory model should resolve symbolic links | |
1511 | |
1512 This is only relevant on operating systems that support symbolic links. | |
1513 | |
1514 By default, this property is false. | |
1515 */ | |
1516 void QFileSystemModel::setResolveSymlinks(bool enable) | |
1517 { | |
1518 Q_D(QFileSystemModel); | |
1519 d->fileInfoGatherer.setResolveSymlinks(enable); | |
1520 } | |
1521 | |
1522 bool QFileSystemModel::resolveSymlinks() const | |
1523 { | |
1524 Q_D(const QFileSystemModel); | |
1525 return d->fileInfoGatherer.resolveSymlinks(); | |
1526 } | |
1527 | |
1528 /*! | |
1529 \property QFileSystemModel::readOnly | |
1530 \brief Whether the directory model allows writing to the file system | |
1531 | |
1532 If this property is set to false, the directory model will allow renaming, copying | |
1533 and deleting of files and directories. | |
1534 | |
1535 This property is true by default | |
1536 */ | |
1537 void QFileSystemModel::setReadOnly(bool enable) | |
1538 { | |
1539 Q_D(QFileSystemModel); | |
1540 d->readOnly = enable; | |
1541 } | |
1542 | |
1543 bool QFileSystemModel::isReadOnly() const | |
1544 { | |
1545 Q_D(const QFileSystemModel); | |
1546 return d->readOnly; | |
1547 } | |
1548 | |
1549 /*! | |
1550 \property QFileSystemModel::nameFilterDisables | |
1551 \brief Whether files that don't pass the name filter are hidden or disabled | |
1552 | |
1553 This property is true by default | |
1554 */ | |
1555 void QFileSystemModel::setNameFilterDisables(bool enable) | |
1556 { | |
1557 Q_D(QFileSystemModel); | |
1558 if (d->nameFilterDisables == enable) | |
1559 return; | |
1560 d->nameFilterDisables = enable; | |
1561 d->forceSort = true; | |
1562 d->delayedSort(); | |
1563 } | |
1564 | |
1565 bool QFileSystemModel::nameFilterDisables() const | |
1566 { | |
1567 Q_D(const QFileSystemModel); | |
1568 return d->nameFilterDisables; | |
1569 } | |
1570 | |
1571 /*! | |
1572 Sets the name \a filters to apply against the existing files. | |
1573 */ | |
1574 void QFileSystemModel::setNameFilters(const QStringList &filters) | |
1575 { | |
1576 // Prep the regexp's ahead of time | |
1577 #ifndef QT_NO_REGEXP | |
1578 Q_D(QFileSystemModel); | |
1579 | |
1580 if (!d->bypassFilters.isEmpty()) { | |
1581 // update the bypass filter to only bypass the stuff that must be kept around | |
1582 d->bypassFilters.clear(); | |
1583 // We guarantee that rootPath will stick around | |
1584 QPersistentModelIndex root(index(rootPath())); | |
1585 QModelIndexList persistantList = persistentIndexList(); | |
1586 for (int i = 0; i < persistantList.count(); ++i) { | |
1587 QFileSystemModelPrivate::QFileSystemNode *node; | |
1588 node = d->node(persistantList.at(i)); | |
1589 while (node) { | |
1590 if (d->bypassFilters.contains(node)) | |
1591 break; | |
1592 if (node->isDir()) | |
1593 d->bypassFilters[node] = true; | |
1594 node = node->parent; | |
1595 } | |
1596 } | |
1597 } | |
1598 | |
1599 d->nameFilters.clear(); | |
1600 const Qt::CaseSensitivity caseSensitive = | |
1601 (filter() & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive; | |
1602 for (int i = 0; i < filters.size(); ++i) { | |
1603 d->nameFilters << QRegExp(filters.at(i), caseSensitive, QRegExp::Wildcard); | |
1604 } | |
1605 d->forceSort = true; | |
1606 d->delayedSort(); | |
1607 #endif | |
1608 } | |
1609 | |
1610 /*! | |
1611 Returns a list of filters applied to the names in the model. | |
1612 */ | |
1613 QStringList QFileSystemModel::nameFilters() const | |
1614 { | |
1615 Q_D(const QFileSystemModel); | |
1616 QStringList filters; | |
1617 #ifndef QT_NO_REGEXP | |
1618 for (int i = 0; i < d->nameFilters.size(); ++i) { | |
1619 filters << d->nameFilters.at(i).pattern(); | |
1620 } | |
1621 #endif | |
1622 return filters; | |
1623 } | |
1624 | |
1625 /*! | |
1626 \reimp | |
1627 */ | |
1628 bool QFileSystemModel::event(QEvent *event) | |
1629 { | |
1630 Q_D(QFileSystemModel); | |
1631 if (event->type() == QEvent::LanguageChange) { | |
1632 d->root.retranslateStrings(d->fileInfoGatherer.iconProvider(), QString()); | |
1633 return true; | |
1634 } | |
1635 return QAbstractItemModel::event(event); | |
1636 } | |
1637 | |
1638 bool QFileSystemModel::rmdir(const QModelIndex &aindex) const | |
1639 { | |
1640 QString path = filePath(aindex); | |
1641 QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func()); | |
1642 d->fileInfoGatherer.removePath(path); | |
1643 return QDir().rmdir(path); | |
1644 } | |
1645 | |
1646 /*! | |
1647 \internal | |
1648 | |
1649 Performed quick listing and see if any files have been added or removed, | |
1650 then fetch more information on visible files. | |
1651 */ | |
1652 void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files) | |
1653 { | |
1654 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory, false); | |
1655 if (parentNode->children.count() == 0) | |
1656 return; | |
1657 QStringList toRemove; | |
1658 #if defined(Q_OS_SYMBIAN) | |
1659 // Filename case must be exact in qBinaryFind below, so create a list of all lowercase names. | |
1660 QStringList newFiles; | |
1661 for(int i = 0; i < files.size(); i++) { | |
1662 newFiles << files.at(i).toLower(); | |
1663 } | |
1664 #else | |
1665 QStringList newFiles = files; | |
1666 #endif | |
1667 qSort(newFiles.begin(), newFiles.end()); | |
1668 QHash<QString, QFileSystemNode*>::const_iterator i = parentNode->children.constBegin(); | |
1669 while (i != parentNode->children.constEnd()) { | |
1670 QStringList::iterator iterator; | |
1671 iterator = qBinaryFind(newFiles.begin(), newFiles.end(), | |
1672 #if defined(Q_OS_SYMBIAN) | |
1673 i.value()->fileName.toLower()); | |
1674 #else | |
1675 i.value()->fileName); | |
1676 #endif | |
1677 if (iterator == newFiles.end()) { | |
1678 toRemove.append(i.value()->fileName); | |
1679 } | |
1680 ++i; | |
1681 } | |
1682 for (int i = 0 ; i < toRemove.count() ; ++i ) | |
1683 removeNode(parentNode, toRemove[i]); | |
1684 } | |
1685 | |
1686 /*! | |
1687 \internal | |
1688 | |
1689 Adds a new file to the children of parentNode | |
1690 | |
1691 *WARNING* this will change the count of children | |
1692 */ | |
1693 QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo& info) | |
1694 { | |
1695 // In the common case, itemLocation == count() so check there first | |
1696 QFileSystemModelPrivate::QFileSystemNode *node = new QFileSystemModelPrivate::QFileSystemNode(fileName, parentNode); | |
1697 #ifndef QT_NO_FILESYSTEMWATCHER | |
1698 node->populate(info); | |
1699 #endif | |
1700 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) | |
1701 //The parentNode is "" so we are listing the drives | |
1702 if (parentNode->fileName.isEmpty()) { | |
1703 wchar_t name[MAX_PATH + 1]; | |
1704 //GetVolumeInformation requires to add trailing backslash | |
1705 const QString nodeName = fileName + QLatin1String("\\"); | |
1706 BOOL success = ::GetVolumeInformation((wchar_t *)(nodeName.utf16()), | |
1707 name, MAX_PATH + 1, NULL, 0, NULL, NULL, 0); | |
1708 if (success && name[0]) | |
1709 node->volumeName = QString::fromWCharArray(name); | |
1710 } | |
1711 #endif | |
1712 parentNode->children.insert(fileName, node); | |
1713 return node; | |
1714 } | |
1715 | |
1716 /*! | |
1717 \internal | |
1718 | |
1719 File at parentNode->children(itemLocation) has been removed, remove from the lists | |
1720 and emit signals if necessary | |
1721 | |
1722 *WARNING* this will change the count of children and could change visibleChildren | |
1723 */ | |
1724 void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode, const QString& name) | |
1725 { | |
1726 Q_Q(QFileSystemModel); | |
1727 QModelIndex parent = index(parentNode); | |
1728 bool indexHidden = isHiddenByFilter(parentNode, parent); | |
1729 | |
1730 int vLocation = parentNode->visibleLocation(name); | |
1731 if (vLocation >= 0 && !indexHidden) | |
1732 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation), | |
1733 translateVisibleLocation(parentNode, vLocation)); | |
1734 QFileSystemNode * node = parentNode->children.take(name); | |
1735 delete node; | |
1736 // cleanup sort files after removing rather then re-sorting which is O(n) | |
1737 if (vLocation >= 0) | |
1738 parentNode->visibleChildren.removeAt(vLocation); | |
1739 if (vLocation >= 0 && !indexHidden) | |
1740 q->endRemoveRows(); | |
1741 } | |
1742 | |
1743 /* | |
1744 \internal | |
1745 Helper functor used by addVisibleFiles() | |
1746 */ | |
1747 class QFileSystemModelVisibleFinder | |
1748 { | |
1749 public: | |
1750 inline QFileSystemModelVisibleFinder(QFileSystemModelPrivate::QFileSystemNode *node, QFileSystemModelSorter *sorter) : parentNode(node), sorter(sorter) {} | |
1751 | |
1752 bool operator()(const QString &, QString r) const | |
1753 { | |
1754 return sorter->compareNodes(parentNode->children.value(name), parentNode->children.value(r)); | |
1755 } | |
1756 | |
1757 QString name; | |
1758 private: | |
1759 QFileSystemModelPrivate::QFileSystemNode *parentNode; | |
1760 QFileSystemModelSorter *sorter; | |
1761 }; | |
1762 | |
1763 /*! | |
1764 \internal | |
1765 | |
1766 File at parentNode->children(itemLocation) was not visible before, but now should be | |
1767 and emit signals if necessary. | |
1768 | |
1769 *WARNING* this will change the visible count | |
1770 */ | |
1771 void QFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles) | |
1772 { | |
1773 Q_Q(QFileSystemModel); | |
1774 QModelIndex parent = index(parentNode); | |
1775 bool indexHidden = isHiddenByFilter(parentNode, parent); | |
1776 if (!indexHidden) { | |
1777 q->beginInsertRows(parent, parentNode->visibleChildren.count() , parentNode->visibleChildren.count() + newFiles.count() - 1); | |
1778 } | |
1779 | |
1780 if (parentNode->dirtyChildrenIndex == -1) | |
1781 parentNode->dirtyChildrenIndex = parentNode->visibleChildren.count(); | |
1782 | |
1783 for (int i = 0; i < newFiles.count(); ++i) { | |
1784 parentNode->visibleChildren.append(newFiles.at(i)); | |
1785 parentNode->children[newFiles.at(i)]->isVisible = true; | |
1786 } | |
1787 if (!indexHidden) | |
1788 q->endInsertRows(); | |
1789 } | |
1790 | |
1791 /*! | |
1792 \internal | |
1793 | |
1794 File was visible before, but now should NOT be | |
1795 | |
1796 *WARNING* this will change the visible count | |
1797 */ | |
1798 void QFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode, int vLocation) | |
1799 { | |
1800 Q_Q(QFileSystemModel); | |
1801 if (vLocation == -1) | |
1802 return; | |
1803 QModelIndex parent = index(parentNode); | |
1804 bool indexHidden = isHiddenByFilter(parentNode, parent); | |
1805 if (!indexHidden) | |
1806 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation), | |
1807 translateVisibleLocation(parentNode, vLocation)); | |
1808 parentNode->children[parentNode->visibleChildren.at(vLocation)]->isVisible = false; | |
1809 parentNode->visibleChildren.removeAt(vLocation); | |
1810 if (!indexHidden) | |
1811 q->endRemoveRows(); | |
1812 } | |
1813 | |
1814 /*! | |
1815 \internal | |
1816 | |
1817 The thread has received new information about files, | |
1818 update and emit dataChanged if it has actually changed. | |
1819 */ | |
1820 void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo> > &updates) | |
1821 { | |
1822 Q_Q(QFileSystemModel); | |
1823 QVector<QString> rowsToUpdate; | |
1824 QStringList newFiles; | |
1825 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path, false); | |
1826 QModelIndex parentIndex = index(parentNode); | |
1827 for (int i = 0; i < updates.count(); ++i) { | |
1828 QString fileName = updates.at(i).first; | |
1829 Q_ASSERT(!fileName.isEmpty()); | |
1830 QExtendedInformation info = fileInfoGatherer.getInfo(updates.at(i).second); | |
1831 bool previouslyHere = parentNode->children.contains(fileName); | |
1832 if (!previouslyHere) { | |
1833 addNode(parentNode, fileName, info.fileInfo()); | |
1834 } | |
1835 QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName); | |
1836 bool isCaseSensitive = parentNode->caseSensitive(); | |
1837 if (isCaseSensitive) { | |
1838 if (node->fileName != fileName) | |
1839 continue; | |
1840 } else { | |
1841 if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0) | |
1842 continue; | |
1843 } | |
1844 if (isCaseSensitive) { | |
1845 Q_ASSERT(node->fileName == fileName); | |
1846 } else { | |
1847 node->fileName = fileName; | |
1848 } | |
1849 | |
1850 if (info.size() == -1 && !info.isSymLink()) { | |
1851 removeNode(parentNode, fileName); | |
1852 continue; | |
1853 } | |
1854 if (*node != info ) { | |
1855 node->populate(info); | |
1856 bypassFilters.remove(node); | |
1857 // brand new information. | |
1858 if (filtersAcceptsNode(node)) { | |
1859 if (!node->isVisible) { | |
1860 newFiles.append(fileName); | |
1861 } else { | |
1862 rowsToUpdate.append(fileName); | |
1863 } | |
1864 } else { | |
1865 if (node->isVisible) { | |
1866 int visibleLocation = parentNode->visibleLocation(fileName); | |
1867 removeVisibleFile(parentNode, visibleLocation); | |
1868 } else { | |
1869 // The file is not visible, don't do anything | |
1870 } | |
1871 } | |
1872 } | |
1873 } | |
1874 | |
1875 // bundle up all of the changed signals into as few as possible. | |
1876 qSort(rowsToUpdate.begin(), rowsToUpdate.end()); | |
1877 QString min; | |
1878 QString max; | |
1879 for (int i = 0; i < rowsToUpdate.count(); ++i) { | |
1880 QString value = rowsToUpdate.at(i); | |
1881 //##TODO is there a way to bundle signals with QString as the content of the list? | |
1882 /*if (min.isEmpty()) { | |
1883 min = value; | |
1884 if (i != rowsToUpdate.count() - 1) | |
1885 continue; | |
1886 } | |
1887 if (i != rowsToUpdate.count() - 1) { | |
1888 if ((value == min + 1 && max.isEmpty()) || value == max + 1) { | |
1889 max = value; | |
1890 continue; | |
1891 } | |
1892 }*/ | |
1893 max = value; | |
1894 min = value; | |
1895 int visibleMin = parentNode->visibleLocation(min); | |
1896 int visibleMax = parentNode->visibleLocation(max); | |
1897 if (visibleMin >= 0 | |
1898 && visibleMin < parentNode->visibleChildren.count() | |
1899 && parentNode->visibleChildren.at(visibleMin) == min | |
1900 && visibleMax >= 0) { | |
1901 QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMin), 0, parentIndex); | |
1902 QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMax), 3, parentIndex); | |
1903 emit q->dataChanged(bottom, top); | |
1904 } | |
1905 | |
1906 /*min = QString(); | |
1907 max = QString();*/ | |
1908 } | |
1909 | |
1910 if (newFiles.count() > 0) { | |
1911 addVisibleFiles(parentNode, newFiles); | |
1912 } | |
1913 | |
1914 if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) { | |
1915 forceSort = true; | |
1916 delayedSort(); | |
1917 } | |
1918 } | |
1919 | |
1920 /*! | |
1921 \internal | |
1922 */ | |
1923 void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName) | |
1924 { | |
1925 resolvedSymLinks[fileName] = resolvedName; | |
1926 } | |
1927 | |
1928 /*! | |
1929 \internal | |
1930 */ | |
1931 void QFileSystemModelPrivate::init() | |
1932 { | |
1933 Q_Q(QFileSystemModel); | |
1934 qRegisterMetaType<QList<QPair<QString,QFileInfo> > >("QList<QPair<QString,QFileInfo> >"); | |
1935 q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(QString,QStringList)), | |
1936 q, SLOT(_q_directoryChanged(QString,QStringList))); | |
1937 q->connect(&fileInfoGatherer, SIGNAL(updates(QString,QList<QPair<QString,QFileInfo> >)), | |
1938 q, SLOT(_q_fileSystemChanged(QString,QList<QPair<QString,QFileInfo> >))); | |
1939 q->connect(&fileInfoGatherer, SIGNAL(nameResolved(QString,QString)), | |
1940 q, SLOT(_q_resolvedName(QString,QString))); | |
1941 q->connect(&fileInfoGatherer, SIGNAL(directoryLoaded(QString)), | |
1942 q, SIGNAL(directoryLoaded(QString))); | |
1943 q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection); | |
1944 | |
1945 QHash<int, QByteArray> roles = q->roleNames(); | |
1946 roles.insertMulti(QFileSystemModel::FileIconRole, "fileIcon"); // == Qt::decoration | |
1947 roles.insert(QFileSystemModel::FilePathRole, "filePath"); | |
1948 roles.insert(QFileSystemModel::FileNameRole, "fileName"); | |
1949 roles.insert(QFileSystemModel::FilePermissions, "filePermissions"); | |
1950 q->setRoleNames(roles); | |
1951 } | |
1952 | |
1953 /*! | |
1954 \internal | |
1955 | |
1956 Returns false if node doesn't pass the filters otherwise true | |
1957 | |
1958 QDir::Modified is not supported | |
1959 QDir::Drives is not supported | |
1960 */ | |
1961 bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) const | |
1962 { | |
1963 // always accept drives | |
1964 if (node->parent == &root || bypassFilters.contains(node)) | |
1965 return true; | |
1966 | |
1967 // If we don't know anything yet don't accept it | |
1968 if (!node->hasInformation()) | |
1969 return false; | |
1970 | |
1971 const bool filterPermissions = ((filters & QDir::PermissionMask) | |
1972 && (filters & QDir::PermissionMask) != QDir::PermissionMask); | |
1973 const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs)); | |
1974 const bool hideFiles = !(filters & QDir::Files); | |
1975 const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable)); | |
1976 const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable)); | |
1977 const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable)); | |
1978 const bool hideHidden = !(filters & QDir::Hidden); | |
1979 const bool hideSystem = !(filters & QDir::System); | |
1980 const bool hideSymlinks = (filters & QDir::NoSymLinks); | |
1981 const bool hideDot = (filters & QDir::NoDot) || (filters & QDir::NoDotAndDotDot); // ### Qt5: simplify (because NoDotAndDotDot=NoDot|NoDotDot) | |
1982 const bool hideDotDot = (filters & QDir::NoDotDot) || (filters & QDir::NoDotAndDotDot); // ### Qt5: simplify (because NoDotAndDotDot=NoDot|NoDotDot) | |
1983 | |
1984 // Note that we match the behavior of entryList and not QFileInfo on this and this | |
1985 // incompatibility won't be fixed until Qt 5 at least | |
1986 bool isDot = (node->fileName == QLatin1String(".")); | |
1987 bool isDotDot = (node->fileName == QLatin1String("..")); | |
1988 if ( (hideHidden && !(isDot || isDotDot) && node->isHidden()) | |
1989 || (hideSystem && node->isSystem()) | |
1990 || (hideDirs && node->isDir()) | |
1991 || (hideFiles && node->isFile()) | |
1992 || (hideSymlinks && node->isSymLink()) | |
1993 || (hideReadable && node->isReadable()) | |
1994 || (hideWritable && node->isWritable()) | |
1995 || (hideExecutable && node->isExecutable()) | |
1996 || (hideDot && isDot) | |
1997 || (hideDotDot && isDotDot)) | |
1998 return false; | |
1999 | |
2000 return nameFilterDisables || passNameFilters(node); | |
2001 } | |
2002 | |
2003 /* | |
2004 \internal | |
2005 | |
2006 Returns true if node passes the name filters and should be visible. | |
2007 */ | |
2008 bool QFileSystemModelPrivate::passNameFilters(const QFileSystemNode *node) const | |
2009 { | |
2010 #ifndef QT_NO_REGEXP | |
2011 if (nameFilters.isEmpty()) | |
2012 return true; | |
2013 | |
2014 // Check the name regularexpression filters | |
2015 if (!(node->isDir() && (filters & QDir::AllDirs))) { | |
2016 for (int i = 0; i < nameFilters.size(); ++i) { | |
2017 if (nameFilters.at(i).exactMatch(node->fileName)) | |
2018 return true; | |
2019 } | |
2020 return false; | |
2021 } | |
2022 #endif | |
2023 return true; | |
2024 } | |
2025 | |
2026 QT_END_NAMESPACE | |
2027 | |
2028 #include "moc_qfilesystemmodel.cpp" | |
2029 | |
2030 #endif // QT_NO_FILESYSTEMMODEL |