File indexing completed on 2024-09-22 04:41:03

0001 /*
0002     SPDX-FileCopyrightText: 2008 Stephen Kelly <steveire@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 class KJob;
0010 
0011 #include "collectionfetchjob.h"
0012 #include "item.h"
0013 #include "itemfetchscope.h"
0014 #include "mimetypechecker.h"
0015 
0016 #include "entitytreemodel.h"
0017 
0018 #include "akonaditests_export.h"
0019 
0020 #include <QLoggingCategory>
0021 
0022 Q_DECLARE_LOGGING_CATEGORY(DebugETM)
0023 
0024 namespace Akonadi
0025 {
0026 class Monitor;
0027 class AgentInstance;
0028 }
0029 
0030 struct Node {
0031     using Id = qint64;
0032     enum Type : char {
0033         Item,
0034         Collection,
0035     };
0036 
0037     explicit Node(Type type, Id id, Akonadi::Collection::Id parentId)
0038         : id(id)
0039         , parent(parentId)
0040         , type(type)
0041     {
0042     }
0043 
0044     static bool isItem(Node *node)
0045     {
0046         return node->type == Node::Item;
0047     }
0048 
0049     static bool isCollection(Node *node)
0050     {
0051         return node->type == Node::Collection;
0052     }
0053 
0054     Id id;
0055     Akonadi::Collection::Id parent;
0056     Type type;
0057 };
0058 
0059 template<typename Key, typename Value>
0060 class RefCountedHash
0061 {
0062     mutable Value *defaultValue = nullptr;
0063 
0064 public:
0065     explicit RefCountedHash() = default;
0066     Q_DISABLE_COPY_MOVE(RefCountedHash)
0067 
0068     ~RefCountedHash()
0069     {
0070         delete defaultValue;
0071     }
0072 
0073     inline auto begin()
0074     {
0075         return mHash.begin();
0076     }
0077     inline auto end()
0078     {
0079         return mHash.end();
0080     }
0081     inline auto begin() const
0082     {
0083         return mHash.begin();
0084     }
0085     inline auto end() const
0086     {
0087         return mHash.end();
0088     }
0089     inline auto find(const Key &key) const
0090     {
0091         return mHash.find(key);
0092     }
0093     inline auto find(const Key &key)
0094     {
0095         return mHash.find(key);
0096     }
0097 
0098     inline bool size() const
0099     {
0100         return mHash.size();
0101     }
0102     inline bool isEmpty() const
0103     {
0104         return mHash.isEmpty();
0105     }
0106 
0107     inline void clear()
0108     {
0109         mHash.clear();
0110     }
0111     inline bool contains(const Key &key) const
0112     {
0113         return mHash.contains(key);
0114     }
0115 
0116     inline const Value &value(const Key &key) const
0117     {
0118         auto it = mHash.find(key);
0119         if (it == mHash.end()) {
0120             return defaultValue ? *defaultValue : *(defaultValue = new Value());
0121         }
0122         return it->value;
0123     }
0124 
0125     inline const Value &operator[](const Key &key) const
0126     {
0127         return value(key);
0128     }
0129 
0130     inline auto ref(const Key &key, const Value &value)
0131     {
0132         auto it = mHash.find(key);
0133         if (it != mHash.end()) {
0134             ++(it->refCnt);
0135             return it;
0136         } else {
0137             return mHash.insert(key, {1, std::move(value)});
0138         }
0139     }
0140 
0141     inline void unref(const Key &key)
0142     {
0143         auto it = mHash.find(key);
0144         if (it == mHash.end()) {
0145             return;
0146         }
0147         --(it->refCnt);
0148         if (it->refCnt == 0) {
0149             mHash.erase(it);
0150         }
0151     }
0152 
0153 private:
0154     template<typename V>
0155     struct RefCountedValue {
0156         uint8_t refCnt = 0;
0157         V value;
0158     };
0159     QHash<Key, RefCountedValue<Value>> mHash;
0160 };
0161 
0162 namespace Akonadi
0163 {
0164 /**
0165  * @internal
0166  */
0167 class AKONADI_TESTS_EXPORT EntityTreeModelPrivate
0168 {
0169 public:
0170     explicit EntityTreeModelPrivate(EntityTreeModel *parent);
0171     ~EntityTreeModelPrivate();
0172     EntityTreeModel *const q_ptr;
0173 
0174     enum RetrieveDepth {
0175         Base,
0176         Recursive,
0177     };
0178 
0179     void init(Monitor *monitor);
0180 
0181     void prependNode(Node *node);
0182     void appendNode(Node *node);
0183 
0184     void fetchCollections(const Collection &collection, CollectionFetchJob::Type type = CollectionFetchJob::FirstLevel);
0185     void fetchCollections(const Collection::List &collections, CollectionFetchJob::Type type = CollectionFetchJob::FirstLevel);
0186     void fetchCollections(Akonadi::CollectionFetchJob *job);
0187     void fetchItems(const Collection &collection);
0188     void collectionsFetched(const Akonadi::Collection::List &collections);
0189     void itemsFetched(const Akonadi::Item::List &items);
0190     void itemsFetched(const Collection::Id collectionId, const Akonadi::Item::List &items);
0191 
0192     void monitoredCollectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent);
0193     void monitoredCollectionRemoved(const Akonadi::Collection &collection);
0194     void monitoredCollectionChanged(const Akonadi::Collection &collection);
0195     void monitoredCollectionStatisticsChanged(Akonadi::Collection::Id, const Akonadi::CollectionStatistics &statistics);
0196     void
0197     monitoredCollectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &sourceCollection, const Akonadi::Collection &destCollection);
0198 
0199     void monitoredItemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection);
0200     void monitoredItemRemoved(const Akonadi::Item &item, const Akonadi::Collection &collection = Akonadi::Collection());
0201     void monitoredItemChanged(const Akonadi::Item &item, const QSet<QByteArray> &);
0202     void monitoredItemMoved(const Akonadi::Item &item, const Akonadi::Collection &, const Akonadi::Collection &);
0203 
0204     void monitoredItemLinked(const Akonadi::Item &item, const Akonadi::Collection &);
0205     void monitoredItemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &);
0206 
0207     void monitoredMimeTypeChanged(const QString &mimeType, bool monitored);
0208     void monitoredCollectionsChanged(const Akonadi::Collection &collection, bool monitored);
0209     void monitoredItemsChanged(const Akonadi::Item &item, bool monitored);
0210     void monitoredResourcesChanged(const QByteArray &resource, bool monitored);
0211 
0212     Collection::List getParentCollections(const Item &item) const;
0213     void removeChildEntities(Collection::Id collectionId);
0214 
0215     /**
0216      * Returns the list of names of the child collections of @p collection.
0217      */
0218     QStringList childCollectionNames(const Collection &collection) const;
0219 
0220     /**
0221      * Fetch parent collections and insert this @p collection and its parents into the node tree
0222      *
0223      * Returns whether the ancestor chain was complete and the parent collections were inserted into
0224      * the tree.
0225      */
0226     bool retrieveAncestors(const Akonadi::Collection &collection, bool insertBaseCollection = true);
0227     void ancestorsFetched(const Akonadi::Collection::List &collectionList);
0228     void insertCollection(const Akonadi::Collection &collection, const Akonadi::Collection &parent);
0229 
0230     void beginResetModel();
0231     void endResetModel();
0232     /**
0233      * Start function for filling the Model, finds and fetches the root of the node tree
0234      * Next relevant function for filling the model is startFirstListJob()
0235      */
0236     void fillModel();
0237 
0238     void changeFetchState(const Collection &parent);
0239     void agentInstanceRemoved(const Akonadi::AgentInstance &instance);
0240 
0241     QIcon iconForName(const QString &name) const;
0242 
0243     QHash<Collection::Id, Collection> m_collections;
0244     RefCountedHash<Item::Id, Item> m_items;
0245     QHash<Collection::Id, QList<Node *>> m_childEntities;
0246     QSet<Collection::Id> m_populatedCols;
0247     QSet<Collection::Id> m_collectionsWithoutItems;
0248 
0249     QList<Item::Id> m_pendingCutItems;
0250     QList<Item::Id> m_pendingCutCollections;
0251     mutable QSet<Collection::Id> m_pendingCollectionRetrieveJobs;
0252     mutable QSet<KJob *> m_pendingCollectionFetchJobs;
0253 
0254     // Icon cache to workaround QIcon::fromTheme being very slow (bug #346644)
0255     mutable QHash<QString, QIcon> m_iconCache;
0256     mutable QString m_iconThemeName;
0257 
0258     Monitor *m_monitor = nullptr;
0259     Collection m_rootCollection;
0260     Node *m_rootNode = nullptr;
0261     bool m_needDeleteRootNode = false;
0262     QString m_rootCollectionDisplayName;
0263     QStringList m_mimeTypeFilter;
0264     MimeTypeChecker m_mimeChecker;
0265     EntityTreeModel::CollectionFetchStrategy m_collectionFetchStrategy = EntityTreeModel::FetchCollectionsRecursive;
0266     EntityTreeModel::ItemPopulationStrategy m_itemPopulation = EntityTreeModel::ImmediatePopulation;
0267     CollectionFetchScope::ListFilter m_listFilter = CollectionFetchScope::NoFilter;
0268     bool m_includeStatistics = false;
0269     bool m_showRootCollection = false;
0270     bool m_collectionTreeFetched = false;
0271     bool m_showSystemEntities = false;
0272     Session *m_session = nullptr;
0273     /**
0274      * Called after the root collection was fetched by fillModel
0275      *
0276      * Initiates further fetching of collections depending on the monitored collections
0277      * (in the monitor) and the m_collectionFetchStrategy.
0278      *
0279      * Further collections are either fetched directly with fetchCollections and
0280      * fetchItems or, in case that collections or resources are monitored explicitly
0281      * via fetchTopLevelCollections
0282      */
0283     void startFirstListJob();
0284 
0285     void serverStarted();
0286 
0287     void monitoredItemsRetrieved(KJob *job);
0288     void rootFetchJobDone(KJob *job);
0289     void collectionFetchJobDone(KJob *job);
0290     void itemFetchJobDone(Collection::Id collectionId, KJob *job);
0291     void updateJobDone(KJob *job);
0292     void pasteJobDone(KJob *job);
0293 
0294     /**
0295      * Returns the index of the node in @p list with the id @p id. Returns -1 if not found.
0296      */
0297     template<Node::Type Type>
0298     int indexOf(const QList<Node *> &nodes, Node::Id id) const
0299     {
0300         int i = 0;
0301         for (const Node *node : nodes) {
0302             if (node->id == id && node->type == Type) {
0303                 return i;
0304             }
0305             i++;
0306         }
0307 
0308         return -1;
0309     }
0310 
0311     Q_DECLARE_PUBLIC(EntityTreeModel)
0312 
0313     void fetchTopLevelCollections() const;
0314     void topLevelCollectionsFetched(const Akonadi::Collection::List &collectionList);
0315 
0316     /**
0317       @returns True if @p item or one of its descendants is hidden.
0318     */
0319     bool isHidden(const Item &item) const;
0320     bool isHidden(const Collection &collection) const;
0321 
0322     template<typename T>
0323     bool isHiddenImpl(const T &entity, Node::Type type) const;
0324 
0325     void ref(Collection::Id id);
0326     void deref(Collection::Id id);
0327 
0328     /**
0329      * @returns true if the collection is actively monitored (referenced or buffered with refcounting enabled)
0330      *
0331      * purely for testing
0332      */
0333     bool isMonitored(Collection::Id id) const;
0334 
0335     /**
0336      * @returns true if the collection is buffered
0337      *
0338      * purely for testing
0339      */
0340     bool isBuffered(Collection::Id id) const;
0341 
0342     /**
0343       @returns true if the Collection with the id of @p id should be purged.
0344     */
0345     bool shouldPurge(Collection::Id id) const;
0346 
0347     /**
0348       Purges the items in the Collection @p id
0349     */
0350     void purgeItems(Collection::Id id);
0351 
0352     /**
0353       Removes the items starting from @p it and up to a maximum of @p end in Collection @p col. @p pos should be the index of @p it
0354       in the m_childEntities before calling, and is updated to the position of the next Collection in m_childEntities afterward.
0355       This is required to emit model remove signals properly.
0356 
0357       @returns an iterator pointing to the next Collection after @p it, or at @p end
0358     */
0359     QList<Node *>::iterator removeItems(QList<Node *>::iterator it, const QList<Node *>::iterator &end, int *pos, const Collection &col);
0360 
0361     /**
0362       Skips over Collections in m_childEntities up to a maximum of @p end. @p it is an iterator pointing to the first Collection
0363       in a block of Collections, and @p pos initially describes the index of @p it in m_childEntities and is updated to point to
0364       the index of the next Item in the list.
0365 
0366       @returns an iterator pointing to the next Item after @p it, or at @p end
0367     */
0368     QList<Node *>::iterator skipCollections(QList<Node *>::iterator it, const QList<Node *>::iterator &end, int *pos);
0369 
0370     /**
0371       Emits the data changed signal for the entire row as in the subclass, instead of just for the first column.
0372     */
0373     void dataChanged(const QModelIndex &top, const QModelIndex &bottom);
0374 
0375     /**
0376      * Returns the model index for the given @p collection.
0377      */
0378     QModelIndex indexForCollection(const Collection &collection) const;
0379 
0380     /**
0381      * Returns the model indexes for the given @p item.
0382      */
0383     QModelIndexList indexesForItem(const Item &item) const;
0384 
0385     bool canFetchMore(const QModelIndex &parent) const;
0386 
0387     /**
0388      * Returns true if the collection matches all filters and should be part of the model.
0389      * This method checks all properties that could change by modifying the collection.
0390      * Currently that includes:
0391      * * hidden attribute
0392      * * content mime types
0393      */
0394     bool shouldBePartOfModel(const Collection &collection) const;
0395     bool hasChildCollection(const Collection &collection) const;
0396     bool isAncestorMonitored(const Collection &collection) const;
0397 };
0398 
0399 }