File indexing completed on 2024-04-28 05:45:05
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 Q_SIGNALS: 0209 /** 0210 * Is emitted if the loading of a directory has been started. It is 0211 * assured that a signal directoryLoadingCompleted() will be send after 0212 * the loading has been finished. For tracking the loading progress 0213 * the signal directoryLoadingProgress() gets emitted in between. 0214 */ 0215 void directoryLoadingStarted(); 0216 0217 /** 0218 * Is emitted after the loading of a directory has been completed or new 0219 * items have been inserted to an already loaded directory. Usually 0220 * one or more itemsInserted() signals are emitted before loadingCompleted() 0221 * (the only exception is loading an empty directory, where only a 0222 * loadingCompleted() signal gets emitted). 0223 */ 0224 void directoryLoadingCompleted(); 0225 0226 /** 0227 * Is emitted when the model is being refreshed (F5 key press) 0228 */ 0229 void directoryRefreshing(); 0230 0231 /** 0232 * Is emitted after the loading of a directory has been canceled. 0233 */ 0234 void directoryLoadingCanceled(); 0235 0236 /** 0237 * Informs about the progress in percent when loading a directory. It is assured 0238 * that the signal directoryLoadingStarted() has been emitted before. 0239 */ 0240 void directoryLoadingProgress(int percent); 0241 0242 /** 0243 * Is emitted if the sort-role gets resolved asynchronously and provides 0244 * the progress-information of the sorting in percent. It is assured 0245 * that the last sortProgress-signal contains 100 as value. 0246 */ 0247 void directorySortingProgress(int percent); 0248 0249 /** 0250 * Is emitted if an information message (e.g. "Connecting to host...") 0251 * should be shown. 0252 */ 0253 void infoMessage(const QString &message); 0254 0255 /** 0256 * Is emitted if an error message (e.g. "Unknown location") 0257 * should be shown. 0258 */ 0259 void errorMessage(const QString &message); 0260 0261 /** 0262 * Is emitted if a redirection from the current URL \a oldUrl 0263 * to the new URL \a newUrl has been done. 0264 */ 0265 void directoryRedirection(const QUrl &oldUrl, const QUrl &newUrl); 0266 0267 /** 0268 * Is emitted when the URL passed by KFileItemModel::setUrl() represents a file. 0269 * In this case no signal errorMessage() will be emitted. 0270 */ 0271 void urlIsFileError(const QUrl &url); 0272 0273 /** 0274 * It is emitted for files when they change and 0275 * for dirs when files are added or removed. 0276 */ 0277 void fileItemsChanged(const KFileItemList &changedFileItems); 0278 0279 /** 0280 * It is emitted when the parent directory was removed. 0281 */ 0282 void currentDirectoryRemoved(); 0283 0284 protected: 0285 void onGroupedSortingChanged(bool current) override; 0286 void onSortRoleChanged(const QByteArray ¤t, const QByteArray &previous, bool resortItems = true) override; 0287 void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) override; 0288 0289 private Q_SLOTS: 0290 /** 0291 * Resorts all items dependent on the set sortRole(), sortOrder() 0292 * and foldersFirst() settings. 0293 */ 0294 void resortAllItems(); 0295 0296 void slotCompleted(); 0297 void slotCanceled(); 0298 void slotItemsAdded(const QUrl &directoryUrl, const KFileItemList &items); 0299 void slotItemsDeleted(const KFileItemList &items); 0300 void slotRefreshItems(const QList<QPair<KFileItem, KFileItem>> &items); 0301 void slotClear(); 0302 void slotSortingChoiceChanged(); 0303 void slotListerError(KIO::Job *job); 0304 0305 void dispatchPendingItemsToInsert(); 0306 0307 private: 0308 enum RoleType { 0309 // User visible roles: 0310 NoRole, 0311 NameRole, 0312 SizeRole, 0313 ModificationTimeRole, 0314 CreationTimeRole, 0315 AccessTimeRole, 0316 PermissionsRole, 0317 OwnerRole, 0318 GroupRole, 0319 TypeRole, 0320 ExtensionRole, 0321 DestinationRole, 0322 PathRole, 0323 DeletionTimeRole, 0324 // User visible roles available with Baloo: 0325 CommentRole, 0326 TagsRole, 0327 RatingRole, 0328 DimensionsRole, 0329 WidthRole, 0330 HeightRole, 0331 ImageDateTimeRole, 0332 OrientationRole, 0333 PublisherRole, 0334 PageCountRole, 0335 WordCountRole, 0336 TitleRole, 0337 AuthorRole, 0338 LineCountRole, 0339 ArtistRole, 0340 GenreRole, 0341 AlbumRole, 0342 DurationRole, 0343 TrackRole, 0344 ReleaseYearRole, 0345 BitrateRole, 0346 OriginUrlRole, 0347 AspectRatioRole, 0348 FrameRateRole, 0349 // Non-visible roles: 0350 IsDirRole, 0351 IsLinkRole, 0352 IsHiddenRole, 0353 IsExpandedRole, 0354 IsExpandableRole, 0355 ExpandedParentsCountRole, 0356 // Mandatory last entry: 0357 RolesCount 0358 }; 0359 0360 struct ItemData { 0361 KFileItem item; 0362 QHash<QByteArray, QVariant> values; 0363 ItemData *parent; 0364 }; 0365 0366 enum RemoveItemsBehavior { KeepItemData, DeleteItemData, DeleteItemDataIfUnfiltered }; 0367 0368 void insertItems(QList<ItemData *> &items); 0369 void removeItems(const KItemRangeList &itemRanges, RemoveItemsBehavior behavior); 0370 0371 /** 0372 * Helper method for insertItems() and removeItems(): Creates 0373 * a list of ItemData elements based on the given items. 0374 * Note that the ItemData instances are created dynamically and 0375 * must be deleted by the caller. 0376 */ 0377 QList<ItemData *> createItemDataList(const QUrl &parentUrl, const KFileItemList &items) const; 0378 0379 /** 0380 * Prepares the items for sorting. Normally, the hash 'values' in ItemData is filled 0381 * lazily to save time and memory, but for some sort roles, it is expected that the 0382 * sort role data is stored in 'values'. 0383 */ 0384 void prepareItemsForSorting(QList<ItemData *> &itemDataList); 0385 0386 static int expandedParentsCount(const ItemData *data); 0387 0388 void removeExpandedItems(); 0389 0390 /** 0391 * This function is called by setData() and slotRefreshItems(). It emits 0392 * the itemsChanged() signal, checks if the sort order is still correct, 0393 * and starts m_resortAllItemsTimer if that is not the case. 0394 */ 0395 void emitItemsChangedAndTriggerResorting(const KItemRangeList &itemRanges, const QSet<QByteArray> &changedRoles); 0396 0397 /** 0398 * Resets all values from m_requestRole to false. 0399 */ 0400 void resetRoles(); 0401 0402 /** 0403 * @return Role-type for the given role. 0404 * Runtime complexity is O(1). 0405 */ 0406 RoleType typeForRole(const QByteArray &role) const; 0407 0408 /** 0409 * @return Role-byte-array for the given role-type. 0410 * Runtime complexity is O(1). 0411 */ 0412 QByteArray roleForType(RoleType roleType) const; 0413 0414 QHash<QByteArray, QVariant> retrieveData(const KFileItem &item, const ItemData *parent) const; 0415 0416 /** 0417 * @return True if role values benefit from natural or case insensitive sorting. 0418 */ 0419 static bool isRoleValueNatural(const RoleType roleType); 0420 0421 /** 0422 * @return True if \a a has a KFileItem whose text is 'less than' the one 0423 * of \a b according to QString::operator<(const QString&). 0424 */ 0425 static bool nameLessThan(const ItemData *a, const ItemData *b); 0426 0427 /** 0428 * @return True if the item-data \a a should be ordered before the item-data 0429 * \b. The item-data may have different parent-items. 0430 */ 0431 bool lessThan(const ItemData *a, const ItemData *b, const QCollator &collator) const; 0432 0433 /** 0434 * Sorts the items between \a begin and \a end using the comparison 0435 * function lessThan(). 0436 */ 0437 void sort(const QList<ItemData *>::iterator &begin, const QList<ItemData *>::iterator &end) const; 0438 0439 /** 0440 * Helper method for lessThan() and expandedParentsCountCompare(): Compares 0441 * the passed item-data using m_sortRole as criteria. Both items must 0442 * have the same parent item, otherwise the comparison will be wrong. 0443 */ 0444 int sortRoleCompare(const ItemData *a, const ItemData *b, const QCollator &collator) const; 0445 0446 int stringCompare(const QString &a, const QString &b, const QCollator &collator) const; 0447 0448 QList<QPair<int, QVariant>> nameRoleGroups() const; 0449 QList<QPair<int, QVariant>> sizeRoleGroups() const; 0450 QList<QPair<int, QVariant>> timeRoleGroups(const std::function<QDateTime(const ItemData *)> &fileTimeCb) const; 0451 QList<QPair<int, QVariant>> permissionRoleGroups() const; 0452 QList<QPair<int, QVariant>> ratingRoleGroups() const; 0453 QList<QPair<int, QVariant>> genericStringRoleGroups(const QByteArray &typeForRole) const; 0454 0455 /** 0456 * Helper method for all xxxRoleGroups() methods to check whether the 0457 * item with the given index is a child-item. A child-item is defined 0458 * as item having an expansion-level > 0. All xxxRoleGroups() methods 0459 * should skip the grouping if the item is a child-item (although 0460 * KItemListView would be capable to show sub-groups in groups this 0461 * results in visual clutter for most usecases). 0462 */ 0463 bool isChildItem(int index) const; 0464 0465 void scheduleResortAllItems(); 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