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