]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kfileitemmodel.h
Add setting also hide application/x-trash files when hiding hidden files
[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 void scheduleResortAllItems();
209
210 Q_SIGNALS:
211 /**
212 * Is emitted if the loading of a directory has been started. It is
213 * assured that a signal directoryLoadingCompleted() will be send after
214 * the loading has been finished. For tracking the loading progress
215 * the signal directoryLoadingProgress() gets emitted in between.
216 */
217 void directoryLoadingStarted();
218
219 /**
220 * Is emitted after the loading of a directory has been completed or new
221 * items have been inserted to an already loaded directory. Usually
222 * one or more itemsInserted() signals are emitted before loadingCompleted()
223 * (the only exception is loading an empty directory, where only a
224 * loadingCompleted() signal gets emitted).
225 */
226 void directoryLoadingCompleted();
227
228 /**
229 * Is emitted when the model is being refreshed (F5 key press)
230 */
231 void directoryRefreshing();
232
233 /**
234 * Is emitted after the loading of a directory has been canceled.
235 */
236 void directoryLoadingCanceled();
237
238 /**
239 * Informs about the progress in percent when loading a directory. It is assured
240 * that the signal directoryLoadingStarted() has been emitted before.
241 */
242 void directoryLoadingProgress(int percent);
243
244 /**
245 * Is emitted if the sort-role gets resolved asynchronously and provides
246 * the progress-information of the sorting in percent. It is assured
247 * that the last sortProgress-signal contains 100 as value.
248 */
249 void directorySortingProgress(int percent);
250
251 /**
252 * Is emitted if an information message (e.g. "Connecting to host...")
253 * should be shown.
254 */
255 void infoMessage(const QString &message);
256
257 /**
258 * Is emitted if an error message (e.g. "Unknown location")
259 * should be shown.
260 */
261 void errorMessage(const QString &message);
262
263 /**
264 * Is emitted if a redirection from the current URL \a oldUrl
265 * to the new URL \a newUrl has been done.
266 */
267 void directoryRedirection(const QUrl &oldUrl, const QUrl &newUrl);
268
269 /**
270 * Is emitted when the URL passed by KFileItemModel::setUrl() represents a file.
271 * In this case no signal errorMessage() will be emitted.
272 */
273 void urlIsFileError(const QUrl &url);
274
275 /**
276 * It is emitted for files when they change and
277 * for dirs when files are added or removed.
278 */
279 void fileItemsChanged(const KFileItemList &changedFileItems);
280
281 /**
282 * It is emitted when the parent directory was removed.
283 */
284 void currentDirectoryRemoved();
285
286 protected:
287 void onGroupedSortingChanged(bool current) override;
288 void onSortRoleChanged(const QByteArray &current, const QByteArray &previous, bool resortItems = true) override;
289 void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) override;
290
291 private Q_SLOTS:
292 /**
293 * Resorts all items dependent on the set sortRole(), sortOrder()
294 * and foldersFirst() settings.
295 */
296 void resortAllItems();
297
298 void slotCompleted();
299 void slotCanceled();
300 void slotItemsAdded(const QUrl &directoryUrl, const KFileItemList &items);
301 void slotItemsDeleted(const KFileItemList &items);
302 void slotRefreshItems(const QList<QPair<KFileItem, KFileItem>> &items);
303 void slotClear();
304 void slotSortingChoiceChanged();
305 void slotListerError(KIO::Job *job);
306
307 void dispatchPendingItemsToInsert();
308
309 private:
310 enum RoleType {
311 // User visible roles:
312 NoRole,
313 NameRole,
314 SizeRole,
315 ModificationTimeRole,
316 CreationTimeRole,
317 AccessTimeRole,
318 PermissionsRole,
319 OwnerRole,
320 GroupRole,
321 TypeRole,
322 ExtensionRole,
323 DestinationRole,
324 PathRole,
325 DeletionTimeRole,
326 // User visible roles available with Baloo:
327 CommentRole,
328 TagsRole,
329 RatingRole,
330 DimensionsRole,
331 WidthRole,
332 HeightRole,
333 ImageDateTimeRole,
334 OrientationRole,
335 PublisherRole,
336 PageCountRole,
337 WordCountRole,
338 TitleRole,
339 AuthorRole,
340 LineCountRole,
341 ArtistRole,
342 GenreRole,
343 AlbumRole,
344 DurationRole,
345 TrackRole,
346 ReleaseYearRole,
347 BitrateRole,
348 OriginUrlRole,
349 AspectRatioRole,
350 FrameRateRole,
351 // Non-visible roles:
352 IsDirRole,
353 IsLinkRole,
354 IsHiddenRole,
355 IsExpandedRole,
356 IsExpandableRole,
357 ExpandedParentsCountRole,
358 // Mandatory last entry:
359 RolesCount
360 };
361
362 struct ItemData {
363 KFileItem item;
364 QHash<QByteArray, QVariant> values;
365 ItemData *parent;
366 };
367
368 enum RemoveItemsBehavior { KeepItemData, DeleteItemData, DeleteItemDataIfUnfiltered };
369
370 void insertItems(QList<ItemData *> &items);
371 void removeItems(const KItemRangeList &itemRanges, RemoveItemsBehavior behavior);
372
373 /**
374 * Helper method for insertItems() and removeItems(): Creates
375 * a list of ItemData elements based on the given items.
376 * Note that the ItemData instances are created dynamically and
377 * must be deleted by the caller.
378 */
379 QList<ItemData *> createItemDataList(const QUrl &parentUrl, const KFileItemList &items) const;
380
381 /**
382 * Prepares the items for sorting. Normally, the hash 'values' in ItemData is filled
383 * lazily to save time and memory, but for some sort roles, it is expected that the
384 * sort role data is stored in 'values'.
385 */
386 void prepareItemsForSorting(QList<ItemData *> &itemDataList);
387
388 static int expandedParentsCount(const ItemData *data);
389
390 void removeExpandedItems();
391
392 /**
393 * This function is called by setData() and slotRefreshItems(). It emits
394 * the itemsChanged() signal, checks if the sort order is still correct,
395 * and starts m_resortAllItemsTimer if that is not the case.
396 */
397 void emitItemsChangedAndTriggerResorting(const KItemRangeList &itemRanges, const QSet<QByteArray> &changedRoles);
398
399 /**
400 * Resets all values from m_requestRole to false.
401 */
402 void resetRoles();
403
404 /**
405 * @return Role-type for the given role.
406 * Runtime complexity is O(1).
407 */
408 RoleType typeForRole(const QByteArray &role) const;
409
410 /**
411 * @return Role-byte-array for the given role-type.
412 * Runtime complexity is O(1).
413 */
414 QByteArray roleForType(RoleType roleType) const;
415
416 QHash<QByteArray, QVariant> retrieveData(const KFileItem &item, const ItemData *parent) const;
417
418 /**
419 * @return True if role values benefit from natural or case insensitive sorting.
420 */
421 static bool isRoleValueNatural(const RoleType roleType);
422
423 /**
424 * @return True if \a a has a KFileItem whose text is 'less than' the one
425 * of \a b according to QString::operator<(const QString&).
426 */
427 static bool nameLessThan(const ItemData *a, const ItemData *b);
428
429 /**
430 * @return True if the item-data \a a should be ordered before the item-data
431 * \b. The item-data may have different parent-items.
432 */
433 bool lessThan(const ItemData *a, const ItemData *b, const QCollator &collator) const;
434
435 /**
436 * Sorts the items between \a begin and \a end using the comparison
437 * function lessThan().
438 */
439 void sort(const QList<ItemData *>::iterator &begin, const QList<ItemData *>::iterator &end) const;
440
441 /**
442 * Helper method for lessThan() and expandedParentsCountCompare(): Compares
443 * the passed item-data using m_sortRole as criteria. Both items must
444 * have the same parent item, otherwise the comparison will be wrong.
445 */
446 int sortRoleCompare(const ItemData *a, const ItemData *b, const QCollator &collator) const;
447
448 int stringCompare(const QString &a, const QString &b, const QCollator &collator) const;
449
450 QList<QPair<int, QVariant>> nameRoleGroups() const;
451 QList<QPair<int, QVariant>> sizeRoleGroups() const;
452 QList<QPair<int, QVariant>> timeRoleGroups(const std::function<QDateTime(const ItemData *)> &fileTimeCb) const;
453 QList<QPair<int, QVariant>> permissionRoleGroups() const;
454 QList<QPair<int, QVariant>> ratingRoleGroups() const;
455 QList<QPair<int, QVariant>> genericStringRoleGroups(const QByteArray &typeForRole) const;
456
457 /**
458 * Helper method for all xxxRoleGroups() methods to check whether the
459 * item with the given index is a child-item. A child-item is defined
460 * as item having an expansion-level > 0. All xxxRoleGroups() methods
461 * should skip the grouping if the item is a child-item (although
462 * KItemListView would be capable to show sub-groups in groups this
463 * results in visual clutter for most usecases).
464 */
465 bool isChildItem(int index) const;
466
467 /**
468 * Is invoked by KFileItemModelRolesUpdater and results in emitting the
469 * sortProgress signal with a percent-value of the progress.
470 */
471 void emitSortProgress(int resolvedCount);
472
473 /**
474 * Applies the filters set through @ref setNameFilter and @ref setMimeTypeFilters.
475 */
476 void applyFilters();
477
478 /**
479 * Removes filtered items whose expanded parents have been deleted
480 * or collapsed via setExpanded(parentIndex, false).
481 */
482 void removeFilteredChildren(const KItemRangeList &parents);
483
484 /**
485 * Loads the selected choice of sorting method from Dolphin General Settings
486 */
487 void loadSortingSettings();
488
489 /**
490 * Maps the QByteArray-roles to RoleTypes and provides translation- and
491 * group-contexts.
492 */
493 struct RoleInfoMap {
494 const char *const role;
495 const RoleType roleType;
496 const KLazyLocalizedString roleTranslation;
497 const KLazyLocalizedString groupTranslation;
498 const KLazyLocalizedString tooltipTranslation;
499 const bool requiresBaloo;
500 const bool requiresIndexer;
501 };
502
503 /**
504 * @return Map of user visible roles that are accessible by KFileItemModel::rolesInformation().
505 */
506 static const RoleInfoMap *rolesInfoMap(int &count);
507
508 /**
509 * Determines the MIME-types of all items that can be done within
510 * the given timeout.
511 */
512 static void determineMimeTypes(const KFileItemList &items, int timeout);
513
514 /**
515 * @return Returns a copy of \a value that is implicitly shared
516 * with other users to save memory.
517 */
518 static QByteArray sharedValue(const QByteArray &value);
519
520 /**
521 * Checks if the model's internal data structures are consistent.
522 */
523 bool isConsistent() const;
524
525 /**
526 * Filters out the expanded folders that don't pass the filter themselves and don't have any filter-passing children.
527 * Will update the removedItemRanges arguments to include the parents that have been filtered.
528 * @returns the number of parents that have been filtered.
529 * @param removedItemRanges The ranges of items being deleted/filtered, will get updated
530 * @param parentsToEnsureVisible Parents that must be visible no matter what due to being ancestors of newly visible items
531 */
532 int filterChildlessParents(KItemRangeList &removedItemRanges, const QSet<ItemData *> &parentsToEnsureVisible = QSet<ItemData *>());
533
534 private:
535 KDirLister *m_dirLister = nullptr;
536
537 QCollator m_collator;
538 bool m_naturalSorting;
539 bool m_sortDirsFirst;
540 bool m_sortHiddenLast;
541
542 RoleType m_sortRole;
543 int m_sortingProgressPercent; // Value of directorySortingProgress() signal
544 QSet<QByteArray> m_roles;
545
546 QList<ItemData *> m_itemData;
547
548 // m_items is a cache for the method index(const QUrl&). If it contains N
549 // entries, it is guaranteed that these correspond to the first N items in
550 // the model, i.e., that (for every i between 0 and N - 1)
551 // m_items.value(fileItem(i).url()) == i
552 mutable QHash<QUrl, int> m_items;
553
554 KFileItemModelFilter m_filter;
555 QHash<KFileItem, ItemData *> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
556
557 bool m_requestRole[RolesCount];
558
559 QTimer *m_maximumUpdateIntervalTimer;
560 QTimer *m_resortAllItemsTimer;
561 QList<ItemData *> m_pendingItemsToInsert;
562
563 // Cache for KFileItemModel::groups()
564 mutable QList<QPair<int, QVariant>> m_groups;
565
566 // Stores the URLs (key: target url, value: url) of the expanded directories.
567 QHash<QUrl, QUrl> m_expandedDirs;
568
569 // URLs that must be expanded. The expanding is initially triggered in setExpanded()
570 // and done step after step in slotCompleted().
571 QSet<QUrl> m_urlsToExpand;
572
573 friend class KFileItemModelRolesUpdater; // Accesses emitSortProgress() method
574 friend class KFileItemModelTest; // For unit testing
575 friend class KFileItemModelBenchmark; // For unit testing
576 friend class KFileItemListViewTest; // For unit testing
577 friend class DolphinPart; // Accesses m_dirLister
578 };
579
580 inline bool KFileItemModel::isRoleValueNatural(RoleType roleType)
581 {
582 return (roleType == TypeRole || roleType == ExtensionRole || roleType == TagsRole || roleType == CommentRole || roleType == TitleRole
583 || roleType == ArtistRole || roleType == GenreRole || roleType == AlbumRole || roleType == PathRole || roleType == DestinationRole
584 || roleType == OriginUrlRole || roleType == OwnerRole || roleType == GroupRole);
585 }
586
587 inline bool KFileItemModel::nameLessThan(const ItemData *a, const ItemData *b)
588 {
589 return a->item.text() < b->item.text();
590 }
591
592 inline bool KFileItemModel::isChildItem(int index) const
593 {
594 if (m_itemData.at(index)->parent) {
595 return true;
596 } else {
597 return false;
598 }
599 }
600
601 #endif