|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qdirmodel.h" |
|
43 |
|
44 #ifndef QT_NO_DIRMODEL |
|
45 #include <qstack.h> |
|
46 #include <qfile.h> |
|
47 #include <qfilesystemmodel.h> |
|
48 #include <qurl.h> |
|
49 #include <qmime.h> |
|
50 #include <qpair.h> |
|
51 #include <qvector.h> |
|
52 #include <qobject.h> |
|
53 #include <qdatetime.h> |
|
54 #include <qlocale.h> |
|
55 #include <qstyle.h> |
|
56 #include <qapplication.h> |
|
57 #include <private/qabstractitemmodel_p.h> |
|
58 #include <qdebug.h> |
|
59 |
|
60 /*! |
|
61 \enum QDirModel::Roles |
|
62 \value FileIconRole |
|
63 \value FilePathRole |
|
64 \value FileNameRole |
|
65 */ |
|
66 |
|
67 QT_BEGIN_NAMESPACE |
|
68 |
|
69 class QDirModelPrivate : public QAbstractItemModelPrivate |
|
70 { |
|
71 Q_DECLARE_PUBLIC(QDirModel) |
|
72 |
|
73 public: |
|
74 struct QDirNode |
|
75 { |
|
76 QDirNode() : parent(0), populated(false), stat(false) {} |
|
77 ~QDirNode() { children.clear(); } |
|
78 QDirNode *parent; |
|
79 QFileInfo info; |
|
80 QIcon icon; // cache the icon |
|
81 mutable QVector<QDirNode> children; |
|
82 mutable bool populated; // have we read the children |
|
83 mutable bool stat; |
|
84 }; |
|
85 |
|
86 QDirModelPrivate() |
|
87 : resolveSymlinks(true), |
|
88 readOnly(true), |
|
89 lazyChildCount(false), |
|
90 allowAppendChild(true), |
|
91 iconProvider(&defaultProvider), |
|
92 shouldStat(true) // ### This is set to false by QFileDialog |
|
93 { } |
|
94 |
|
95 void init(); |
|
96 QDirNode *node(int row, QDirNode *parent) const; |
|
97 QVector<QDirNode> children(QDirNode *parent, bool stat) const; |
|
98 |
|
99 void _q_refresh(); |
|
100 |
|
101 void savePersistentIndexes(); |
|
102 void restorePersistentIndexes(); |
|
103 |
|
104 QFileInfoList entryInfoList(const QString &path) const; |
|
105 QStringList entryList(const QString &path) const; |
|
106 |
|
107 QString name(const QModelIndex &index) const; |
|
108 QString size(const QModelIndex &index) const; |
|
109 QString type(const QModelIndex &index) const; |
|
110 QString time(const QModelIndex &index) const; |
|
111 |
|
112 void appendChild(QDirModelPrivate::QDirNode *parent, const QString &path) const; |
|
113 static QFileInfo resolvedInfo(QFileInfo info); |
|
114 |
|
115 inline QDirNode *node(const QModelIndex &index) const; |
|
116 inline void populate(QDirNode *parent) const; |
|
117 inline void clear(QDirNode *parent) const; |
|
118 |
|
119 void invalidate(); |
|
120 |
|
121 mutable QDirNode root; |
|
122 bool resolveSymlinks; |
|
123 bool readOnly; |
|
124 bool lazyChildCount; |
|
125 bool allowAppendChild; |
|
126 |
|
127 QDir::Filters filters; |
|
128 QDir::SortFlags sort; |
|
129 QStringList nameFilters; |
|
130 |
|
131 QFileIconProvider *iconProvider; |
|
132 QFileIconProvider defaultProvider; |
|
133 |
|
134 struct SavedPersistent { |
|
135 QString path; |
|
136 int column; |
|
137 QPersistentModelIndexData *data; |
|
138 QPersistentModelIndex index; |
|
139 }; |
|
140 QList<SavedPersistent> savedPersistent; |
|
141 QPersistentModelIndex toBeRefreshed; |
|
142 |
|
143 bool shouldStat; // use the "carefull not to stat directories" mode |
|
144 }; |
|
145 |
|
146 void qt_setDirModelShouldNotStat(QDirModelPrivate *modelPrivate) |
|
147 { |
|
148 modelPrivate->shouldStat = false; |
|
149 } |
|
150 |
|
151 QDirModelPrivate::QDirNode *QDirModelPrivate::node(const QModelIndex &index) const |
|
152 { |
|
153 QDirModelPrivate::QDirNode *n = |
|
154 static_cast<QDirModelPrivate::QDirNode*>(index.internalPointer()); |
|
155 Q_ASSERT(n); |
|
156 return n; |
|
157 } |
|
158 |
|
159 void QDirModelPrivate::populate(QDirNode *parent) const |
|
160 { |
|
161 Q_ASSERT(parent); |
|
162 parent->children = children(parent, parent->stat); |
|
163 parent->populated = true; |
|
164 } |
|
165 |
|
166 void QDirModelPrivate::clear(QDirNode *parent) const |
|
167 { |
|
168 Q_ASSERT(parent); |
|
169 parent->children.clear(); |
|
170 parent->populated = false; |
|
171 } |
|
172 |
|
173 void QDirModelPrivate::invalidate() |
|
174 { |
|
175 QStack<const QDirNode*> nodes; |
|
176 nodes.push(&root); |
|
177 while (!nodes.empty()) { |
|
178 const QDirNode *current = nodes.pop(); |
|
179 current->stat = false; |
|
180 const QVector<QDirNode> children = current->children; |
|
181 for (int i = 0; i < children.count(); ++i) |
|
182 nodes.push(&children.at(i)); |
|
183 } |
|
184 } |
|
185 |
|
186 /*! |
|
187 \class QDirModel |
|
188 |
|
189 \brief The QDirModel class provides a data model for the local filesystem. |
|
190 |
|
191 \ingroup model-view |
|
192 |
|
193 \note The usage of QDirModel is not recommended anymore. The |
|
194 QFileSystemModel class is a more performant alternative. |
|
195 |
|
196 This class provides access to the local filesystem, providing functions |
|
197 for renaming and removing files and directories, and for creating new |
|
198 directories. In the simplest case, it can be used with a suitable display |
|
199 widget as part of a browser or filer. |
|
200 |
|
201 QDirModel keeps a cache with file information. The cache needs to be |
|
202 updated with refresh(). |
|
203 |
|
204 A directory model that displays the contents of a default directory |
|
205 is usually constructed with a parent object: |
|
206 |
|
207 \snippet doc/src/snippets/shareddirmodel/main.cpp 2 |
|
208 |
|
209 A tree view can be used to display the contents of the model |
|
210 |
|
211 \snippet doc/src/snippets/shareddirmodel/main.cpp 4 |
|
212 |
|
213 and the contents of a particular directory can be displayed by |
|
214 setting the tree view's root index: |
|
215 |
|
216 \snippet doc/src/snippets/shareddirmodel/main.cpp 7 |
|
217 |
|
218 The view's root index can be used to control how much of a |
|
219 hierarchical model is displayed. QDirModel provides a convenience |
|
220 function that returns a suitable model index for a path to a |
|
221 directory within the model. |
|
222 |
|
223 QDirModel can be accessed using the standard interface provided by |
|
224 QAbstractItemModel, but it also provides some convenience functions |
|
225 that are specific to a directory model. The fileInfo() and isDir() |
|
226 functions provide information about the underlying files and directories |
|
227 related to items in the model. |
|
228 |
|
229 Directories can be created and removed using mkdir(), rmdir(), and the |
|
230 model will be automatically updated to take the changes into account. |
|
231 |
|
232 \note QDirModel requires an instance of a GUI application. |
|
233 |
|
234 \sa nameFilters(), setFilter(), filter(), QListView, QTreeView, QFileSystemModel, |
|
235 {Dir View Example}, {Model Classes} |
|
236 */ |
|
237 |
|
238 /*! |
|
239 Constructs a new directory model with the given \a parent. |
|
240 Only those files matching the \a nameFilters and the |
|
241 \a filters are included in the model. The sort order is given by the |
|
242 \a sort flags. |
|
243 */ |
|
244 |
|
245 QDirModel::QDirModel(const QStringList &nameFilters, |
|
246 QDir::Filters filters, |
|
247 QDir::SortFlags sort, |
|
248 QObject *parent) |
|
249 : QAbstractItemModel(*new QDirModelPrivate, parent) |
|
250 { |
|
251 Q_D(QDirModel); |
|
252 // we always start with QDir::drives() |
|
253 d->nameFilters = nameFilters.isEmpty() ? QStringList(QLatin1String("*")) : nameFilters; |
|
254 d->filters = filters; |
|
255 d->sort = sort; |
|
256 d->root.parent = 0; |
|
257 d->root.info = QFileInfo(); |
|
258 d->clear(&d->root); |
|
259 } |
|
260 |
|
261 /*! |
|
262 Constructs a directory model with the given \a parent. |
|
263 */ |
|
264 |
|
265 QDirModel::QDirModel(QObject *parent) |
|
266 : QAbstractItemModel(*new QDirModelPrivate, parent) |
|
267 { |
|
268 Q_D(QDirModel); |
|
269 d->init(); |
|
270 } |
|
271 |
|
272 /*! |
|
273 \internal |
|
274 */ |
|
275 QDirModel::QDirModel(QDirModelPrivate &dd, QObject *parent) |
|
276 : QAbstractItemModel(dd, parent) |
|
277 { |
|
278 Q_D(QDirModel); |
|
279 d->init(); |
|
280 } |
|
281 |
|
282 /*! |
|
283 Destroys this directory model. |
|
284 */ |
|
285 |
|
286 QDirModel::~QDirModel() |
|
287 { |
|
288 |
|
289 } |
|
290 |
|
291 /*! |
|
292 Returns the model item index for the item in the \a parent with the |
|
293 given \a row and \a column. |
|
294 |
|
295 */ |
|
296 |
|
297 QModelIndex QDirModel::index(int row, int column, const QModelIndex &parent) const |
|
298 { |
|
299 Q_D(const QDirModel); |
|
300 // note that rowCount does lazy population |
|
301 if (column < 0 || column >= columnCount(parent) || row < 0 || parent.column() > 0) |
|
302 return QModelIndex(); |
|
303 // make sure the list of children is up to date |
|
304 QDirModelPrivate::QDirNode *p = (d->indexValid(parent) ? d->node(parent) : &d->root); |
|
305 Q_ASSERT(p); |
|
306 if (!p->populated) |
|
307 d->populate(p); // populate without stat'ing |
|
308 if (row >= p->children.count()) |
|
309 return QModelIndex(); |
|
310 // now get the internal pointer for the index |
|
311 QDirModelPrivate::QDirNode *n = d->node(row, d->indexValid(parent) ? p : 0); |
|
312 Q_ASSERT(n); |
|
313 |
|
314 return createIndex(row, column, n); |
|
315 } |
|
316 |
|
317 /*! |
|
318 Return the parent of the given \a child model item. |
|
319 */ |
|
320 |
|
321 QModelIndex QDirModel::parent(const QModelIndex &child) const |
|
322 { |
|
323 Q_D(const QDirModel); |
|
324 |
|
325 if (!d->indexValid(child)) |
|
326 return QModelIndex(); |
|
327 QDirModelPrivate::QDirNode *node = d->node(child); |
|
328 QDirModelPrivate::QDirNode *par = (node ? node->parent : 0); |
|
329 if (par == 0) // parent is the root node |
|
330 return QModelIndex(); |
|
331 |
|
332 // get the parent's row |
|
333 const QVector<QDirModelPrivate::QDirNode> children = |
|
334 par->parent ? par->parent->children : d->root.children; |
|
335 Q_ASSERT(children.count() > 0); |
|
336 int row = (par - &(children.at(0))); |
|
337 Q_ASSERT(row >= 0); |
|
338 |
|
339 return createIndex(row, 0, par); |
|
340 } |
|
341 |
|
342 /*! |
|
343 Returns the number of rows in the \a parent model item. |
|
344 |
|
345 */ |
|
346 |
|
347 int QDirModel::rowCount(const QModelIndex &parent) const |
|
348 { |
|
349 Q_D(const QDirModel); |
|
350 if (parent.column() > 0) |
|
351 return 0; |
|
352 |
|
353 if (!parent.isValid()) { |
|
354 if (!d->root.populated) // lazy population |
|
355 d->populate(&d->root); |
|
356 return d->root.children.count(); |
|
357 } |
|
358 if (parent.model() != this) |
|
359 return 0; |
|
360 QDirModelPrivate::QDirNode *p = d->node(parent); |
|
361 if (p->info.isDir() && !p->populated) // lazy population |
|
362 d->populate(p); |
|
363 return p->children.count(); |
|
364 } |
|
365 |
|
366 /*! |
|
367 Returns the number of columns in the \a parent model item. |
|
368 |
|
369 */ |
|
370 |
|
371 int QDirModel::columnCount(const QModelIndex &parent) const |
|
372 { |
|
373 if (parent.column() > 0) |
|
374 return 0; |
|
375 return 4; |
|
376 } |
|
377 |
|
378 /*! |
|
379 Returns the data for the model item \a index with the given \a role. |
|
380 */ |
|
381 QVariant QDirModel::data(const QModelIndex &index, int role) const |
|
382 { |
|
383 Q_D(const QDirModel); |
|
384 if (!d->indexValid(index)) |
|
385 return QVariant(); |
|
386 |
|
387 if (role == Qt::DisplayRole || role == Qt::EditRole) { |
|
388 switch (index.column()) { |
|
389 case 0: return d->name(index); |
|
390 case 1: return d->size(index); |
|
391 case 2: return d->type(index); |
|
392 case 3: return d->time(index); |
|
393 default: |
|
394 qWarning("data: invalid display value column %d", index.column()); |
|
395 return QVariant(); |
|
396 } |
|
397 } |
|
398 |
|
399 if (index.column() == 0) { |
|
400 if (role == FileIconRole) |
|
401 return fileIcon(index); |
|
402 if (role == FilePathRole) |
|
403 return filePath(index); |
|
404 if (role == FileNameRole) |
|
405 return fileName(index); |
|
406 } |
|
407 |
|
408 if (index.column() == 1 && Qt::TextAlignmentRole == role) { |
|
409 return Qt::AlignRight; |
|
410 } |
|
411 return QVariant(); |
|
412 } |
|
413 |
|
414 /*! |
|
415 Sets the data for the model item \a index with the given \a role to |
|
416 the data referenced by the \a value. Returns true if successful; |
|
417 otherwise returns false. |
|
418 |
|
419 \sa Qt::ItemDataRole |
|
420 */ |
|
421 |
|
422 bool QDirModel::setData(const QModelIndex &index, const QVariant &value, int role) |
|
423 { |
|
424 Q_D(QDirModel); |
|
425 if (!d->indexValid(index) || index.column() != 0 |
|
426 || (flags(index) & Qt::ItemIsEditable) == 0 || role != Qt::EditRole) |
|
427 return false; |
|
428 |
|
429 QDirModelPrivate::QDirNode *node = d->node(index); |
|
430 QDir dir = node->info.dir(); |
|
431 QString name = value.toString(); |
|
432 if (dir.rename(node->info.fileName(), name)) { |
|
433 node->info = QFileInfo(dir, name); |
|
434 QModelIndex sibling = index.sibling(index.row(), 3); |
|
435 emit dataChanged(index, sibling); |
|
436 |
|
437 d->toBeRefreshed = index.parent(); |
|
438 QMetaObject::invokeMethod(this, "_q_refresh", Qt::QueuedConnection); |
|
439 |
|
440 return true; |
|
441 } |
|
442 |
|
443 return false; |
|
444 } |
|
445 |
|
446 /*! |
|
447 Returns the data stored under the given \a role for the specified \a section |
|
448 of the header with the given \a orientation. |
|
449 */ |
|
450 |
|
451 QVariant QDirModel::headerData(int section, Qt::Orientation orientation, int role) const |
|
452 { |
|
453 if (orientation == Qt::Horizontal) { |
|
454 if (role != Qt::DisplayRole) |
|
455 return QVariant(); |
|
456 switch (section) { |
|
457 case 0: return tr("Name"); |
|
458 case 1: return tr("Size"); |
|
459 case 2: return |
|
460 #ifdef Q_OS_MAC |
|
461 tr("Kind", "Match OS X Finder"); |
|
462 #else |
|
463 tr("Type", "All other platforms"); |
|
464 #endif |
|
465 // Windows - Type |
|
466 // OS X - Kind |
|
467 // Konqueror - File Type |
|
468 // Nautilus - Type |
|
469 case 3: return tr("Date Modified"); |
|
470 default: return QVariant(); |
|
471 } |
|
472 } |
|
473 return QAbstractItemModel::headerData(section, orientation, role); |
|
474 } |
|
475 |
|
476 /*! |
|
477 Returns true if the \a parent model item has children; otherwise |
|
478 returns false. |
|
479 */ |
|
480 |
|
481 bool QDirModel::hasChildren(const QModelIndex &parent) const |
|
482 { |
|
483 Q_D(const QDirModel); |
|
484 if (parent.column() > 0) |
|
485 return false; |
|
486 |
|
487 if (!parent.isValid()) // the invalid index is the "My Computer" item |
|
488 return true; // the drives |
|
489 QDirModelPrivate::QDirNode *p = d->node(parent); |
|
490 Q_ASSERT(p); |
|
491 |
|
492 if (d->lazyChildCount) // optimization that only checks for children if the node has been populated |
|
493 return p->info.isDir(); |
|
494 return p->info.isDir() && rowCount(parent) > 0; |
|
495 } |
|
496 |
|
497 /*! |
|
498 Returns the item flags for the given \a index in the model. |
|
499 |
|
500 \sa Qt::ItemFlags |
|
501 */ |
|
502 Qt::ItemFlags QDirModel::flags(const QModelIndex &index) const |
|
503 { |
|
504 Q_D(const QDirModel); |
|
505 Qt::ItemFlags flags = QAbstractItemModel::flags(index); |
|
506 if (!d->indexValid(index)) |
|
507 return flags; |
|
508 flags |= Qt::ItemIsDragEnabled; |
|
509 if (d->readOnly) |
|
510 return flags; |
|
511 QDirModelPrivate::QDirNode *node = d->node(index); |
|
512 if ((index.column() == 0) && node->info.isWritable()) { |
|
513 flags |= Qt::ItemIsEditable; |
|
514 if (fileInfo(index).isDir()) // is directory and is editable |
|
515 flags |= Qt::ItemIsDropEnabled; |
|
516 } |
|
517 return flags; |
|
518 } |
|
519 |
|
520 /*! |
|
521 Sort the model items in the \a column using the \a order given. |
|
522 The order is a value defined in \l Qt::SortOrder. |
|
523 */ |
|
524 |
|
525 void QDirModel::sort(int column, Qt::SortOrder order) |
|
526 { |
|
527 QDir::SortFlags sort = QDir::DirsFirst | QDir::IgnoreCase; |
|
528 if (order == Qt::DescendingOrder) |
|
529 sort |= QDir::Reversed; |
|
530 |
|
531 switch (column) { |
|
532 case 0: |
|
533 sort |= QDir::Name; |
|
534 break; |
|
535 case 1: |
|
536 sort |= QDir::Size; |
|
537 break; |
|
538 case 2: |
|
539 sort |= QDir::Type; |
|
540 break; |
|
541 case 3: |
|
542 sort |= QDir::Time; |
|
543 break; |
|
544 default: |
|
545 break; |
|
546 } |
|
547 |
|
548 setSorting(sort); |
|
549 } |
|
550 |
|
551 /*! |
|
552 Returns a list of MIME types that can be used to describe a list of items |
|
553 in the model. |
|
554 */ |
|
555 |
|
556 QStringList QDirModel::mimeTypes() const |
|
557 { |
|
558 return QStringList(QLatin1String("text/uri-list")); |
|
559 } |
|
560 |
|
561 /*! |
|
562 Returns an object that contains a serialized description of the specified |
|
563 \a indexes. The format used to describe the items corresponding to the |
|
564 indexes is obtained from the mimeTypes() function. |
|
565 |
|
566 If the list of indexes is empty, 0 is returned rather than a serialized |
|
567 empty list. |
|
568 */ |
|
569 |
|
570 QMimeData *QDirModel::mimeData(const QModelIndexList &indexes) const |
|
571 { |
|
572 QList<QUrl> urls; |
|
573 QList<QModelIndex>::const_iterator it = indexes.begin(); |
|
574 for (; it != indexes.end(); ++it) |
|
575 if ((*it).column() == 0) |
|
576 urls << QUrl::fromLocalFile(filePath(*it)); |
|
577 QMimeData *data = new QMimeData(); |
|
578 data->setUrls(urls); |
|
579 return data; |
|
580 } |
|
581 |
|
582 /*! |
|
583 Handles the \a data supplied by a drag and drop operation that ended with |
|
584 the given \a action over the row in the model specified by the \a row and |
|
585 \a column and by the \a parent index. |
|
586 |
|
587 \sa supportedDropActions() |
|
588 */ |
|
589 |
|
590 bool QDirModel::dropMimeData(const QMimeData *data, Qt::DropAction action, |
|
591 int /* row */, int /* column */, const QModelIndex &parent) |
|
592 { |
|
593 Q_D(QDirModel); |
|
594 if (!d->indexValid(parent) || isReadOnly()) |
|
595 return false; |
|
596 |
|
597 bool success = true; |
|
598 QString to = filePath(parent) + QDir::separator(); |
|
599 QModelIndex _parent = parent; |
|
600 |
|
601 QList<QUrl> urls = data->urls(); |
|
602 QList<QUrl>::const_iterator it = urls.constBegin(); |
|
603 |
|
604 switch (action) { |
|
605 case Qt::CopyAction: |
|
606 for (; it != urls.constEnd(); ++it) { |
|
607 QString path = (*it).toLocalFile(); |
|
608 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success; |
|
609 } |
|
610 break; |
|
611 case Qt::LinkAction: |
|
612 for (; it != urls.constEnd(); ++it) { |
|
613 QString path = (*it).toLocalFile(); |
|
614 success = QFile::link(path, to + QFileInfo(path).fileName()) && success; |
|
615 } |
|
616 break; |
|
617 case Qt::MoveAction: |
|
618 for (; it != urls.constEnd(); ++it) { |
|
619 QString path = (*it).toLocalFile(); |
|
620 if (QFile::copy(path, to + QFileInfo(path).fileName()) |
|
621 && QFile::remove(path)) { |
|
622 QModelIndex idx=index(QFileInfo(path).path()); |
|
623 if (idx.isValid()) { |
|
624 refresh(idx); |
|
625 //the previous call to refresh may invalidate the _parent. so recreate a new QModelIndex |
|
626 _parent = index(to); |
|
627 } |
|
628 } else { |
|
629 success = false; |
|
630 } |
|
631 } |
|
632 break; |
|
633 default: |
|
634 return false; |
|
635 } |
|
636 |
|
637 if (success) |
|
638 refresh(_parent); |
|
639 |
|
640 return success; |
|
641 } |
|
642 |
|
643 /*! |
|
644 Returns the drop actions supported by this model. |
|
645 |
|
646 \sa Qt::DropActions |
|
647 */ |
|
648 |
|
649 Qt::DropActions QDirModel::supportedDropActions() const |
|
650 { |
|
651 return Qt::CopyAction | Qt::MoveAction; // FIXME: LinkAction is not supported yet |
|
652 } |
|
653 |
|
654 /*! |
|
655 Sets the \a provider of file icons for the directory model. |
|
656 |
|
657 */ |
|
658 |
|
659 void QDirModel::setIconProvider(QFileIconProvider *provider) |
|
660 { |
|
661 Q_D(QDirModel); |
|
662 d->iconProvider = provider; |
|
663 } |
|
664 |
|
665 /*! |
|
666 Returns the file icon provider for this directory model. |
|
667 */ |
|
668 |
|
669 QFileIconProvider *QDirModel::iconProvider() const |
|
670 { |
|
671 Q_D(const QDirModel); |
|
672 return d->iconProvider; |
|
673 } |
|
674 |
|
675 /*! |
|
676 Sets the name \a filters for the directory model. |
|
677 */ |
|
678 |
|
679 void QDirModel::setNameFilters(const QStringList &filters) |
|
680 { |
|
681 Q_D(QDirModel); |
|
682 d->nameFilters = filters; |
|
683 emit layoutAboutToBeChanged(); |
|
684 if (d->shouldStat) |
|
685 refresh(QModelIndex()); |
|
686 else |
|
687 d->invalidate(); |
|
688 emit layoutChanged(); |
|
689 } |
|
690 |
|
691 /*! |
|
692 Returns a list of filters applied to the names in the model. |
|
693 */ |
|
694 |
|
695 QStringList QDirModel::nameFilters() const |
|
696 { |
|
697 Q_D(const QDirModel); |
|
698 return d->nameFilters; |
|
699 } |
|
700 |
|
701 /*! |
|
702 Sets the directory model's filter to that specified by \a filters. |
|
703 |
|
704 Note that the filter you set should always include the QDir::AllDirs enum value, |
|
705 otherwise QDirModel won't be able to read the directory structure. |
|
706 |
|
707 \sa QDir::Filters |
|
708 */ |
|
709 |
|
710 void QDirModel::setFilter(QDir::Filters filters) |
|
711 { |
|
712 Q_D(QDirModel); |
|
713 d->filters = filters; |
|
714 emit layoutAboutToBeChanged(); |
|
715 if (d->shouldStat) |
|
716 refresh(QModelIndex()); |
|
717 else |
|
718 d->invalidate(); |
|
719 emit layoutChanged(); |
|
720 } |
|
721 |
|
722 /*! |
|
723 Returns the filter specification for the directory model. |
|
724 |
|
725 \sa QDir::Filters |
|
726 */ |
|
727 |
|
728 QDir::Filters QDirModel::filter() const |
|
729 { |
|
730 Q_D(const QDirModel); |
|
731 return d->filters; |
|
732 } |
|
733 |
|
734 /*! |
|
735 Sets the directory model's sorting order to that specified by \a sort. |
|
736 |
|
737 \sa QDir::SortFlags |
|
738 */ |
|
739 |
|
740 void QDirModel::setSorting(QDir::SortFlags sort) |
|
741 { |
|
742 Q_D(QDirModel); |
|
743 d->sort = sort; |
|
744 emit layoutAboutToBeChanged(); |
|
745 if (d->shouldStat) |
|
746 refresh(QModelIndex()); |
|
747 else |
|
748 d->invalidate(); |
|
749 emit layoutChanged(); |
|
750 } |
|
751 |
|
752 /*! |
|
753 Returns the sorting method used for the directory model. |
|
754 |
|
755 \sa QDir::SortFlags */ |
|
756 |
|
757 QDir::SortFlags QDirModel::sorting() const |
|
758 { |
|
759 Q_D(const QDirModel); |
|
760 return d->sort; |
|
761 } |
|
762 |
|
763 /*! |
|
764 \property QDirModel::resolveSymlinks |
|
765 \brief Whether the directory model should resolve symbolic links |
|
766 |
|
767 This is only relevant on operating systems that support symbolic |
|
768 links. |
|
769 */ |
|
770 void QDirModel::setResolveSymlinks(bool enable) |
|
771 { |
|
772 Q_D(QDirModel); |
|
773 d->resolveSymlinks = enable; |
|
774 } |
|
775 |
|
776 bool QDirModel::resolveSymlinks() const |
|
777 { |
|
778 Q_D(const QDirModel); |
|
779 return d->resolveSymlinks; |
|
780 } |
|
781 |
|
782 /*! |
|
783 \property QDirModel::readOnly |
|
784 \brief Whether the directory model allows writing to the file system |
|
785 |
|
786 If this property is set to false, the directory model will allow renaming, copying |
|
787 and deleting of files and directories. |
|
788 |
|
789 This property is true by default |
|
790 */ |
|
791 |
|
792 void QDirModel::setReadOnly(bool enable) |
|
793 { |
|
794 Q_D(QDirModel); |
|
795 d->readOnly = enable; |
|
796 } |
|
797 |
|
798 bool QDirModel::isReadOnly() const |
|
799 { |
|
800 Q_D(const QDirModel); |
|
801 return d->readOnly; |
|
802 } |
|
803 |
|
804 /*! |
|
805 \property QDirModel::lazyChildCount |
|
806 \brief Whether the directory model optimizes the hasChildren function |
|
807 to only check if the item is a directory. |
|
808 |
|
809 If this property is set to false, the directory model will make sure that a directory |
|
810 actually containes any files before reporting that it has children. |
|
811 Otherwise the directory model will report that an item has children if the item |
|
812 is a directory. |
|
813 |
|
814 This property is false by default |
|
815 */ |
|
816 |
|
817 void QDirModel::setLazyChildCount(bool enable) |
|
818 { |
|
819 Q_D(QDirModel); |
|
820 d->lazyChildCount = enable; |
|
821 } |
|
822 |
|
823 bool QDirModel::lazyChildCount() const |
|
824 { |
|
825 Q_D(const QDirModel); |
|
826 return d->lazyChildCount; |
|
827 } |
|
828 |
|
829 /*! |
|
830 QDirModel caches file information. This function updates the |
|
831 cache. The \a parent parameter is the directory from which the |
|
832 model is updated; the default value will update the model from |
|
833 root directory of the file system (the entire model). |
|
834 */ |
|
835 |
|
836 void QDirModel::refresh(const QModelIndex &parent) |
|
837 { |
|
838 Q_D(QDirModel); |
|
839 |
|
840 QDirModelPrivate::QDirNode *n = d->indexValid(parent) ? d->node(parent) : &(d->root); |
|
841 |
|
842 int rows = n->children.count(); |
|
843 if (rows == 0) { |
|
844 emit layoutAboutToBeChanged(); |
|
845 n->stat = true; // make sure that next time we read all the info |
|
846 n->populated = false; |
|
847 emit layoutChanged(); |
|
848 return; |
|
849 } |
|
850 |
|
851 emit layoutAboutToBeChanged(); |
|
852 d->savePersistentIndexes(); |
|
853 d->rowsAboutToBeRemoved(parent, 0, rows - 1); |
|
854 n->stat = true; // make sure that next time we read all the info |
|
855 d->clear(n); |
|
856 d->rowsRemoved(parent, 0, rows - 1); |
|
857 d->restorePersistentIndexes(); |
|
858 emit layoutChanged(); |
|
859 } |
|
860 |
|
861 /*! |
|
862 \overload |
|
863 |
|
864 Returns the model item index for the given \a path. |
|
865 */ |
|
866 |
|
867 QModelIndex QDirModel::index(const QString &path, int column) const |
|
868 { |
|
869 Q_D(const QDirModel); |
|
870 |
|
871 if (path.isEmpty() || path == QCoreApplication::translate("QFileDialog", "My Computer")) |
|
872 return QModelIndex(); |
|
873 |
|
874 QString absolutePath = QDir(path).absolutePath(); |
|
875 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) |
|
876 absolutePath = absolutePath.toLower(); |
|
877 #endif |
|
878 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) |
|
879 // On Windows, "filename......." and "filename" are equivalent |
|
880 if (absolutePath.endsWith(QLatin1Char('.'))) { |
|
881 int i; |
|
882 for (i = absolutePath.count() - 1; i >= 0; --i) { |
|
883 if (absolutePath.at(i) != QLatin1Char('.')) |
|
884 break; |
|
885 } |
|
886 absolutePath = absolutePath.left(i+1); |
|
887 } |
|
888 #endif |
|
889 |
|
890 QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts); |
|
891 if ((pathElements.isEmpty() || !QFileInfo(path).exists()) |
|
892 #if !defined(Q_OS_WIN) || defined(Q_OS_WINCE) |
|
893 && path != QLatin1String("/") |
|
894 #endif |
|
895 ) |
|
896 return QModelIndex(); |
|
897 |
|
898 QModelIndex idx; // start with "My Computer" |
|
899 if (!d->root.populated) // make sure the root is populated |
|
900 d->populate(&d->root); |
|
901 |
|
902 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) |
|
903 if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path |
|
904 QString host = pathElements.first(); |
|
905 int r = 0; |
|
906 for (; r < d->root.children.count(); ++r) |
|
907 if (d->root.children.at(r).info.fileName() == host) |
|
908 break; |
|
909 bool childAppended = false; |
|
910 if (r >= d->root.children.count() && d->allowAppendChild) { |
|
911 d->appendChild(&d->root, QLatin1String("//") + host); |
|
912 childAppended = true; |
|
913 } |
|
914 idx = index(r, 0, QModelIndex()); |
|
915 pathElements.pop_front(); |
|
916 if (childAppended) |
|
917 emit const_cast<QDirModel*>(this)->layoutChanged(); |
|
918 } else |
|
919 #endif |
|
920 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) |
|
921 if (pathElements.at(0).endsWith(QLatin1Char(':'))) { |
|
922 pathElements[0] += QLatin1Char('/'); |
|
923 } |
|
924 #else |
|
925 // add the "/" item, since it is a valid path element on unix |
|
926 pathElements.prepend(QLatin1String("/")); |
|
927 #endif |
|
928 |
|
929 for (int i = 0; i < pathElements.count(); ++i) { |
|
930 Q_ASSERT(!pathElements.at(i).isEmpty()); |
|
931 QString element = pathElements.at(i); |
|
932 QDirModelPrivate::QDirNode *parent = (idx.isValid() ? d->node(idx) : &d->root); |
|
933 |
|
934 Q_ASSERT(parent); |
|
935 if (!parent->populated) |
|
936 d->populate(parent); |
|
937 |
|
938 // search for the element in the child nodes first |
|
939 int row = -1; |
|
940 for (int j = parent->children.count() - 1; j >= 0; --j) { |
|
941 const QFileInfo& fi = parent->children.at(j).info; |
|
942 QString childFileName; |
|
943 childFileName = idx.isValid() ? fi.fileName() : fi.absoluteFilePath(); |
|
944 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) |
|
945 childFileName = childFileName.toLower(); |
|
946 #endif |
|
947 if (childFileName == element) { |
|
948 if (i == pathElements.count() - 1) |
|
949 parent->children[j].stat = true; |
|
950 row = j; |
|
951 break; |
|
952 } |
|
953 } |
|
954 |
|
955 // we couldn't find the path element, we create a new node since we _know_ that the path is valid |
|
956 if (row == -1) { |
|
957 #if defined(Q_OS_WINCE) |
|
958 QString newPath; |
|
959 if (parent->info.isRoot()) |
|
960 newPath = parent->info.absoluteFilePath() + element; |
|
961 else |
|
962 newPath = parent->info.absoluteFilePath() + QLatin1Char('/') + element; |
|
963 #else |
|
964 QString newPath = parent->info.absoluteFilePath() + QLatin1Char('/') + element; |
|
965 #endif |
|
966 if (!d->allowAppendChild || !QFileInfo(newPath).isDir()) |
|
967 return QModelIndex(); |
|
968 d->appendChild(parent, newPath); |
|
969 row = parent->children.count() - 1; |
|
970 if (i == pathElements.count() - 1) // always stat children of the last element |
|
971 parent->children[row].stat = true; |
|
972 emit const_cast<QDirModel*>(this)->layoutChanged(); |
|
973 } |
|
974 |
|
975 Q_ASSERT(row >= 0); |
|
976 idx = createIndex(row, 0, static_cast<void*>(&parent->children[row])); |
|
977 Q_ASSERT(idx.isValid()); |
|
978 } |
|
979 |
|
980 if (column != 0) |
|
981 return idx.sibling(idx.row(), column); |
|
982 return idx; |
|
983 } |
|
984 |
|
985 /*! |
|
986 Returns true if the model item \a index represents a directory; |
|
987 otherwise returns false. |
|
988 */ |
|
989 |
|
990 bool QDirModel::isDir(const QModelIndex &index) const |
|
991 { |
|
992 Q_D(const QDirModel); |
|
993 Q_ASSERT(d->indexValid(index)); |
|
994 QDirModelPrivate::QDirNode *node = d->node(index); |
|
995 return node->info.isDir(); |
|
996 } |
|
997 |
|
998 /*! |
|
999 Create a directory with the \a name in the \a parent model item. |
|
1000 */ |
|
1001 |
|
1002 QModelIndex QDirModel::mkdir(const QModelIndex &parent, const QString &name) |
|
1003 { |
|
1004 Q_D(QDirModel); |
|
1005 if (!d->indexValid(parent) || isReadOnly()) |
|
1006 return QModelIndex(); |
|
1007 |
|
1008 QDirModelPrivate::QDirNode *p = d->node(parent); |
|
1009 QString path = p->info.absoluteFilePath(); |
|
1010 // For the indexOf() method to work, the new directory has to be a direct child of |
|
1011 // the parent directory. |
|
1012 |
|
1013 QDir newDir(name); |
|
1014 QDir dir(path); |
|
1015 if (newDir.isRelative()) |
|
1016 newDir = QDir(path + QLatin1Char('/') + name); |
|
1017 QString childName = newDir.dirName(); // Get the singular name of the directory |
|
1018 newDir.cdUp(); |
|
1019 |
|
1020 if (newDir.absolutePath() != dir.absolutePath() || !dir.mkdir(name)) |
|
1021 return QModelIndex(); // nothing happened |
|
1022 |
|
1023 refresh(parent); |
|
1024 |
|
1025 QStringList entryList = d->entryList(path); |
|
1026 int r = entryList.indexOf(childName); |
|
1027 QModelIndex i = index(r, 0, parent); // return an invalid index |
|
1028 |
|
1029 return i; |
|
1030 } |
|
1031 |
|
1032 /*! |
|
1033 Removes the directory corresponding to the model item \a index in the |
|
1034 directory model and \bold{deletes the corresponding directory from the |
|
1035 file system}, returning true if successful. If the directory cannot be |
|
1036 removed, false is returned. |
|
1037 |
|
1038 \warning This function deletes directories from the file system; it does |
|
1039 \bold{not} move them to a location where they can be recovered. |
|
1040 |
|
1041 \sa remove() |
|
1042 */ |
|
1043 |
|
1044 bool QDirModel::rmdir(const QModelIndex &index) |
|
1045 { |
|
1046 Q_D(QDirModel); |
|
1047 if (!d->indexValid(index) || isReadOnly()) |
|
1048 return false; |
|
1049 |
|
1050 QDirModelPrivate::QDirNode *n = d_func()->node(index); |
|
1051 if (!n->info.isDir()) { |
|
1052 qWarning("rmdir: the node is not a directory"); |
|
1053 return false; |
|
1054 } |
|
1055 |
|
1056 QModelIndex par = parent(index); |
|
1057 QDirModelPrivate::QDirNode *p = d_func()->node(par); |
|
1058 QDir dir = p->info.dir(); // parent dir |
|
1059 QString path = n->info.absoluteFilePath(); |
|
1060 if (!dir.rmdir(path)) |
|
1061 return false; |
|
1062 |
|
1063 refresh(par); |
|
1064 |
|
1065 return true; |
|
1066 } |
|
1067 |
|
1068 /*! |
|
1069 Removes the model item \a index from the directory model and \bold{deletes the |
|
1070 corresponding file from the file system}, returning true if successful. If the |
|
1071 item cannot be removed, false is returned. |
|
1072 |
|
1073 \warning This function deletes files from the file system; it does \bold{not} |
|
1074 move them to a location where they can be recovered. |
|
1075 |
|
1076 \sa rmdir() |
|
1077 */ |
|
1078 |
|
1079 bool QDirModel::remove(const QModelIndex &index) |
|
1080 { |
|
1081 Q_D(QDirModel); |
|
1082 if (!d->indexValid(index) || isReadOnly()) |
|
1083 return false; |
|
1084 |
|
1085 QDirModelPrivate::QDirNode *n = d_func()->node(index); |
|
1086 if (n->info.isDir()) |
|
1087 return false; |
|
1088 |
|
1089 QModelIndex par = parent(index); |
|
1090 QDirModelPrivate::QDirNode *p = d_func()->node(par); |
|
1091 QDir dir = p->info.dir(); // parent dir |
|
1092 QString path = n->info.absoluteFilePath(); |
|
1093 if (!dir.remove(path)) |
|
1094 return false; |
|
1095 |
|
1096 refresh(par); |
|
1097 |
|
1098 return true; |
|
1099 } |
|
1100 |
|
1101 /*! |
|
1102 Returns the path of the item stored in the model under the |
|
1103 \a index given. |
|
1104 |
|
1105 */ |
|
1106 |
|
1107 QString QDirModel::filePath(const QModelIndex &index) const |
|
1108 { |
|
1109 Q_D(const QDirModel); |
|
1110 if (d->indexValid(index)) { |
|
1111 QFileInfo fi = fileInfo(index); |
|
1112 if (d->resolveSymlinks && fi.isSymLink()) |
|
1113 fi = d->resolvedInfo(fi); |
|
1114 return QDir::cleanPath(fi.absoluteFilePath()); |
|
1115 } |
|
1116 return QString(); // root path |
|
1117 } |
|
1118 |
|
1119 /*! |
|
1120 Returns the name of the item stored in the model under the |
|
1121 \a index given. |
|
1122 |
|
1123 */ |
|
1124 |
|
1125 QString QDirModel::fileName(const QModelIndex &index) const |
|
1126 { |
|
1127 Q_D(const QDirModel); |
|
1128 if (!d->indexValid(index)) |
|
1129 return QString(); |
|
1130 QFileInfo info = fileInfo(index); |
|
1131 if (info.isRoot()) |
|
1132 return info.absoluteFilePath(); |
|
1133 if (d->resolveSymlinks && info.isSymLink()) |
|
1134 info = d->resolvedInfo(info); |
|
1135 return info.fileName(); |
|
1136 } |
|
1137 |
|
1138 /*! |
|
1139 Returns the icons for the item stored in the model under the given |
|
1140 \a index. |
|
1141 */ |
|
1142 |
|
1143 QIcon QDirModel::fileIcon(const QModelIndex &index) const |
|
1144 { |
|
1145 Q_D(const QDirModel); |
|
1146 if (!d->indexValid(index)) |
|
1147 return d->iconProvider->icon(QFileIconProvider::Computer); |
|
1148 QDirModelPrivate::QDirNode *node = d->node(index); |
|
1149 if (node->icon.isNull()) |
|
1150 node->icon = d->iconProvider->icon(node->info); |
|
1151 return node->icon; |
|
1152 } |
|
1153 |
|
1154 /*! |
|
1155 Returns the file information for the specified model \a index. |
|
1156 |
|
1157 \bold{Note:} If the model index represents a symbolic link in the |
|
1158 underlying filing system, the file information returned will contain |
|
1159 information about the symbolic link itself, regardless of whether |
|
1160 resolveSymlinks is enabled or not. |
|
1161 |
|
1162 \sa QFileInfo::symLinkTarget() |
|
1163 */ |
|
1164 |
|
1165 QFileInfo QDirModel::fileInfo(const QModelIndex &index) const |
|
1166 { |
|
1167 Q_D(const QDirModel); |
|
1168 Q_ASSERT(d->indexValid(index)); |
|
1169 |
|
1170 QDirModelPrivate::QDirNode *node = d->node(index); |
|
1171 return node->info; |
|
1172 } |
|
1173 |
|
1174 /*! |
|
1175 \fn QObject *QDirModel::parent() const |
|
1176 \internal |
|
1177 */ |
|
1178 |
|
1179 /* |
|
1180 The root node is never seen outside the model. |
|
1181 */ |
|
1182 |
|
1183 void QDirModelPrivate::init() |
|
1184 { |
|
1185 filters = QDir::AllEntries | QDir::NoDotAndDotDot; |
|
1186 sort = QDir::Name; |
|
1187 nameFilters << QLatin1String("*"); |
|
1188 root.parent = 0; |
|
1189 root.info = QFileInfo(); |
|
1190 clear(&root); |
|
1191 } |
|
1192 |
|
1193 QDirModelPrivate::QDirNode *QDirModelPrivate::node(int row, QDirNode *parent) const |
|
1194 { |
|
1195 if (row < 0) |
|
1196 return 0; |
|
1197 |
|
1198 bool isDir = !parent || parent->info.isDir(); |
|
1199 QDirNode *p = (parent ? parent : &root); |
|
1200 if (isDir && !p->populated) |
|
1201 populate(p); // will also resolve symlinks |
|
1202 |
|
1203 if (row >= p->children.count()) { |
|
1204 qWarning("node: the row does not exist"); |
|
1205 return 0; |
|
1206 } |
|
1207 |
|
1208 return const_cast<QDirNode*>(&p->children.at(row)); |
|
1209 } |
|
1210 |
|
1211 QVector<QDirModelPrivate::QDirNode> QDirModelPrivate::children(QDirNode *parent, bool stat) const |
|
1212 { |
|
1213 Q_ASSERT(parent); |
|
1214 QFileInfoList infoList; |
|
1215 if (parent == &root) { |
|
1216 parent = 0; |
|
1217 infoList = QDir::drives(); |
|
1218 } else if (parent->info.isDir()) { |
|
1219 //resolve directory links only if requested. |
|
1220 if (parent->info.isSymLink() && resolveSymlinks) { |
|
1221 QString link = parent->info.symLinkTarget(); |
|
1222 if (link.size() > 1 && link.at(link.size() - 1) == QDir::separator()) |
|
1223 link.chop(1); |
|
1224 if (stat) |
|
1225 infoList = entryInfoList(link); |
|
1226 else |
|
1227 infoList = QDir(link).entryInfoList(nameFilters, QDir::AllEntries | QDir::System); |
|
1228 } else { |
|
1229 if (stat) |
|
1230 infoList = entryInfoList(parent->info.absoluteFilePath()); |
|
1231 else |
|
1232 infoList = QDir(parent->info.absoluteFilePath()).entryInfoList(nameFilters, QDir::AllEntries | QDir::System); |
|
1233 } |
|
1234 } |
|
1235 |
|
1236 QVector<QDirNode> nodes(infoList.count()); |
|
1237 for (int i = 0; i < infoList.count(); ++i) { |
|
1238 QDirNode &node = nodes[i]; |
|
1239 node.parent = parent; |
|
1240 node.info = infoList.at(i); |
|
1241 node.populated = false; |
|
1242 node.stat = shouldStat; |
|
1243 } |
|
1244 |
|
1245 return nodes; |
|
1246 } |
|
1247 |
|
1248 void QDirModelPrivate::_q_refresh() |
|
1249 { |
|
1250 Q_Q(QDirModel); |
|
1251 q->refresh(toBeRefreshed); |
|
1252 toBeRefreshed = QModelIndex(); |
|
1253 } |
|
1254 |
|
1255 void QDirModelPrivate::savePersistentIndexes() |
|
1256 { |
|
1257 Q_Q(QDirModel); |
|
1258 savedPersistent.clear(); |
|
1259 foreach (QPersistentModelIndexData *data, persistent.indexes) { |
|
1260 SavedPersistent saved; |
|
1261 QModelIndex index = data->index; |
|
1262 saved.path = q->filePath(index); |
|
1263 saved.column = index.column(); |
|
1264 saved.data = data; |
|
1265 saved.index = index; |
|
1266 savedPersistent.append(saved); |
|
1267 } |
|
1268 } |
|
1269 |
|
1270 void QDirModelPrivate::restorePersistentIndexes() |
|
1271 { |
|
1272 Q_Q(QDirModel); |
|
1273 bool allow = allowAppendChild; |
|
1274 allowAppendChild = false; |
|
1275 for (int i = 0; i < savedPersistent.count(); ++i) { |
|
1276 QPersistentModelIndexData *data = savedPersistent.at(i).data; |
|
1277 QString path = savedPersistent.at(i).path; |
|
1278 int column = savedPersistent.at(i).column; |
|
1279 QModelIndex idx = q->index(path, column); |
|
1280 if (idx != data->index || data->model == 0) { |
|
1281 //data->model may be equal to 0 if the model is getting destroyed |
|
1282 persistent.indexes.remove(data->index); |
|
1283 data->index = idx; |
|
1284 data->model = q; |
|
1285 if (idx.isValid()) |
|
1286 persistent.indexes.insert(idx, data); |
|
1287 } |
|
1288 } |
|
1289 savedPersistent.clear(); |
|
1290 allowAppendChild = allow; |
|
1291 } |
|
1292 |
|
1293 QFileInfoList QDirModelPrivate::entryInfoList(const QString &path) const |
|
1294 { |
|
1295 const QDir dir(path); |
|
1296 return dir.entryInfoList(nameFilters, filters, sort); |
|
1297 } |
|
1298 |
|
1299 QStringList QDirModelPrivate::entryList(const QString &path) const |
|
1300 { |
|
1301 const QDir dir(path); |
|
1302 return dir.entryList(nameFilters, filters, sort); |
|
1303 } |
|
1304 |
|
1305 QString QDirModelPrivate::name(const QModelIndex &index) const |
|
1306 { |
|
1307 const QDirNode *n = node(index); |
|
1308 const QFileInfo info = n->info; |
|
1309 if (info.isRoot()) { |
|
1310 QString name = info.absoluteFilePath(); |
|
1311 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) |
|
1312 if (name.startsWith(QLatin1Char('/'))) // UNC host |
|
1313 return info.fileName(); |
|
1314 #endif |
|
1315 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) |
|
1316 if (name.endsWith(QLatin1Char('/'))) |
|
1317 name.chop(1); |
|
1318 #endif |
|
1319 return name; |
|
1320 } |
|
1321 return info.fileName(); |
|
1322 } |
|
1323 |
|
1324 QString QDirModelPrivate::size(const QModelIndex &index) const |
|
1325 { |
|
1326 const QDirNode *n = node(index); |
|
1327 if (n->info.isDir()) { |
|
1328 #ifdef Q_OS_MAC |
|
1329 return QLatin1String("--"); |
|
1330 #else |
|
1331 return QLatin1String(""); |
|
1332 #endif |
|
1333 // Windows - "" |
|
1334 // OS X - "--" |
|
1335 // Konqueror - "4 KB" |
|
1336 // Nautilus - "9 items" (the number of children) |
|
1337 } |
|
1338 |
|
1339 // According to the Si standard KB is 1000 bytes, KiB is 1024 |
|
1340 // but on windows sizes are calulated by dividing by 1024 so we do what they do. |
|
1341 const quint64 kb = 1024; |
|
1342 const quint64 mb = 1024 * kb; |
|
1343 const quint64 gb = 1024 * mb; |
|
1344 const quint64 tb = 1024 * gb; |
|
1345 quint64 bytes = n->info.size(); |
|
1346 if (bytes >= tb) |
|
1347 return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3)); |
|
1348 if (bytes >= gb) |
|
1349 return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2)); |
|
1350 if (bytes >= mb) |
|
1351 return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1)); |
|
1352 if (bytes >= kb) |
|
1353 return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb)); |
|
1354 return QFileSystemModel::tr("%1 byte(s)").arg(QLocale().toString(bytes)); |
|
1355 } |
|
1356 |
|
1357 QString QDirModelPrivate::type(const QModelIndex &index) const |
|
1358 { |
|
1359 return iconProvider->type(node(index)->info); |
|
1360 } |
|
1361 |
|
1362 QString QDirModelPrivate::time(const QModelIndex &index) const |
|
1363 { |
|
1364 #ifndef QT_NO_DATESTRING |
|
1365 return node(index)->info.lastModified().toString(Qt::LocalDate); |
|
1366 #else |
|
1367 Q_UNUSED(index); |
|
1368 return QString(); |
|
1369 #endif |
|
1370 } |
|
1371 |
|
1372 void QDirModelPrivate::appendChild(QDirModelPrivate::QDirNode *parent, const QString &path) const |
|
1373 { |
|
1374 QDirModelPrivate::QDirNode node; |
|
1375 node.populated = false; |
|
1376 node.stat = shouldStat; |
|
1377 node.parent = (parent == &root ? 0 : parent); |
|
1378 node.info = QFileInfo(path); |
|
1379 node.info.setCaching(true); |
|
1380 |
|
1381 // The following append(node) may reallocate the vector, thus |
|
1382 // we need to update the pointers to the childnodes parent. |
|
1383 QDirModelPrivate *that = const_cast<QDirModelPrivate *>(this); |
|
1384 that->savePersistentIndexes(); |
|
1385 parent->children.append(node); |
|
1386 for (int i = 0; i < parent->children.count(); ++i) { |
|
1387 QDirNode *childNode = &parent->children[i]; |
|
1388 for (int j = 0; j < childNode->children.count(); ++j) |
|
1389 childNode->children[j].parent = childNode; |
|
1390 } |
|
1391 that->restorePersistentIndexes(); |
|
1392 } |
|
1393 |
|
1394 QFileInfo QDirModelPrivate::resolvedInfo(QFileInfo info) |
|
1395 { |
|
1396 #ifdef Q_OS_WIN |
|
1397 // On windows, we cannot create a shortcut to a shortcut. |
|
1398 return QFileInfo(info.symLinkTarget()); |
|
1399 #else |
|
1400 QStringList paths; |
|
1401 do { |
|
1402 QFileInfo link(info.symLinkTarget()); |
|
1403 if (link.isRelative()) |
|
1404 info.setFile(info.absolutePath(), link.filePath()); |
|
1405 else |
|
1406 info = link; |
|
1407 if (paths.contains(info.absoluteFilePath())) |
|
1408 return QFileInfo(); |
|
1409 paths.append(info.absoluteFilePath()); |
|
1410 } while (info.isSymLink()); |
|
1411 return info; |
|
1412 #endif |
|
1413 } |
|
1414 |
|
1415 QT_END_NAMESPACE |
|
1416 |
|
1417 #include "moc_qdirmodel.cpp" |
|
1418 |
|
1419 #endif // QT_NO_DIRMODEL |