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