File indexing completed on 2024-05-12 09:50:53
0001 /* 0002 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #ifndef KFILEITEMMODEL_H 0008 #define KFILEITEMMODEL_H 0009 0010 #include "dolphin_export.h" 0011 #include "kitemviews/kitemmodelbase.h" 0012 #include "kitemviews/private/kfileitemmodelfilter.h" 0013 0014 #include <KFileItem> 0015 #include <KLazyLocalizedString> 0016 0017 #include <QCollator> 0018 #include <QHash> 0019 #include <QSet> 0020 #include <QUrl> 0021 0022 #include <functional> 0023 0024 class KDirLister; 0025 0026 class QTimer; 0027 0028 namespace KIO 0029 { 0030 class Job; 0031 } 0032 0033 /** 0034 * @brief KItemModelBase implementation for KFileItems. 0035 * 0036 * Allows to load items of a directory. Sorting and grouping of 0037 * items are supported. Roles that are not part of KFileItem can 0038 * be added with KFileItemModel::setData(). 0039 * 0040 * Recursive expansion of sub-directories is supported by 0041 * KFileItemModel::setExpanded(). 0042 */ 0043 class DOLPHIN_EXPORT KFileItemModel : public KItemModelBase 0044 { 0045 Q_OBJECT 0046 0047 public: 0048 explicit KFileItemModel(QObject *parent = nullptr); 0049 ~KFileItemModel() override; 0050 0051 /** 0052 * Loads the directory specified by \a url. The signals 0053 * directoryLoadingStarted(), directoryLoadingProgress() and directoryLoadingCompleted() 0054 * indicate the current state of the loading process. The items 0055 * of the directory are added after the loading has been completed. 0056 */ 0057 void loadDirectory(const QUrl &url); 0058 0059 /** 0060 * Throws away all currently loaded items and refreshes the directory 0061 * by reloading all items again. 0062 */ 0063 void refreshDirectory(const QUrl &url); 0064 0065 /** 0066 * @return Parent directory of the items that are shown. In case 0067 * if a directory tree is shown, KFileItemModel::dir() returns 0068 * the root-parent of all items. 0069 * @see rootItem() 0070 */ 0071 QUrl directory() const override; 0072 0073 /** 0074 * Cancels the loading of a directory which has been started by either 0075 * loadDirectory() or refreshDirectory(). 0076 */ 0077 void cancelDirectoryLoading(); 0078 0079 int count() const override; 0080 QHash<QByteArray, QVariant> data(int index) const override; 0081 bool setData(int index, const QHash<QByteArray, QVariant> &values) override; 0082 0083 /** 0084 * Sets a separate sorting with directories first (true) or a mixed 0085 * sorting of files and directories (false). 0086 */ 0087 void setSortDirectoriesFirst(bool dirsFirst); 0088 bool sortDirectoriesFirst() const; 0089 0090 /** 0091 * Sets a separate sorting with hidden files and folders last (true) or not (false). 0092 */ 0093 void setSortHiddenLast(bool hiddenLast); 0094 bool sortHiddenLast() const; 0095 0096 void setShowHiddenFiles(bool show); 0097 bool showHiddenFiles() const; 0098 0099 /** 0100 * If set to true, only directories are shown as items of the model. Files 0101 * are ignored. 0102 */ 0103 void setShowDirectoriesOnly(bool enabled); 0104 bool showDirectoriesOnly() const; 0105 0106 QMimeData *createMimeData(const KItemSet &indexes) const override; 0107 0108 int indexForKeyboardSearch(const QString &text, int startFromIndex = 0) const override; 0109 0110 bool supportsDropping(int index) const override; 0111 0112 QString roleDescription(const QByteArray &role) const override; 0113 0114 QList<QPair<int, QVariant>> groups() const override; 0115 0116 /** 0117 * @return The file-item for the index \a index. If the index is in a valid 0118 * range it is assured that the file-item is not null. The runtime 0119 * complexity of this call is O(1). 0120 */ 0121 KFileItem fileItem(int index) const; 0122 0123 /** 0124 * @return The file-item for the url \a url. If no file-item with the given 0125 * URL is found KFileItem::isNull() will be true for the returned 0126 * file-item. The runtime complexity of this call is O(1). 0127 */ 0128 KFileItem fileItem(const QUrl &url) const; 0129 0130 /** 0131 * @return The index for the file-item \a item. -1 is returned if no file-item 0132 * is found or if the file-item is null. The amortized runtime 0133 * complexity of this call is O(1). 0134 */ 0135 int index(const KFileItem &item) const; 0136 0137 /** 0138 * @return The index for the URL \a url. -1 is returned if no file-item 0139 * is found. The amortized runtime complexity of this call is O(1). 0140 */ 0141 int index(const QUrl &url) const; 0142 0143 /** 0144 * @return Root item of all items representing the item 0145 * for KFileItemModel::dir(). 0146 */ 0147 KFileItem rootItem() const; 0148 0149 /** 0150 * Clears all items of the model. 0151 */ 0152 void clear(); 0153 0154 /** 0155 * Sets the roles that should be shown for each item. 0156 */ 0157 void setRoles(const QSet<QByteArray> &roles); 0158 QSet<QByteArray> roles() const; 0159 0160 bool setExpanded(int index, bool expanded) override; 0161 bool isExpanded(int index) const override; 0162 bool isExpandable(int index) const override; 0163 int expandedParentsCount(int index) const override; 0164 0165 QSet<QUrl> expandedDirectories() const; 0166 0167 /** 0168 * Marks the URLs in \a urls as sub-directories which were expanded previously. 0169 * After calling loadDirectory() or refreshDirectory() the marked sub-directories 0170 * will be expanded step-by-step. 0171 */ 0172 void restoreExpandedDirectories(const QSet<QUrl> &urls); 0173 0174 /** 0175 * Expands all parent-directories of the item \a url. 0176 */ 0177 void expandParentDirectories(const QUrl &url); 0178 0179 void setNameFilter(const QString &nameFilter); 0180 QString nameFilter() const; 0181 0182 void setMimeTypeFilters(const QStringList &filters); 0183 QStringList mimeTypeFilters() const; 0184 0185 void setExcludeMimeTypeFilter(const QStringList &filters); 0186 QStringList excludeMimeTypeFilter() const; 0187 0188 struct RoleInfo { 0189 QByteArray role; 0190 QString translation; 0191 QString group; 0192 QString tooltip; 0193 bool requiresBaloo; 0194 bool requiresIndexer; 0195 }; 0196 0197 /** 0198 * @return Provides static information for all available roles that 0199 * are supported by KFileItemModel. Some roles can only be 0200 * determined if Baloo is enabled and/or the Baloo 0201 * indexing is enabled. 0202 */ 0203 static QList<RoleInfo> rolesInformation(); 0204 0205 /** set to true to hide application/x-trash files */ 0206 void setShowTrashMime(bool show); 0207 0208 void scheduleResortAllItems(); 0209 0210 Q_SIGNALS: 0211 /** 0212 * Is emitted if the loading of a directory has been started. It is 0213 * assured that a signal directoryLoadingCompleted() will be send after 0214 * the loading has been finished. For tracking the loading progress 0215 * the signal directoryLoadingProgress() gets emitted in between. 0216 */ 0217 void directoryLoadingStarted(); 0218 0219 /** 0220 * Is emitted after the loading of a directory has been completed or new 0221 * items have been inserted to an already loaded directory. Usually 0222 * one or more itemsInserted() signals are emitted before loadingCompleted() 0223 * (the only exception is loading an empty directory, where only a 0224 * loadingCompleted() signal gets emitted). 0225 */ 0226 void directoryLoadingCompleted(); 0227 0228 /** 0229 * Is emitted when the model is being refreshed (F5 key press) 0230 */ 0231 void directoryRefreshing(); 0232 0233 /** 0234 * Is emitted after the loading of a directory has been canceled. 0235 */ 0236 void directoryLoadingCanceled(); 0237 0238 /** 0239 * Informs about the progress in percent when loading a directory. It is assured 0240 * that the signal directoryLoadingStarted() has been emitted before. 0241 */ 0242 void directoryLoadingProgress(int percent); 0243 0244 /** 0245 * Is emitted if the sort-role gets resolved asynchronously and provides 0246 * the progress-information of the sorting in percent. It is assured 0247 * that the last sortProgress-signal contains 100 as value. 0248 */ 0249 void directorySortingProgress(int percent); 0250 0251 /** 0252 * Is emitted if an information message (e.g. "Connecting to host...") 0253 * should be shown. 0254 */ 0255 void infoMessage(const QString &message); 0256 0257 /** 0258 * Is emitted if an error message (e.g. "Unknown location") 0259 * should be shown. 0260 */ 0261 void errorMessage(const QString &message); 0262 0263 /** 0264 * Is emitted if a redirection from the current URL \a oldUrl 0265 * to the new URL \a newUrl has been done. 0266 */ 0267 void directoryRedirection(const QUrl &oldUrl, const QUrl &newUrl); 0268 0269 /** 0270 * Is emitted when the URL passed by KFileItemModel::setUrl() represents a file. 0271 * In this case no signal errorMessage() will be emitted. 0272 */ 0273 void urlIsFileError(const QUrl &url); 0274 0275 /** 0276 * It is emitted for files when they change and 0277 * for dirs when files are added or removed. 0278 */ 0279 void fileItemsChanged(const KFileItemList &changedFileItems); 0280 0281 /** 0282 * It is emitted when the parent directory was removed. 0283 */ 0284 void currentDirectoryRemoved(); 0285 0286 protected: 0287 void onGroupedSortingChanged(bool current) override; 0288 void onSortRoleChanged(const QByteArray ¤t, const QByteArray &previous, bool resortItems = true) override; 0289 void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) override; 0290 0291 private Q_SLOTS: 0292 /** 0293 * Resorts all items dependent on the set sortRole(), sortOrder() 0294 * and foldersFirst() settings. 0295 */ 0296 void resortAllItems(); 0297 0298 void slotCompleted(); 0299 void slotCanceled(); 0300 void slotItemsAdded(const QUrl &directoryUrl, const KFileItemList &items); 0301 void slotItemsDeleted(const KFileItemList &items); 0302 void slotRefreshItems(const QList<QPair<KFileItem, KFileItem>> &items); 0303 void slotClear(); 0304 void slotSortingChoiceChanged(); 0305 void slotListerError(KIO::Job *job); 0306 0307 void dispatchPendingItemsToInsert(); 0308 0309 private: 0310 enum RoleType { 0311 // User visible roles: 0312 NoRole, 0313 NameRole, 0314 SizeRole, 0315 ModificationTimeRole, 0316 CreationTimeRole, 0317 AccessTimeRole, 0318 PermissionsRole, 0319 OwnerRole, 0320 GroupRole, 0321 TypeRole, 0322 ExtensionRole, 0323 DestinationRole, 0324 PathRole, 0325 DeletionTimeRole, 0326 // User visible roles available with Baloo: 0327 CommentRole, 0328 TagsRole, 0329 RatingRole, 0330 DimensionsRole, 0331 WidthRole, 0332 HeightRole, 0333 ImageDateTimeRole, 0334 OrientationRole, 0335 PublisherRole, 0336 PageCountRole, 0337 WordCountRole, 0338 TitleRole, 0339 AuthorRole, 0340 LineCountRole, 0341 ArtistRole, 0342 GenreRole, 0343 AlbumRole, 0344 DurationRole, 0345 TrackRole, 0346 ReleaseYearRole, 0347 BitrateRole, 0348 OriginUrlRole, 0349 AspectRatioRole, 0350 FrameRateRole, 0351 // Non-visible roles: 0352 IsDirRole, 0353 IsLinkRole, 0354 IsHiddenRole, 0355 IsExpandedRole, 0356 IsExpandableRole, 0357 ExpandedParentsCountRole, 0358 // Mandatory last entry: 0359 RolesCount 0360 }; 0361 0362 struct ItemData { 0363 KFileItem item; 0364 QHash<QByteArray, QVariant> values; 0365 ItemData *parent; 0366 }; 0367 0368 enum RemoveItemsBehavior { KeepItemData, DeleteItemData, DeleteItemDataIfUnfiltered }; 0369 0370 void insertItems(QList<ItemData *> &items); 0371 void removeItems(const KItemRangeList &itemRanges, RemoveItemsBehavior behavior); 0372 0373 /** 0374 * Helper method for insertItems() and removeItems(): Creates 0375 * a list of ItemData elements based on the given items. 0376 * Note that the ItemData instances are created dynamically and 0377 * must be deleted by the caller. 0378 */ 0379 QList<ItemData *> createItemDataList(const QUrl &parentUrl, const KFileItemList &items) const; 0380 0381 /** 0382 * Prepares the items for sorting. Normally, the hash 'values' in ItemData is filled 0383 * lazily to save time and memory, but for some sort roles, it is expected that the 0384 * sort role data is stored in 'values'. 0385 */ 0386 void prepareItemsForSorting(QList<ItemData *> &itemDataList); 0387 0388 static int expandedParentsCount(const ItemData *data); 0389 0390 void removeExpandedItems(); 0391 0392 /** 0393 * This function is called by setData() and slotRefreshItems(). It emits 0394 * the itemsChanged() signal, checks if the sort order is still correct, 0395 * and starts m_resortAllItemsTimer if that is not the case. 0396 */ 0397 void emitItemsChangedAndTriggerResorting(const KItemRangeList &itemRanges, const QSet<QByteArray> &changedRoles); 0398 0399 /** 0400 * Resets all values from m_requestRole to false. 0401 */ 0402 void resetRoles(); 0403 0404 /** 0405 * @return Role-type for the given role. 0406 * Runtime complexity is O(1). 0407 */ 0408 RoleType typeForRole(const QByteArray &role) const; 0409 0410 /** 0411 * @return Role-byte-array for the given role-type. 0412 * Runtime complexity is O(1). 0413 */ 0414 QByteArray roleForType(RoleType roleType) const; 0415 0416 QHash<QByteArray, QVariant> retrieveData(const KFileItem &item, const ItemData *parent) const; 0417 0418 /** 0419 * @return True if role values benefit from natural or case insensitive sorting. 0420 */ 0421 static bool isRoleValueNatural(const RoleType roleType); 0422 0423 /** 0424 * @return True if \a a has a KFileItem whose text is 'less than' the one 0425 * of \a b according to QString::operator<(const QString&). 0426 */ 0427 static bool nameLessThan(const ItemData *a, const ItemData *b); 0428 0429 /** 0430 * @return True if the item-data \a a should be ordered before the item-data 0431 * \b. The item-data may have different parent-items. 0432 */ 0433 bool lessThan(const ItemData *a, const ItemData *b, const QCollator &collator) const; 0434 0435 /** 0436 * Sorts the items between \a begin and \a end using the comparison 0437 * function lessThan(). 0438 */ 0439 void sort(const QList<ItemData *>::iterator &begin, const QList<ItemData *>::iterator &end) const; 0440 0441 /** 0442 * Helper method for lessThan() and expandedParentsCountCompare(): Compares 0443 * the passed item-data using m_sortRole as criteria. Both items must 0444 * have the same parent item, otherwise the comparison will be wrong. 0445 */ 0446 int sortRoleCompare(const ItemData *a, const ItemData *b, const QCollator &collator) const; 0447 0448 int stringCompare(const QString &a, const QString &b, const QCollator &collator) const; 0449 0450 QList<QPair<int, QVariant>> nameRoleGroups() const; 0451 QList<QPair<int, QVariant>> sizeRoleGroups() const; 0452 QList<QPair<int, QVariant>> timeRoleGroups(const std::function<QDateTime(const ItemData *)> &fileTimeCb) const; 0453 QList<QPair<int, QVariant>> permissionRoleGroups() const; 0454 QList<QPair<int, QVariant>> ratingRoleGroups() const; 0455 QList<QPair<int, QVariant>> genericStringRoleGroups(const QByteArray &typeForRole) const; 0456 0457 /** 0458 * Helper method for all xxxRoleGroups() methods to check whether the 0459 * item with the given index is a child-item. A child-item is defined 0460 * as item having an expansion-level > 0. All xxxRoleGroups() methods 0461 * should skip the grouping if the item is a child-item (although 0462 * KItemListView would be capable to show sub-groups in groups this 0463 * results in visual clutter for most usecases). 0464 */ 0465 bool isChildItem(int index) const; 0466 0467 /** 0468 * Is invoked by KFileItemModelRolesUpdater and results in emitting the 0469 * sortProgress signal with a percent-value of the progress. 0470 */ 0471 void emitSortProgress(int resolvedCount); 0472 0473 /** 0474 * Applies the filters set through @ref setNameFilter and @ref setMimeTypeFilters. 0475 */ 0476 void applyFilters(); 0477 0478 /** 0479 * Removes filtered items whose expanded parents have been deleted 0480 * or collapsed via setExpanded(parentIndex, false). 0481 */ 0482 void removeFilteredChildren(const KItemRangeList &parents); 0483 0484 /** 0485 * Loads the selected choice of sorting method from Dolphin General Settings 0486 */ 0487 void loadSortingSettings(); 0488 0489 /** 0490 * Maps the QByteArray-roles to RoleTypes and provides translation- and 0491 * group-contexts. 0492 */ 0493 struct RoleInfoMap { 0494 const char *const role; 0495 const RoleType roleType; 0496 const KLazyLocalizedString roleTranslation; 0497 const KLazyLocalizedString groupTranslation; 0498 const KLazyLocalizedString tooltipTranslation; 0499 const bool requiresBaloo; 0500 const bool requiresIndexer; 0501 }; 0502 0503 /** 0504 * @return Map of user visible roles that are accessible by KFileItemModel::rolesInformation(). 0505 */ 0506 static const RoleInfoMap *rolesInfoMap(int &count); 0507 0508 /** 0509 * Determines the MIME-types of all items that can be done within 0510 * the given timeout. 0511 */ 0512 static void determineMimeTypes(const KFileItemList &items, int timeout); 0513 0514 /** 0515 * @return Returns a copy of \a value that is implicitly shared 0516 * with other users to save memory. 0517 */ 0518 static QByteArray sharedValue(const QByteArray &value); 0519 0520 /** 0521 * Checks if the model's internal data structures are consistent. 0522 */ 0523 bool isConsistent() const; 0524 0525 /** 0526 * Filters out the expanded folders that don't pass the filter themselves and don't have any filter-passing children. 0527 * Will update the removedItemRanges arguments to include the parents that have been filtered. 0528 * @returns the number of parents that have been filtered. 0529 * @param removedItemRanges The ranges of items being deleted/filtered, will get updated 0530 * @param parentsToEnsureVisible Parents that must be visible no matter what due to being ancestors of newly visible items 0531 */ 0532 int filterChildlessParents(KItemRangeList &removedItemRanges, const QSet<ItemData *> &parentsToEnsureVisible = QSet<ItemData *>()); 0533 0534 private: 0535 KDirLister *m_dirLister = nullptr; 0536 0537 QCollator m_collator; 0538 bool m_naturalSorting; 0539 bool m_sortDirsFirst; 0540 bool m_sortHiddenLast; 0541 0542 RoleType m_sortRole; 0543 int m_sortingProgressPercent; // Value of directorySortingProgress() signal 0544 QSet<QByteArray> m_roles; 0545 0546 QList<ItemData *> m_itemData; 0547 0548 // m_items is a cache for the method index(const QUrl&). If it contains N 0549 // entries, it is guaranteed that these correspond to the first N items in 0550 // the model, i.e., that (for every i between 0 and N - 1) 0551 // m_items.value(fileItem(i).url()) == i 0552 mutable QHash<QUrl, int> m_items; 0553 0554 KFileItemModelFilter m_filter; 0555 QHash<KFileItem, ItemData *> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter() 0556 0557 bool m_requestRole[RolesCount]; 0558 0559 QTimer *m_maximumUpdateIntervalTimer; 0560 QTimer *m_resortAllItemsTimer; 0561 QList<ItemData *> m_pendingItemsToInsert; 0562 0563 // Cache for KFileItemModel::groups() 0564 mutable QList<QPair<int, QVariant>> m_groups; 0565 0566 // Stores the URLs (key: target url, value: url) of the expanded directories. 0567 QHash<QUrl, QUrl> m_expandedDirs; 0568 0569 // URLs that must be expanded. The expanding is initially triggered in setExpanded() 0570 // and done step after step in slotCompleted(). 0571 QSet<QUrl> m_urlsToExpand; 0572 0573 friend class KFileItemModelRolesUpdater; // Accesses emitSortProgress() method 0574 friend class KFileItemModelTest; // For unit testing 0575 friend class KFileItemModelBenchmark; // For unit testing 0576 friend class KFileItemListViewTest; // For unit testing 0577 friend class DolphinPart; // Accesses m_dirLister 0578 }; 0579 0580 inline bool KFileItemModel::isRoleValueNatural(RoleType roleType) 0581 { 0582 return (roleType == TypeRole || roleType == ExtensionRole || roleType == TagsRole || roleType == CommentRole || roleType == TitleRole 0583 || roleType == ArtistRole || roleType == GenreRole || roleType == AlbumRole || roleType == PathRole || roleType == DestinationRole 0584 || roleType == OriginUrlRole || roleType == OwnerRole || roleType == GroupRole); 0585 } 0586 0587 inline bool KFileItemModel::nameLessThan(const ItemData *a, const ItemData *b) 0588 { 0589 return a->item.text() < b->item.text(); 0590 } 0591 0592 inline bool KFileItemModel::isChildItem(int index) const 0593 { 0594 if (m_itemData.at(index)->parent) { 0595 return true; 0596 } else { 0597 return false; 0598 } 0599 } 0600 0601 #endif