]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kfileitemmodel.h
Merge branch 'davidedmundson/highdpi'
[dolphin.git] / src / kitemviews / kfileitemmodel.h
1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20 #ifndef KFILEITEMMODEL_H
21 #define KFILEITEMMODEL_H
22
23 #include "dolphin_export.h"
24 #include <KFileItem>
25 #include <QUrl>
26 #include <kitemviews/kitemmodelbase.h>
27 #include <kitemviews/private/kfileitemmodelfilter.h>
28
29 #include <QCollator>
30 #include <QHash>
31 #include <QSet>
32
33 class KFileItemModelDirLister;
34 class QTimer;
35
36 /**
37 * @brief KItemModelBase implementation for KFileItems.
38 *
39 * Allows to load items of a directory. Sorting and grouping of
40 * items are supported. Roles that are not part of KFileItem can
41 * be added with KFileItemModel::setData().
42 *
43 * Recursive expansion of sub-directories is supported by
44 * KFileItemModel::setExpanded().
45 */
46 class DOLPHIN_EXPORT KFileItemModel : public KItemModelBase
47 {
48 Q_OBJECT
49
50 public:
51 explicit KFileItemModel(QObject* parent = 0);
52 virtual ~KFileItemModel();
53
54 /**
55 * Loads the directory specified by \a url. The signals
56 * directoryLoadingStarted(), directoryLoadingProgress() and directoryLoadingCompleted()
57 * indicate the current state of the loading process. The items
58 * of the directory are added after the loading has been completed.
59 */
60 void loadDirectory(const QUrl& url);
61
62 /**
63 * Throws away all currently loaded items and refreshes the directory
64 * by reloading all items again.
65 */
66 void refreshDirectory(const QUrl& url);
67
68 /**
69 * @return Parent directory of the items that are shown. In case
70 * if a directory tree is shown, KFileItemModel::dir() returns
71 * the root-parent of all items.
72 * @see rootItem()
73 */
74 QUrl directory() const;
75
76 /**
77 * Cancels the loading of a directory which has been started by either
78 * loadDirectory() or refreshDirectory().
79 */
80 void cancelDirectoryLoading();
81
82 virtual int count() const Q_DECL_OVERRIDE;
83 virtual QHash<QByteArray, QVariant> data(int index) const Q_DECL_OVERRIDE;
84 virtual bool setData(int index, const QHash<QByteArray, QVariant>& values) Q_DECL_OVERRIDE;
85
86 /**
87 * Sets a separate sorting with directories first (true) or a mixed
88 * sorting of files and directories (false).
89 */
90 void setSortDirectoriesFirst(bool dirsFirst);
91 bool sortDirectoriesFirst() const;
92
93 void setShowHiddenFiles(bool show);
94 bool showHiddenFiles() const;
95
96 /**
97 * If set to true, only directories are shown as items of the model. Files
98 * are ignored.
99 */
100 void setShowDirectoriesOnly(bool enabled);
101 bool showDirectoriesOnly() const;
102
103 virtual QMimeData* createMimeData(const KItemSet& indexes) const Q_DECL_OVERRIDE;
104
105 virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const Q_DECL_OVERRIDE;
106
107 virtual bool supportsDropping(int index) const Q_DECL_OVERRIDE;
108
109 virtual QString roleDescription(const QByteArray& role) const Q_DECL_OVERRIDE;
110
111 virtual QList<QPair<int, QVariant> > groups() const Q_DECL_OVERRIDE;
112
113 /**
114 * @return The file-item for the index \a index. If the index is in a valid
115 * range it is assured that the file-item is not null. The runtime
116 * complexity of this call is O(1).
117 */
118 KFileItem fileItem(int index) const;
119
120 /**
121 * @return The file-item for the url \a url. If no file-item with the given
122 * URL is found KFileItem::isNull() will be true for the returned
123 * file-item. The runtime complexity of this call is O(1).
124 */
125 KFileItem fileItem(const QUrl& url) const;
126
127 /**
128 * @return The index for the file-item \a item. -1 is returned if no file-item
129 * is found or if the file-item is null. The amortized runtime
130 * complexity of this call is O(1).
131 */
132 int index(const KFileItem& item) const;
133
134 /**
135 * @return The index for the URL \a url. -1 is returned if no file-item
136 * is found. The amortized runtime complexity of this call is O(1).
137 */
138 int index(const QUrl &url) const;
139
140 /**
141 * @return Root item of all items representing the item
142 * for KFileItemModel::dir().
143 */
144 KFileItem rootItem() const;
145
146 /**
147 * Clears all items of the model.
148 */
149 void clear();
150
151 /**
152 * Sets the roles that should be shown for each item.
153 */
154 void setRoles(const QSet<QByteArray>& roles);
155 QSet<QByteArray> roles() const;
156
157 virtual bool setExpanded(int index, bool expanded) Q_DECL_OVERRIDE;
158 virtual bool isExpanded(int index) const Q_DECL_OVERRIDE;
159 virtual bool isExpandable(int index) const Q_DECL_OVERRIDE;
160 virtual int expandedParentsCount(int index) const Q_DECL_OVERRIDE;
161
162 QSet<QUrl> expandedDirectories() const;
163
164 /**
165 * Marks the URLs in \a urls as sub-directories which were expanded previously.
166 * After calling loadDirectory() or refreshDirectory() the marked sub-directories
167 * will be expanded step-by-step.
168 */
169 void restoreExpandedDirectories(const QSet<QUrl>& urls);
170
171 /**
172 * Expands all parent-directories of the item \a url.
173 */
174 void expandParentDirectories(const QUrl& url);
175
176 void setNameFilter(const QString& nameFilter);
177 QString nameFilter() const;
178
179 void setMimeTypeFilters(const QStringList& filters);
180 QStringList mimeTypeFilters() const;
181
182 struct RoleInfo
183 { QByteArray role;
184 QString translation;
185 QString group;
186 bool requiresBaloo;
187 bool requiresIndexer;
188 };
189
190 /**
191 * @return Provides static information for all available roles that
192 * are supported by KFileItemModel. Some roles can only be
193 * determined if Baloo is enabled and/or the Baloo
194 * indexing is enabled.
195 */
196 static QList<RoleInfo> rolesInformation();
197
198 signals:
199 /**
200 * Is emitted if the loading of a directory has been started. It is
201 * assured that a signal directoryLoadingCompleted() will be send after
202 * the loading has been finished. For tracking the loading progress
203 * the signal directoryLoadingProgress() gets emitted in between.
204 */
205 void directoryLoadingStarted();
206
207 /**
208 * Is emitted after the loading of a directory has been completed or new
209 * items have been inserted to an already loaded directory. Usually
210 * one or more itemsInserted() signals are emitted before loadingCompleted()
211 * (the only exception is loading an empty directory, where only a
212 * loadingCompleted() signal gets emitted).
213 */
214 void directoryLoadingCompleted();
215
216 /**
217 * Is emitted after the loading of a directory has been canceled.
218 */
219 void directoryLoadingCanceled();
220
221 /**
222 * Informs about the progress in percent when loading a directory. It is assured
223 * that the signal directoryLoadingStarted() has been emitted before.
224 */
225 void directoryLoadingProgress(int percent);
226
227 /**
228 * Is emitted if the sort-role gets resolved asynchronously and provides
229 * the progress-information of the sorting in percent. It is assured
230 * that the last sortProgress-signal contains 100 as value.
231 */
232 void directorySortingProgress(int percent);
233
234 /**
235 * Is emitted if an information message (e.g. "Connecting to host...")
236 * should be shown.
237 */
238 void infoMessage(const QString& message);
239
240 /**
241 * Is emitted if an error message (e.g. "Unknown location")
242 * should be shown.
243 */
244 void errorMessage(const QString& message);
245
246 /**
247 * Is emitted if a redirection from the current URL \a oldUrl
248 * to the new URL \a newUrl has been done.
249 */
250 void directoryRedirection(const QUrl& oldUrl, const QUrl& newUrl);
251
252 /**
253 * Is emitted when the URL passed by KFileItemModel::setUrl() represents a file.
254 * In this case no signal errorMessage() will be emitted.
255 */
256 void urlIsFileError(const QUrl& url);
257
258 protected:
259 virtual void onGroupedSortingChanged(bool current) Q_DECL_OVERRIDE;
260 virtual void onSortRoleChanged(const QByteArray& current, const QByteArray& previous) Q_DECL_OVERRIDE;
261 virtual void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) Q_DECL_OVERRIDE;
262
263 private slots:
264 /**
265 * Resorts all items dependent on the set sortRole(), sortOrder()
266 * and foldersFirst() settings.
267 */
268 void resortAllItems();
269
270 void slotCompleted();
271 void slotCanceled();
272 void slotItemsAdded(const QUrl& directoryUrl, const KFileItemList& items);
273 void slotItemsDeleted(const KFileItemList& items);
274 void slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items);
275 void slotClear();
276 void slotNaturalSortingChanged();
277
278 void dispatchPendingItemsToInsert();
279
280 private:
281 enum RoleType {
282 // User visible roles:
283 NoRole, NameRole, SizeRole, DateRole, PermissionsRole, OwnerRole,
284 GroupRole, TypeRole, DestinationRole, PathRole,
285 // User visible roles available with Baloo:
286 CommentRole, TagsRole, RatingRole, ImageSizeRole, OrientationRole,
287 WordCountRole, LineCountRole, ArtistRole, AlbumRole, DurationRole, TrackRole,
288 CopiedFromRole,
289 // Non-visible roles:
290 IsDirRole, IsLinkRole, IsExpandedRole, IsExpandableRole, ExpandedParentsCountRole,
291 // Mandatory last entry:
292 RolesCount
293 };
294
295 struct ItemData
296 {
297 KFileItem item;
298 QHash<QByteArray, QVariant> values;
299 ItemData* parent;
300 };
301
302 enum RemoveItemsBehavior {
303 KeepItemData,
304 DeleteItemData
305 };
306
307 void insertItems(QList<ItemData*>& items);
308 void removeItems(const KItemRangeList& itemRanges, RemoveItemsBehavior behavior);
309
310 /**
311 * Helper method for insertItems() and removeItems(): Creates
312 * a list of ItemData elements based on the given items.
313 * Note that the ItemData instances are created dynamically and
314 * must be deleted by the caller.
315 */
316 QList<ItemData*> createItemDataList(const QUrl& parentUrl, const KFileItemList& items) const;
317
318 /**
319 * Prepares the items for sorting. Normally, the hash 'values' in ItemData is filled
320 * lazily to save time and memory, but for some sort roles, it is expected that the
321 * sort role data is stored in 'values'.
322 */
323 void prepareItemsForSorting(QList<ItemData*>& itemDataList);
324
325 static int expandedParentsCount(const ItemData* data);
326
327 void removeExpandedItems();
328
329 /**
330 * This function is called by setData() and slotRefreshItems(). It emits
331 * the itemsChanged() signal, checks if the sort order is still correct,
332 * and starts m_resortAllItemsTimer if that is not the case.
333 */
334 void emitItemsChangedAndTriggerResorting(const KItemRangeList& itemRanges, const QSet<QByteArray>& changedRoles);
335
336 /**
337 * Resets all values from m_requestRole to false.
338 */
339 void resetRoles();
340
341 /**
342 * @return Role-type for the given role.
343 * Runtime complexity is O(1).
344 */
345 RoleType typeForRole(const QByteArray& role) const;
346
347 /**
348 * @return Role-byte-array for the given role-type.
349 * Runtime complexity is O(1).
350 */
351 QByteArray roleForType(RoleType roleType) const;
352
353 QHash<QByteArray, QVariant> retrieveData(const KFileItem& item, const ItemData* parent) const;
354
355 /**
356 * @return True if \a a has a KFileItem whose text is 'less than' the one
357 * of \a b according to QString::operator<(const QString&).
358 */
359 static bool nameLessThan(const ItemData* a, const ItemData* b);
360
361 /**
362 * @return True if the item-data \a a should be ordered before the item-data
363 * \b. The item-data may have different parent-items.
364 */
365 bool lessThan(const ItemData* a, const ItemData* b, const QCollator& collator) const;
366
367 /**
368 * Sorts the items between \a begin and \a end using the comparison
369 * function lessThan().
370 */
371 void sort(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end) const;
372
373 /**
374 * Helper method for lessThan() and expandedParentsCountCompare(): Compares
375 * the passed item-data using m_sortRole as criteria. Both items must
376 * have the same parent item, otherwise the comparison will be wrong.
377 */
378 int sortRoleCompare(const ItemData* a, const ItemData* b, const QCollator& collator) const;
379
380 int stringCompare(const QString& a, const QString& b, const QCollator& collator) const;
381
382 bool useMaximumUpdateInterval() const;
383
384 QList<QPair<int, QVariant> > nameRoleGroups() const;
385 QList<QPair<int, QVariant> > sizeRoleGroups() const;
386 QList<QPair<int, QVariant> > dateRoleGroups() const;
387 QList<QPair<int, QVariant> > permissionRoleGroups() const;
388 QList<QPair<int, QVariant> > ratingRoleGroups() const;
389 QList<QPair<int, QVariant> > genericStringRoleGroups(const QByteArray& typeForRole) const;
390
391 /**
392 * Helper method for all xxxRoleGroups() methods to check whether the
393 * item with the given index is a child-item. A child-item is defined
394 * as item having an expansion-level > 0. All xxxRoleGroups() methods
395 * should skip the grouping if the item is a child-item (although
396 * KItemListView would be capable to show sub-groups in groups this
397 * results in visual clutter for most usecases).
398 */
399 bool isChildItem(int index) const;
400
401 /**
402 * Is invoked by KFileItemModelRolesUpdater and results in emitting the
403 * sortProgress signal with a percent-value of the progress.
404 */
405 void emitSortProgress(int resolvedCount);
406
407 /**
408 * Applies the filters set through @ref setNameFilter and @ref setMimeTypeFilters.
409 */
410 void applyFilters();
411
412 /**
413 * Removes filtered items whose expanded parents have been deleted
414 * or collapsed via setExpanded(parentIndex, false).
415 */
416 void removeFilteredChildren(const KItemRangeList& parents);
417
418 /**
419 * Maps the QByteArray-roles to RoleTypes and provides translation- and
420 * group-contexts.
421 */
422 struct RoleInfoMap
423 {
424 const char* const role;
425 const RoleType roleType;
426 const char* const roleTranslationContext;
427 const char* const roleTranslation;
428 const char* const groupTranslationContext;
429 const char* const groupTranslation;
430 const bool requiresBaloo;
431 const bool requiresIndexer;
432 };
433
434 /**
435 * @return Map of user visible roles that are accessible by KFileItemModel::rolesInformation().
436 */
437 static const RoleInfoMap* rolesInfoMap(int& count);
438
439 /**
440 * Determines the MIME-types of all items that can be done within
441 * the given timeout.
442 */
443 static void determineMimeTypes(const KFileItemList& items, int timeout);
444
445 /**
446 * @return Returns a copy of \a value that is implicitly shared
447 * with other users to save memory.
448 */
449 static QByteArray sharedValue(const QByteArray& value);
450
451 /**
452 * Checks if the model's internal data structures are consistent.
453 */
454 bool isConsistent() const;
455
456 private:
457 KFileItemModelDirLister* m_dirLister;
458
459 QCollator m_collator;
460 bool m_naturalSorting;
461 bool m_sortDirsFirst;
462
463 RoleType m_sortRole;
464 int m_sortingProgressPercent; // Value of directorySortingProgress() signal
465 QSet<QByteArray> m_roles;
466
467 QList<ItemData*> m_itemData;
468
469 // m_items is a cache for the method index(const QUrl&). If it contains N
470 // entries, it is guaranteed that these correspond to the first N items in
471 // the model, i.e., that (for every i between 0 and N - 1)
472 // m_items.value(fileItem(i).url()) == i
473 mutable QHash<QUrl, int> m_items;
474
475 KFileItemModelFilter m_filter;
476 QHash<KFileItem, ItemData*> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
477
478 bool m_requestRole[RolesCount];
479
480 QTimer* m_maximumUpdateIntervalTimer;
481 QTimer* m_resortAllItemsTimer;
482 QList<ItemData*> m_pendingItemsToInsert;
483
484 // Cache for KFileItemModel::groups()
485 mutable QList<QPair<int, QVariant> > m_groups;
486
487 // Stores the URLs (key: target url, value: url) of the expanded directories.
488 QHash<QUrl, QUrl> m_expandedDirs;
489
490 // URLs that must be expanded. The expanding is initially triggered in setExpanded()
491 // and done step after step in slotCompleted().
492 QSet<QUrl> m_urlsToExpand;
493
494 friend class KFileItemModelLessThan; // Accesses lessThan() method
495 friend class KFileItemModelRolesUpdater; // Accesses emitSortProgress() method
496 friend class KFileItemModelTest; // For unit testing
497 friend class KFileItemModelBenchmark; // For unit testing
498 friend class KFileItemListViewTest; // For unit testing
499 friend class DolphinPart; // Accesses m_dirLister
500 };
501
502 inline bool KFileItemModel::nameLessThan(const ItemData* a, const ItemData* b)
503 {
504 return a->item.text() < b->item.text();
505 }
506
507
508 inline bool KFileItemModel::isChildItem(int index) const
509 {
510 if (m_itemData.at(index)->parent) {
511 return true;
512 } else {
513 return false;
514 }
515 }
516
517 #endif
518
519