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 }