X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/d6e10a5942442f8423385ff1301c9163b334e4ed..df871967c948de1aeb4d635fafd937369121aef0:/src/kitemviews/kfileitemmodel.h diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index d48f600df..62a283d33 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -23,65 +23,84 @@ #include #include #include -#include #include +#include #include +#include -class KDirLister; +class KFileItemModelDirLister; class QTimer; /** * @brief KItemModelBase implementation for KFileItems. * - * KFileItemModel is connected with one KDirLister. Each time the KDirLister - * emits new items, removes items or changes items the model gets synchronized. + * Allows to load items of a directory. Sorting and grouping of + * items are supported. Roles that are not part of KFileItem can + * be added with KFileItemModel::setData(). * - * KFileItemModel supports sorting and grouping of items. Additional roles that - * are not part of KFileItem can be added with KFileItemModel::setData(). - * - * Also the recursive expansion of sub-directories is supported by + * Recursive expansion of sub-directories is supported by * KFileItemModel::setExpanded(). - * - * TODO: In the longterm instead of passing a KDirLister just an URL should - * be passed and a KDirLister used internally. This solves the following issues: - * - The user of the API does not need to decide whether he listens to KDirLister - * or KFileItemModel. - * - It resolves minor conceptual differences between KDirLister and KFileItemModel. - * E.g. there is no way for KFileItemModel to check whether a completed() signal - * will be emitted after newItems() will be send by KDirLister or not (in the case - * of setShowingDotFiles() no completed() signal will get emitted). */ class LIBDOLPHINPRIVATE_EXPORT KFileItemModel : public KItemModelBase { Q_OBJECT public: - explicit KFileItemModel(KDirLister* dirLister, QObject* parent = 0); + explicit KFileItemModel(QObject* parent = 0); virtual ~KFileItemModel(); + /** + * Loads the directory specified by \a url. The signals + * directoryLoadingStarted(), directoryLoadingProgress() and directoryLoadingCompleted() + * indicate the current state of the loading process. The items + * of the directory are added after the loading has been completed. + */ + void loadDirectory(const KUrl& url); + + /** + * Throws away all currently loaded items and refreshes the directory + * by reloading all items again. + */ + void refreshDirectory(const KUrl& url); + + /** + * @return Parent directory of the items that are shown. In case + * if a directory tree is shown, KFileItemModel::dir() returns + * the root-parent of all items. + * @see rootItem() + */ + KUrl directory() const; + + /** + * Cancels the loading of a directory which has been started by either + * loadDirectory() or refreshDirectory(). + */ + void cancelDirectoryLoading(); + virtual int count() const; virtual QHash data(int index) const; virtual bool setData(int index, const QHash& values); /** - * Sets a separate sorting with folders first (true) or a mixed sorting of files and folders (false). + * Sets a separate sorting with directories first (true) or a mixed + * sorting of files and directories (false). */ - void setSortFoldersFirst(bool foldersFirst); - bool sortFoldersFirst() const; + void setSortDirectoriesFirst(bool dirsFirst); + bool sortDirectoriesFirst() const; void setShowHiddenFiles(bool show); bool showHiddenFiles() const; /** - * If set to true, only folders are shown as items of the model. Files + * If set to true, only directories are shown as items of the model. Files * are ignored. */ - void setShowFoldersOnly(bool enabled); - bool showFoldersOnly() const; + void setShowDirectoriesOnly(bool enabled); + bool showDirectoriesOnly() const; /** @reimp */ - virtual QMimeData* createMimeData(const QSet& indexes) const; + virtual QMimeData* createMimeData(const KItemSet& indexes) const; /** @reimp */ virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const; @@ -90,7 +109,7 @@ public: virtual bool supportsDropping(int index) const; /** @reimp */ - virtual QString roleDescription(const QByteArray& typeForRole) const; + virtual QString roleDescription(const QByteArray& role) const; /** @reimp */ virtual QList > groups() const; @@ -111,19 +130,20 @@ public: /** * @return The index for the file-item \a item. -1 is returned if no file-item - * is found or if the file-item is null. The runtime + * is found or if the file-item is null. The amortized runtime * complexity of this call is O(1). */ int index(const KFileItem& item) const; /** * @return The index for the URL \a url. -1 is returned if no file-item - * is found. The runtime complexity of this call is O(1). + * is found. The amortized runtime complexity of this call is O(1). */ int index(const KUrl& url) const; /** - * @return Root item of all items. + * @return Root item of all items representing the item + * for KFileItemModel::dir(). */ KFileItem rootItem() const; @@ -132,7 +152,9 @@ public: */ void clear(); - // TODO: "name" + "isDir" is default in ctor + /** + * Sets the roles that should be shown for each item. + */ void setRoles(const QSet& roles); QSet roles() const; @@ -141,32 +163,51 @@ public: virtual bool isExpandable(int index) const; virtual int expandedParentsCount(int index) const; - QSet expandedUrls() const; + QSet expandedDirectories() const; /** - * Marks the URLs in \a urls as subfolders which were expanded previously. - * They are re-expanded one by one each time the KDirLister's completed() signal is received. - * Note that a manual triggering of the KDirLister is required. + * Marks the URLs in \a urls as sub-directories which were expanded previously. + * After calling loadDirectory() or refreshDirectory() the marked sub-directories + * will be expanded step-by-step. */ - void restoreExpandedUrls(const QSet& urls); + void restoreExpandedDirectories(const QSet& urls); /** - * Expands all parent-items of \a url. + * Expands all parent-directories of the item \a url. */ - void expandParentItems(const KUrl& url); + void expandParentDirectories(const KUrl& url); void setNameFilter(const QString& nameFilter); QString nameFilter() const; + void setMimeTypeFilters(const QStringList& filters); + QStringList mimeTypeFilters() const; + struct RoleInfo { QByteArray role; QString translation; QString group; + bool requiresBaloo; + bool requiresIndexer; }; + /** + * @return Provides static information for all available roles that + * are supported by KFileItemModel. Some roles can only be + * determined if Baloo is enabled and/or the Baloo + * indexing is enabled. + */ static QList rolesInformation(); signals: + /** + * Is emitted if the loading of a directory has been started. It is + * assured that a signal directoryLoadingCompleted() will be send after + * the loading has been finished. For tracking the loading progress + * the signal directoryLoadingProgress() gets emitted in between. + */ + void directoryLoadingStarted(); + /** * Is emitted after the loading of a directory has been completed or new * items have been inserted to an already loaded directory. Usually @@ -174,7 +215,49 @@ signals: * (the only exception is loading an empty directory, where only a * loadingCompleted() signal gets emitted). */ - void loadingCompleted(); + void directoryLoadingCompleted(); + + /** + * Is emitted after the loading of a directory has been canceled. + */ + void directoryLoadingCanceled(); + + /** + * Informs about the progress in percent when loading a directory. It is assured + * that the signal directoryLoadingStarted() has been emitted before. + */ + void directoryLoadingProgress(int percent); + + /** + * Is emitted if the sort-role gets resolved asynchronously and provides + * the progress-information of the sorting in percent. It is assured + * that the last sortProgress-signal contains 100 as value. + */ + void directorySortingProgress(int percent); + + /** + * Is emitted if an information message (e.g. "Connecting to host...") + * should be shown. + */ + void infoMessage(const QString& message); + + /** + * Is emitted if an error message (e.g. "Unknown location") + * should be shown. + */ + void errorMessage(const QString& message); + + /** + * Is emitted if a redirection from the current URL \a oldUrl + * to the new URL \a newUrl has been done. + */ + void directoryRedirection(const KUrl& oldUrl, const KUrl& newUrl); + + /** + * Is emitted when the URL passed by KFileItemModel::setUrl() represents a file. + * In this case no signal errorMessage() will be emitted. + */ + void urlIsFileError(const KUrl& url); protected: virtual void onGroupedSortingChanged(bool current); @@ -190,11 +273,10 @@ private slots: void slotCompleted(); void slotCanceled(); - void slotNewItems(const KFileItemList& items); + void slotItemsAdded(const KUrl& directoryUrl, const KFileItemList& items); void slotItemsDeleted(const KFileItemList& items); void slotRefreshItems(const QList >& items); void slotClear(); - void slotClear(const KUrl& url); void slotNaturalSortingChanged(); void dispatchPendingItemsToInsert(); @@ -204,12 +286,12 @@ private: // User visible roles: NoRole, NameRole, SizeRole, DateRole, PermissionsRole, OwnerRole, GroupRole, TypeRole, DestinationRole, PathRole, - // User visible roles available with Nepomuk: + // User visible roles available with Baloo: CommentRole, TagsRole, RatingRole, ImageSizeRole, OrientationRole, WordCountRole, LineCountRole, ArtistRole, AlbumRole, DurationRole, TrackRole, CopiedFromRole, // Non-visible roles: - IsDirRole, IsExpandedRole, IsExpandableRole, ExpandedParentsCountRole, + IsDirRole, IsLinkRole, IsExpandedRole, IsExpandableRole, ExpandedParentsCountRole, // Mandatory last entry: RolesCount }; @@ -221,8 +303,13 @@ private: ItemData* parent; }; - void insertItems(const KFileItemList& items); - void removeItems(const KFileItemList& items); + enum RemoveItemsBehavior { + KeepItemData, + DeleteItemData + }; + + void insertItems(QList& items); + void removeItems(const KItemRangeList& itemRanges, RemoveItemsBehavior behavior); /** * Helper method for insertItems() and removeItems(): Creates @@ -230,10 +317,26 @@ private: * Note that the ItemData instances are created dynamically and * must be deleted by the caller. */ - QList createItemDataList(const KFileItemList& items) const; + QList createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const; + + /** + * Prepares the items for sorting. Normally, the hash 'values' in ItemData is filled + * lazily to save time and memory, but for some sort roles, it is expected that the + * sort role data is stored in 'values'. + */ + void prepareItemsForSorting(QList& itemDataList); + + static int expandedParentsCount(const ItemData* data); void removeExpandedItems(); + /** + * This function is called by setData() and slotRefreshItems(). It emits + * the itemsChanged() signal, checks if the sort order is still correct, + * and starts m_resortAllItemsTimer if that is not the case. + */ + void emitItemsChangedAndTriggerResorting(const KItemRangeList& itemRanges, const QSet& changedRoles); + /** * Resets all values from m_requestRole to false. */ @@ -251,7 +354,13 @@ private: */ QByteArray roleForType(RoleType roleType) const; - QHash retrieveData(const KFileItem& item) const; + QHash retrieveData(const KFileItem& item, const ItemData* parent) const; + + /** + * @return True if \a a has a KFileItem whose text is 'less than' the one + * of \a b according to QString::operator<(const QString&). + */ + static bool nameLessThan(const ItemData* a, const ItemData* b); /** * @return True if the item-data \a a should be ordered before the item-data @@ -259,6 +368,12 @@ private: */ bool lessThan(const ItemData* a, const ItemData* b) const; + /** + * Sorts the items between \a begin and \a end using the comparison + * function lessThan(). + */ + void sort(QList::iterator begin, QList::iterator end) const; + /** * Helper method for lessThan() and expandedParentsCountCompare(): Compares * the passed item-data using m_sortRole as criteria. Both items must @@ -266,48 +381,8 @@ private: */ int sortRoleCompare(const ItemData* a, const ItemData* b) const; - /** - * Sorts the items by using lessThan() as comparison criteria. - * The merge sort algorithm is used to assure a worst-case - * of O(n * log(n)) and to keep the number of comparisons low. - */ - void sort(QList::iterator begin, QList::iterator end); - - /** Helper method for sort(). */ - void merge(QList::iterator begin, - QList::iterator pivot, - QList::iterator end); - - /** Helper method for sort(). */ - QList::iterator lowerBound(QList::iterator begin, - QList::iterator end, - const ItemData* value); - - /** Helper method for sort(). */ - QList::iterator upperBound(QList::iterator begin, - QList::iterator end, - const ItemData* value); - /** Helper method for sort(). */ - void reverse(QList::iterator begin, QList::iterator end); - int stringCompare(const QString& a, const QString& b) const; - /** - * Compares the expansion level of both items. The "expansion level" is defined - * by the number of parent directories. However simply comparing just the numbers - * is not sufficient, it is also important to check the hierarchy for having - * a correct order like shown in a tree. - */ - int expandedParentsCountCompare(const ItemData* a, const ItemData* b) const; - - /** - * Helper method for expandedParentsCountCompare(). - */ - QString subPath(const KFileItem& item, - const QString& itemPath, - int start, - bool* isDir) const; - bool useMaximumUpdateInterval() const; QList > nameRoleGroups() const; @@ -328,9 +403,21 @@ private: bool isChildItem(int index) const; /** - * @return Recursive list of child items that have \a item as upper most parent. + * Is invoked by KFileItemModelRolesUpdater and results in emitting the + * sortProgress signal with a percent-value of the progress. */ - KFileItemList childItems(const KFileItem& item) const; + void emitSortProgress(int resolvedCount); + + /** + * Applies the filters set through @ref setNameFilter and @ref setMimeTypeFilters. + */ + void applyFilters(); + + /** + * Removes filtered items whose expanded parents have been deleted + * or collapsed via setExpanded(parentIndex, false). + */ + void removeFilteredChildren(const KItemRangeList& parents); /** * Maps the QByteArray-roles to RoleTypes and provides translation- and @@ -344,6 +431,8 @@ private: const char* const roleTranslation; const char* const groupTranslationContext; const char* const groupTranslation; + const bool requiresBaloo; + const bool requiresIndexer; }; /** @@ -351,61 +440,82 @@ private: */ static const RoleInfoMap* rolesInfoMap(int& count); + /** + * Determines the MIME-types of all items that can be done within + * the given timeout. + */ + static void determineMimeTypes(const KFileItemList& items, int timeout); + + /** + * @return Returns a copy of \a value that is implicitly shared + * with other users to save memory. + */ + static QByteArray sharedValue(const QByteArray& value); + + /** + * Checks if the model's internal data structures are consistent. + */ + bool isConsistent() const; + private: - QWeakPointer m_dirLister; + KFileItemModelDirLister* m_dirLister; bool m_naturalSorting; - bool m_sortFoldersFirst; + bool m_sortDirsFirst; RoleType m_sortRole; + int m_sortingProgressPercent; // Value of directorySortingProgress() signal QSet m_roles; Qt::CaseSensitivity m_caseSensitivity; QList m_itemData; - QHash m_items; // Allows O(1) access for KFileItemModel::index(const KFileItem& item) + + // m_items is a cache for the method index(const KUrl&). If it contains N + // entries, it is guaranteed that these correspond to the first N items in + // the model, i.e., that (for every i between 0 and N - 1) + // m_items.value(fileItem(i).url()) == i + mutable QHash m_items; KFileItemModelFilter m_filter; - QSet m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter() + QHash m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter() bool m_requestRole[RolesCount]; - QTimer* m_minimumUpdateIntervalTimer; QTimer* m_maximumUpdateIntervalTimer; QTimer* m_resortAllItemsTimer; - KFileItemList m_pendingItemsToInsert; - bool m_pendingEmitLoadingCompleted; + QList m_pendingItemsToInsert; // Cache for KFileItemModel::groups() mutable QList > m_groups; - // Stores the smallest expansion level of the root-URL. Is required to calculate - // the "expandedParentsCount" role in an efficient way. A value < 0 indicates a - // special meaning: - enum ExpandedParentsCountRootTypes - { - // m_expandedParentsCountRoot is uninitialized and must be determined by checking - // the root URL from the KDirLister. - UninitializedExpandedParentsCountRoot = -1, - // All items should be forced to get an expanded parents count of 0 even if they - // represent child items. This is useful for slaves that provide no parent items - // for child items like e.g. the search IO slaves. - ForceExpandedParentsCountRoot = -2 - }; - mutable int m_expandedParentsCountRoot; - - // Stores the URLs of the expanded folders. - QSet m_expandedUrls; + // Stores the URLs (key: target url, value: url) of the expanded directories. + QHash m_expandedDirs; // URLs that must be expanded. The expanding is initially triggered in setExpanded() // and done step after step in slotCompleted(). QSet m_urlsToExpand; - friend class KFileItemModelTest; // For unit testing + friend class KFileItemModelLessThan; // Accesses lessThan() method + friend class KFileItemModelRolesUpdater; // Accesses emitSortProgress() method + friend class KFileItemModelTest; // For unit testing + friend class KFileItemModelBenchmark; // For unit testing + friend class KFileItemListViewTest; // For unit testing + friend class DolphinPart; // Accesses m_dirLister }; +inline bool KFileItemModel::nameLessThan(const ItemData* a, const ItemData* b) +{ + return a->item.text() < b->item.text(); +} + + inline bool KFileItemModel::isChildItem(int index) const { - return m_requestRole[ExpandedParentsCountRole] && m_itemData.at(index)->values.value("expandedParentsCount").toInt() > 0; + if (m_itemData.at(index)->parent) { + return true; + } else { + return false; + } } #endif