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