File indexing completed on 2025-03-09 04:54:18
0001 /****************************************************************************** 0002 * 0003 * SPDX-FileCopyrightText: 2008 Szymon Tomasz Stefanek <pragma@kvirc.net> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 * 0007 *******************************************************************************/ 0008 0009 #pragma once 0010 0011 #include "model.h" 0012 #include "threadingcache.h" 0013 #include <QTimer> 0014 #include <config-messagelist.h> 0015 0016 class QElapsedTimer; 0017 namespace MessageList 0018 { 0019 namespace Core 0020 { 0021 class ViewItemJob; 0022 class ModelInvariantRowMapper; 0023 class MessageItemSetManager; 0024 class ModelPrivate 0025 { 0026 public: 0027 explicit ModelPrivate(Model *owner) 0028 : q(owner) 0029 { 0030 } 0031 0032 void fillView(); 0033 0034 /** 0035 * This is called by MessageList::Manager once in a while. 0036 * It is a good place to check if the date has changed and 0037 * trigger a view reload. 0038 */ 0039 void checkIfDateChanged(); 0040 0041 void viewItemJobStep(); 0042 0043 /** 0044 * Attempt to find the threading parent for the specified message item. 0045 * Sets the message threading status to the appropriate value. 0046 * 0047 * This function performs In-Reply-To and References threading. 0048 */ 0049 MessageItem *findMessageParent(MessageItem *mi); 0050 /** 0051 * Attempt to find the threading parent for the specified message item. 0052 * Sets the message threading status to the appropriate value. 0053 * 0054 * This function performs Subject based threading. 0055 */ 0056 MessageItem *guessMessageParent(MessageItem *mi); 0057 0058 enum AttachOptions { SkipCacheUpdate = 0, StoreInCache = 1 }; 0059 void attachMessageToParent(Item *pParent, MessageItem *mi, AttachOptions attachOptions = StoreInCache); 0060 void messageDetachedUpdateParentProperties(Item *oldParent, MessageItem *mi); 0061 void attachMessageToGroupHeader(MessageItem *mi); 0062 void attachGroup(GroupHeaderItem *ghi); 0063 0064 enum ViewItemJobResult { 0065 ViewItemJobCompleted, 0066 ViewItemJobInterrupted, 0067 }; 0068 ViewItemJobResult viewItemJobStepInternal(); 0069 ViewItemJobResult viewItemJobStepInternalForJob(ViewItemJob *job, QElapsedTimer elapsedTimer); 0070 0071 // FIXME: Those look like they should be made virtual in some job class! -> Refactor 0072 ViewItemJobResult viewItemJobStepInternalForJobPass1Fill(ViewItemJob *job, QElapsedTimer elapsedTimer); 0073 ViewItemJobResult viewItemJobStepInternalForJobPass1Cleanup(ViewItemJob *job, QElapsedTimer elapsedTimer); 0074 ViewItemJobResult viewItemJobStepInternalForJobPass1Update(ViewItemJob *job, QElapsedTimer elapsedTimer); 0075 ViewItemJobResult viewItemJobStepInternalForJobPass2(ViewItemJob *job, QElapsedTimer elapsedTimer); 0076 ViewItemJobResult viewItemJobStepInternalForJobPass3(ViewItemJob *job, QElapsedTimer elapsedTimer); 0077 ViewItemJobResult viewItemJobStepInternalForJobPass4(ViewItemJob *job, QElapsedTimer elapsedTimer); 0078 ViewItemJobResult viewItemJobStepInternalForJobPass5(ViewItemJob *job, QElapsedTimer elapsedTimer); 0079 void clearJobList(); 0080 void clearUnassignedMessageLists(); 0081 void clearOrphanChildrenHash(); 0082 void clearThreadingCacheReferencesIdMD5ToMessageItem(); 0083 void clearThreadingCacheMessageSubjectMD5ToMessageItem(); 0084 void addMessageToReferencesBasedThreadingCache(MessageItem *mi); 0085 void removeMessageFromReferencesBasedThreadingCache(MessageItem *mi); 0086 void addMessageToSubjectBasedThreadingCache(MessageItem *mi); 0087 void removeMessageFromSubjectBasedThreadingCache(MessageItem *mi); 0088 void clear(); 0089 /** 0090 * Sync the expanded state of the subtree with the specified root. 0091 * This will cause the items that are marked with Item::ExpandNeeded to be 0092 * expanded also in the view. For optimization purposes the specified root 0093 * is assumed to be marked as Item::ExpandNeeded so be sure to check it 0094 * before calling this function. 0095 */ 0096 void syncExpandedStateOfSubtree(Item *root); 0097 /** 0098 * Save the expanded state of the subtree with the specified root. 0099 * The state will be saved in the initialExpandStatus() variable. 0100 * For optimization purposes the specified root is assumed to be expanded 0101 * and viewable. 0102 */ 0103 void saveExpandedStateOfSubtree(Item *root); 0104 0105 #ifdef KDEPIM_FOLDEROPEN_PROFILE 0106 // This prints out all the stats we collected 0107 void printStatistics(); 0108 #endif 0109 0110 enum PropertyChanges { 0111 DateChanged = 1, 0112 MaxDateChanged = (1 << 1), 0113 ActionItemStatusChanged = (1 << 2), 0114 UnreadStatusChanged = (1 << 3), 0115 ImportantStatusChanged = (1 << 4), 0116 AttachmentStatusChanged = (1 << 5), 0117 }; 0118 0119 /** 0120 * Handle the specified property changes in item. Depending on the item 0121 * position inside the parent and the types of item and parent the item 0122 * might need re-grouping or re-sorting. This function takes care of that. 0123 * It is meant to be called from somewhere inside viewItemJobStepInternal() 0124 * as it postpones group updates to Pass5. 0125 * 0126 * parent and item must not be null. propertyChangeMask should not be zero. 0127 * 0128 * Return true if parent might be affected by the item property changes 0129 * and false otherwise. 0130 */ 0131 bool handleItemPropertyChanges(int propertyChangeMask, Item *parent, Item *item); 0132 0133 /** 0134 * This one checks if the parent of item requires an update due to the 0135 * properties of item (that might have been changed or the item might 0136 * have been simply added to the parent). The properties 0137 * are propagated up to the root item. As optimization we ASSUME that 0138 * the item->parent() exists (is non 0) and is NOT the root item. 0139 * Be sure to check it before calling this function (it will assert in debug mode anyway). 0140 * ... ah... and don't be afraid: this is NOT (directly) recursive :) 0141 */ 0142 void propagateItemPropertiesToParent(Item *item); 0143 0144 /** 0145 * Recursively applies the current filter to the tree originating at the specified item. 0146 * The item is hidden if the filter doesn't match (the item or any children of it) 0147 * and this function returns false. 0148 * If the filter matches somewhere in the subtree then the item isn't hidden 0149 * and this function returns true. 0150 * 0151 * Assumes that the specified item is viewable. 0152 */ 0153 bool applyFilterToSubtree(Item *item, const QModelIndex &parentIndex); 0154 0155 // Slots connected to the underlying StorageModel. 0156 0157 void slotStorageModelRowsInserted(const QModelIndex &parent, int from, int to); 0158 void slotStorageModelRowsRemoved(const QModelIndex &parent, int from, int to); 0159 void slotStorageModelDataChanged(const QModelIndex &fromIndex, const QModelIndex &toIndex); 0160 void slotStorageModelHeaderDataChanged(Qt::Orientation orientation, int first, int last); 0161 void slotStorageModelLayoutChanged(); 0162 void slotApplyFilter(); 0163 0164 Model *const q; 0165 0166 /** counter to avoid infinite recursions in the setStorageModel() function */ 0167 int mRecursionCounterForReset; 0168 0169 /** 0170 * The currently set storage model: shallow pointer. 0171 */ 0172 StorageModel *mStorageModel = nullptr; 0173 0174 /** 0175 * The currently set aggregation mode: shallow pointer set by Widget 0176 */ 0177 const Aggregation *mAggregation = nullptr; 0178 0179 /** 0180 * The currently used theme: shallow pointer 0181 */ 0182 const Theme *mTheme = nullptr; 0183 0184 /** 0185 * The currently used sort order. Pointer not owned by us, but by the Widget. 0186 */ 0187 const SortOrder *mSortOrder = nullptr; 0188 0189 /** 0190 * The filter to apply on messages. Shallow. Never 0. 0191 */ 0192 const Filter *mFilter = nullptr; 0193 0194 /** 0195 * The timer involved in breaking the "fill" operation in steps 0196 */ 0197 QTimer mFillStepTimer; 0198 0199 /** 0200 * Group Key (usually the label) -> GroupHeaderItem, used to quickly find groups, pointers are shallow copies 0201 */ 0202 QHash<QString, GroupHeaderItem *> mGroupHeaderItemHash; 0203 0204 /** 0205 * Threading cache. 0206 * MessageIdMD5 -> MessageItem, pointers are shallow copies 0207 */ 0208 QHash<QByteArray, MessageItem *> mThreadingCacheMessageIdMD5ToMessageItem; 0209 0210 /** 0211 * Threading cache. 0212 * MessageInReplyToIdMD5 -> MessageItem, pointers are shallow copies 0213 */ 0214 QMultiHash<QByteArray, MessageItem *> mThreadingCacheMessageInReplyToIdMD5ToMessageItem; 0215 0216 /** 0217 * Threading cache. 0218 * ReferencesIdMD5 -> MessageItem, pointers are shallow copies 0219 */ 0220 QHash<QByteArray, QList<MessageItem *> *> mThreadingCacheMessageReferencesIdMD5ToMessageItem; 0221 0222 /** 0223 * Threading cache. 0224 * SubjectMD5 -> MessageItem, pointers are shallow copies 0225 */ 0226 QHash<QByteArray, QList<MessageItem *> *> mThreadingCacheMessageSubjectMD5ToMessageItem; 0227 0228 /** 0229 * List of group headers that either need to be re-sorted or must be removed because empty 0230 */ 0231 QHash<GroupHeaderItem *, GroupHeaderItem *> mGroupHeadersThatNeedUpdate; 0232 0233 /** 0234 * List of unassigned messages, used to handle threading in two passes, pointers are owned! 0235 */ 0236 QList<MessageItem *> mUnassignedMessageListForPass2; 0237 0238 /** 0239 * List of unassigned messages, used to handle threading in two passes, pointers are owned! 0240 */ 0241 QList<MessageItem *> mUnassignedMessageListForPass3; 0242 0243 /** 0244 * List of unassigned messages, used to handle threading in two passes, pointers are owned! 0245 */ 0246 QList<MessageItem *> mUnassignedMessageListForPass4; 0247 0248 /** 0249 * Hash of orphan children used in Pass1Cleanup. 0250 */ 0251 QHash<MessageItem *, MessageItem *> mOrphanChildrenHash; 0252 0253 /** 0254 * Pending fill view jobs, pointers are owned 0255 */ 0256 QList<ViewItemJob *> mViewItemJobs; 0257 0258 /** 0259 * The today's date. Set when the StorageModel is set and thus grouping is performed. 0260 * This is used to put the today's messages in the "Today" group, for instance. 0261 */ 0262 QDate mTodayDate; 0263 0264 /** 0265 * Owned invisible root item, useful to implement algorithms that not need 0266 * to handle the special case of parentless items. This is never 0. 0267 */ 0268 Item *mRootItem = nullptr; 0269 0270 /** 0271 * The view we're attached to. Shallow pointer (the View owns us). 0272 */ 0273 View *mView = nullptr; 0274 0275 /** 0276 * The time at the current ViewItemJob step started. Used to compute the time we 0277 * spent inside this step and eventually jump out on timeout. 0278 */ 0279 time_t mViewItemJobStepStartTime; 0280 0281 /** 0282 * The timeout for a single ViewItemJob step 0283 */ 0284 int mViewItemJobStepChunkTimeout; 0285 0286 /** 0287 * The idle time between two ViewItemJob steps 0288 */ 0289 int mViewItemJobStepIdleInterval; 0290 0291 /** 0292 * The number of messages we process at once in a ViewItemJob step without 0293 * checking the timeouts above. 0294 */ 0295 int mViewItemJobStepMessageCheckCount; 0296 0297 /** 0298 * Our mighty ModelInvariantRowMapper: used to workaround an 0299 * issue related to the Model/View architecture. 0300 * 0301 * \sa ModelInvariantRowMapper 0302 */ 0303 ModelInvariantRowMapper *mInvariantRowMapper = nullptr; 0304 0305 /** 0306 * The label for the "Today" group item, cached, so we don't translate it multiple times. 0307 */ 0308 QString mCachedTodayLabel; 0309 0310 /** 0311 * The label for the "Yesterday" group item, cached, so we don't translate it multiple times. 0312 */ 0313 QString mCachedYesterdayLabel; 0314 0315 /** 0316 * The label for the "Unknown" group item, cached, so we don't translate it multiple times. 0317 */ 0318 QString mCachedUnknownLabel; 0319 0320 /** 0321 * The label for the "Last Week" group item, cached, so we don't translate it multiple times. 0322 */ 0323 QString mCachedLastWeekLabel; 0324 0325 /** 0326 * The label for the "Two Weeks Ago" group item, cached, so we don't translate it multiple times. 0327 */ 0328 QString mCachedTwoWeeksAgoLabel; 0329 0330 /** 0331 * The label for the "Three Weeks Ago" group item, cached, so we don't translate it multiple times. 0332 */ 0333 QString mCachedThreeWeeksAgoLabel; 0334 0335 /** 0336 * The label for the "Four Weeks Ago" group item, cached, so we don't translate it multiple times. 0337 */ 0338 QString mCachedFourWeeksAgoLabel; 0339 0340 /** 0341 * The label for the "Five Weeks Ago" group item, cached, so we don't translate it multiple times. 0342 */ 0343 QString mCachedFiveWeeksAgoLabel; 0344 0345 /** 0346 * Cached bits that we use for fast status checks 0347 */ 0348 qint32 mCachedWatchedOrIgnoredStatusBits; 0349 0350 /** 0351 * The labels for week days names group items, cached, so we don't query QLocale multiple times. 0352 */ 0353 QMap<int, QString> mCachedDayNameLabel; 0354 0355 /* 0356 * The labels for month names group items, cached, so we don't query QLocale multiple times. 0357 */ 0358 QMap<int, QString> mCachedMonthNameLabel; 0359 0360 /** 0361 * Flag signaling a possibly long job batch. This is checked by other 0362 * classes and used to display some kind of "please wait" feedback to the user. 0363 */ 0364 bool mInLengthyJobBatch; 0365 0366 /** 0367 * We need to save the current item before each job step. This is because 0368 * our job may cause items to be reparented (thus removed and read with the current Qt API) 0369 * and QTreeView will loose the current setting. We also use this to force the current 0370 * to a specific item after a cleanup job. 0371 */ 0372 Item *mCurrentItemToRestoreAfterViewItemJobStep = nullptr; 0373 0374 /** 0375 * Set to true in the first large loading job. 0376 * Reset to false when the job finishes. 0377 * 0378 * Please note that this is NOT set for later jobs: only for the first (possibly huge) one. 0379 */ 0380 bool mLoading; 0381 0382 /** 0383 * Pre-selection is the action of automatically selecting a message just after the folder 0384 * has finished loading. We may want to select the message that was selected the last 0385 * time this folder has been open, or we may want to select the first unread message. 0386 * We also may want to do no pre-selection at all (for example, when the user 0387 * starts navigating the view before the pre-selection could actually be made 0388 * and pre-selecting would confuse him). This member holds the option. 0389 * 0390 * See also setStorageModel() and abortMessagePreSelection() 0391 */ 0392 PreSelectionMode mPreSelectionMode; 0393 0394 // Oldest and newest item while loading the model 0395 // Not valid afterwards anymore. Used for pre-selection of the newest/oldest message 0396 MessageItem *mOldestItem = nullptr; 0397 MessageItem *mNewestItem = nullptr; 0398 0399 /** 0400 * The id of the preselected ;essage is "translated" to a message pointer when it's fetched 0401 * from the storage. This message is then selected when it becomes viewable 0402 * (so at the end of the job). 0 if we have no message to select. 0403 * 0404 * See also setStorageModel() and abortMessagePreSelection() 0405 */ 0406 MessageItem *mLastSelectedMessageInFolder = nullptr; 0407 0408 /** 0409 * The "persistent message item sets" are (guess what?) sets of messages 0410 * that can be referenced globally via a persistent id. The MessageItemSetManager 0411 * and this class keep the persistent sets coherent: messages that are deleted 0412 * are automatically removed from all the sets. 0413 * 0414 * Users of this class typically create persistent sets when they start 0415 * an asynchronous job and they query them back on the way or when the job is terminated. 0416 * 0417 * So mPersistentSetManager is in fact the manager for the outstanding "user" jobs. 0418 * 0 if no jobs are pending (so there are no persistent sets at the moment). 0419 */ 0420 MessageItemSetManager *mPersistentSetManager = nullptr; 0421 0422 /** 0423 * This pointer is passed to the Item functions that insert children. 0424 * When we work with disconnected UI this pointer becomes 0. 0425 */ 0426 Model *mModelForItemFunctions = nullptr; 0427 0428 /** 0429 * The cached result of StorageModel::containsOutboundMessages(). 0430 * We access this property at each incoming message and StorageModel::containsOutboundMessages() is 0431 * virtual (so it's always an indirect function call). Caching makes sense. 0432 */ 0433 bool mStorageModelContainsOutboundMessages; 0434 0435 /** 0436 * Vector of signal-slot connections between StorageModel and us 0437 */ 0438 QList<QMetaObject::Connection> mStorageModelConnections; 0439 0440 /** 0441 * Caches child - parent relation based on Akonadi ID and persists the cache 0442 * in a file for each Collection. This allows for very fast reconstruction of 0443 * threading. 0444 */ 0445 ThreadingCache mThreadingCache; 0446 }; 0447 } // namespace Core 0448 } // namespace MessageList