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 &current, 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