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