File indexing completed on 2024-12-15 04:54:43
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 <QList> 0012 #include <QPoint> 0013 #include <QTreeView> 0014 0015 #include "messagelist/enums.h" 0016 #include "messagelist/quicksearchline.h" 0017 0018 class QMenu; 0019 0020 namespace Akonadi 0021 { 0022 class MessageStatus; 0023 } 0024 0025 namespace MessageList 0026 { 0027 namespace Core 0028 { 0029 using MessageItemSetReference = long; 0030 0031 class Aggregation; 0032 class Delegate; 0033 class Item; 0034 class MessageItem; 0035 class Model; 0036 class Theme; 0037 class SortOrder; 0038 class StorageModel; 0039 class Widget; 0040 0041 /** 0042 * The MessageList::View is the real display of the message list. It is 0043 * based on QTreeView, has a Model that manipulates the underlying message storage 0044 * and a Delegate that is responsible of painting the items. 0045 */ 0046 class View : public QTreeView 0047 { 0048 friend class Model; 0049 friend class ModelPrivate; 0050 Q_OBJECT 0051 public: 0052 explicit View(Widget *parent); 0053 ~View() override; 0054 0055 /** 0056 * Returns the Model attached to this View. You probably never need to manipulate 0057 * it directly. 0058 */ 0059 Model *model() const; 0060 0061 /** 0062 * Returns the Delegate attached to this View. You probably never need to manipulate 0063 * it directly. Model uses it to obtain size hints. 0064 */ 0065 Delegate *delegate() const; 0066 0067 /** 0068 * Sets the StorageModel to be displayed in this view. The StorageModel may be 0 (so no content is displayed). 0069 * Setting the StorageModel will obviously trigger a view reload. 0070 * Be sure to set the Aggregation and the Theme BEFORE calling this function. 0071 * 0072 * Pre-selection is the action of automatically selecting a message just after the folder 0073 * has finished loading. See Model::setStorageModel() for more information. 0074 */ 0075 void setStorageModel(StorageModel *storageModel, PreSelectionMode preSelectionMode = PreSelectLastSelected); 0076 0077 /** 0078 * Returns the currently displayed StorageModel. May be 0. 0079 */ 0080 StorageModel *storageModel() const; 0081 0082 /** 0083 * Sets the aggregation for this view. 0084 * Does not trigger a reload of the view: you *MUST* trigger it manually. 0085 */ 0086 void setAggregation(const Aggregation *aggregation); 0087 0088 /** 0089 * Sets the specified theme for this view. 0090 * Does not trigger a reload of the view: you *MUST* trigger it manually. 0091 */ 0092 void setTheme(Theme *theme); 0093 0094 /** 0095 * Sets the specified sort order. 0096 * Does not trigger a reload of the view: you *MUST* trigger it manually. 0097 */ 0098 void setSortOrder(const SortOrder *sortOrder); 0099 0100 /** 0101 * Triggers a reload of the view in order to re-display the current folder. 0102 * Call this function after changing the Aggregation or the Theme. 0103 */ 0104 void reload(); 0105 0106 /** 0107 * Returns the current MessageItem (that is bound to current StorageModel). 0108 * May return 0 if there is no current message or no current StorageModel. 0109 * If the current message item isn't currently selected (so is only focused) 0110 * then it's selected when this function is called, unless selectIfNeeded is false. 0111 */ 0112 MessageItem *currentMessageItem(bool selectIfNeeded = true) const; 0113 0114 /** 0115 * Returns the current Item (that is bound to current StorageModel). 0116 * May return 0 if there is no current item or no current StorageModel. 0117 * If the current item isn't currently selected (so is only focused) 0118 * then it's selected when this function is called. 0119 */ 0120 Item *currentItem() const; 0121 0122 /** 0123 * Sets the current message item. 0124 */ 0125 void setCurrentMessageItem(MessageItem *it, bool center = false); 0126 0127 /** 0128 * Returns true if the specified item is currently displayed in the tree 0129 * and has all the parents expanded. This means that the user can 0130 * see the message (by eventually scrolling the view). 0131 */ 0132 bool isDisplayedWithParentsExpanded(Item *it) const; 0133 0134 /** 0135 * Makes sure that the specified is currently viewable by the user. 0136 * This means that the user can see the message (by eventually scrolling the view). 0137 */ 0138 void ensureDisplayedWithParentsExpanded(Item *it); 0139 0140 /** 0141 * Returns the currently selected MessageItems (bound to current StorageModel). 0142 * The list may be empty if there are no selected messages or no StorageModel. 0143 * 0144 * If includeCollapsedChildren is true then the children of the selected but 0145 * collapsed items are also added to the list. 0146 * 0147 * The returned list is guaranteed to be valid only until you return control 0148 * to the main even loop. Don't store it for any longer. If you need to reference 0149 * this set of messages at a later stage then take a look at createPersistentSet(). 0150 */ 0151 QList<MessageItem *> selectionAsMessageItemList(bool includeCollapsedChildren = true) const; 0152 0153 /** 0154 * Returns the MessageItems bound to the current StorageModel that 0155 * are part of the current thread. The current thread is the thread 0156 * that contains currentMessageItem(). 0157 * The list may be empty if there is no currentMessageItem() or no StorageModel. 0158 * 0159 * The returned list is guaranteed to be valid only until you return control 0160 * to the main even loop. Don't store it for any longer. If you need to reference 0161 * this set of messages at a later stage then take a look at createPersistentSet(). 0162 */ 0163 QList<MessageItem *> currentThreadAsMessageItemList() const; 0164 0165 /** 0166 * Fast function that determines if the selection is empty 0167 */ 0168 bool selectionEmpty() const; 0169 0170 /** 0171 * Selects the specified MessageItems. The current selection is NOT removed. 0172 * Use clearSelection() for that purpose. 0173 */ 0174 void selectMessageItems(const QList<MessageItem *> &list); 0175 0176 /** 0177 * Creates a persistent set for the specified MessageItems and 0178 * returns its reference. Later you can use this reference 0179 * to retrieve the list of MessageItems that are still valid. 0180 * See persistentSetCurrentMessageList() for that. 0181 * 0182 * Persistent sets consume resources (both memory and CPU time 0183 * while manipulating the view) so be sure to call deletePersistentSet() 0184 * when you no longer need it. 0185 */ 0186 MessageItemSetReference createPersistentSet(const QList<MessageItem *> &items); 0187 0188 /** 0189 * Returns the list of MessageItems that are still existing in the 0190 * set pointed by the specified reference. This list will contain 0191 * at most the messages that you have passed to createPersistentSet() 0192 * but may contain less (even 0) if these MessageItem object were removed 0193 * from the view for some reason. 0194 */ 0195 [[nodiscard]] QList<MessageItem *> persistentSetCurrentMessageItemList(MessageItemSetReference ref); 0196 0197 /** 0198 * Deletes the persistent set pointed by the specified reference. 0199 * If the set does not exist anymore, nothing happens. 0200 */ 0201 void deletePersistentSet(MessageItemSetReference ref); 0202 0203 /** 0204 * If bMark is true this function marks the messages as "about to be removed" 0205 * so they appear dimmer and aren't selectable in the view. 0206 * If bMark is false then this function clears the "about to be removed" state 0207 * for the specified MessageItems. 0208 */ 0209 void markMessageItemsAsAboutToBeRemoved(const QList<MessageItem *> &items, bool bMark); 0210 0211 /** 0212 * Returns true if the current Aggregation is threaded, false otherwise 0213 * (or if there is no current Aggregation). 0214 */ 0215 bool isThreaded() const; 0216 0217 /** 0218 * If expand is true then it expands the current thread, otherwise 0219 * collapses it. 0220 */ 0221 void setCurrentThreadExpanded(bool expand); 0222 0223 /** 0224 * If expand is true then it expands all the threads, otherwise 0225 * collapses them. 0226 */ 0227 void setAllThreadsExpanded(bool expand); 0228 0229 /** 0230 * If expand is true then it expands all the groups (only the toplevel 0231 * group item: inner threads are NOT expanded). If expand is false 0232 * then it collapses all the groups. If no grouping is in effect 0233 * then this function does nothing. 0234 */ 0235 void setAllGroupsExpanded(bool expand); 0236 0237 /** 0238 * Selects the next message item in the view. 0239 * 0240 * messageTypeFilter can be used to limit the selection to 0241 * a certain category of messages. 0242 * 0243 * existingSelectionBehaviour specifies how the existing selection 0244 * is manipulated. It may be cleared, expanded or grown/shrunk. 0245 * 0246 * If centerItem is true then the specified item will be positioned 0247 * at the center of the view, if possible. 0248 * If loop is true then the "next" algorithm will restart from the beginning 0249 * of the list if the end is reached, otherwise it will just stop returning false. 0250 * 0251 * \sa MessageList::Core::MessageTypeFilter 0252 * \sa MessageList::Core::ExistingSelectionBehaviour 0253 */ 0254 bool selectNextMessageItem(MessageTypeFilter messageTypeFilter, ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop); 0255 0256 /** 0257 * Selects the previous message item in the view. 0258 * 0259 * messageTypeFilter can be used to limit the selection to 0260 * a certain category of messages. 0261 * 0262 * existingSelectionBehaviour specifies how the existing selection 0263 * is manipulated. It may be cleared, expanded or grown/shrunk. 0264 * 0265 * If centerItem is true then the specified item will be positioned 0266 * at the center of the view, if possible. 0267 * If loop is true then the "previous" algorithm will restart from the end 0268 * of the list if the beginning is reached, otherwise it will just stop returning false. 0269 * 0270 * \sa MessageList::Core::MessageTypeFilter 0271 * \sa MessageList::Core::ExistingSelectionBehaviour 0272 */ 0273 bool selectPreviousMessageItem(MessageTypeFilter messageTypeFilter, ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop); 0274 0275 /** 0276 * Focuses the next message item in the view without actually selecting it. 0277 * 0278 * messageTypeFilter can be used to limit the selection to 0279 * a certain category of messages. 0280 * 0281 * If centerItem is true then the specified item will be positioned 0282 * at the center of the view, if possible. 0283 * If loop is true then the "next" algorithm will restart from the beginning 0284 * of the list if the end is reached, otherwise it will just stop returning false. 0285 */ 0286 bool focusNextMessageItem(MessageTypeFilter messageTypeFilter, bool centerItem, bool loop); 0287 0288 /** 0289 * Focuses the previous message item in the view without actually selecting it. 0290 * If unread is true then focuses the previous unread message item. 0291 * If centerItem is true then the specified item will be positioned 0292 * at the center of the view, if possible. 0293 * If loop is true then the "previous" algorithm will restart from the end 0294 * of the list if the beginning is reached, otherwise it will just stop returning false. 0295 */ 0296 bool focusPreviousMessageItem(MessageTypeFilter messageTypeFilter, bool centerItem, bool loop); 0297 0298 /** 0299 * Selects the currently focused message item. If the currently focused 0300 * message is already selected (which is very likely) nothing happens. 0301 * If centerItem is true then the specified item will be positioned 0302 * at the center of the view, if possible. 0303 */ 0304 void selectFocusedMessageItem(bool centerItem); 0305 0306 /** 0307 * Selects the first message item in the view that matches messageTypeFilter. 0308 * If centerItem is true then the specified item will be positioned 0309 * at the center of the view, if possible. 0310 */ 0311 bool selectFirstMessageItem(MessageTypeFilter messageTypeFilter, bool centerItem); 0312 0313 /** 0314 * Selects the last message item in the view that matches messageTypeFilter. 0315 * If centerItem is true then the specified item will be positioned 0316 * at the center of the view, if possible. 0317 */ 0318 bool selectLastMessageItem(MessageTypeFilter messageTypeFilter, bool centerItem); 0319 0320 /** 0321 * Sets the focus on the quick search line of the currently active tab. 0322 */ 0323 void focusQuickSearch(const QString &selectedText); 0324 0325 /** 0326 * Returns the Akonadi::MessageStatus in the current quicksearch field. 0327 */ 0328 QList<Akonadi::MessageStatus> currentFilterStatus() const; 0329 0330 /** 0331 * Returns the search term in the current quicksearch field. 0332 */ 0333 QString currentFilterSearchString() const; 0334 0335 /** 0336 * Called to hide or show the specified row from the view. 0337 * @reimp 0338 */ 0339 virtual void setRowHidden(int row, const QModelIndex &parent, bool hide); 0340 0341 void sortOrderMenuAboutToShow(QMenu *menu); 0342 0343 void aggregationMenuAboutToShow(QMenu *menu); 0344 0345 void themeMenuAboutToShow(QMenu *menu); 0346 0347 void setCollapseItem(const QModelIndex &index); 0348 void setExpandItem(const QModelIndex &index); 0349 0350 void setQuickSearchClickMessage(const QString &msg); 0351 0352 MessageList::Core::QuickSearchLine::SearchOptions currentOptions() const; 0353 0354 protected: 0355 /** 0356 * Reimplemented in order to catch QHelpEvent 0357 */ 0358 bool event(QEvent *e) override; 0359 0360 /** 0361 * Reimplemented in order to catch palette, font and style changes 0362 */ 0363 void changeEvent(QEvent *e) override; 0364 0365 /** 0366 * Reimplemented in order to handle clicks with sub-item precision. 0367 */ 0368 void mousePressEvent(QMouseEvent *e) override; 0369 0370 /** 0371 * Reimplemented in order to handle double clicks with sub-item precision. 0372 */ 0373 void mouseDoubleClickEvent(QMouseEvent *e) override; 0374 0375 /** 0376 * Reimplemented in order to handle DnD 0377 */ 0378 void mouseMoveEvent(QMouseEvent *e) override; 0379 0380 /** 0381 * Reimplemented in order to handle message DnD 0382 */ 0383 void dragEnterEvent(QDragEnterEvent *e) override; 0384 0385 /** 0386 * Reimplemented in order to handle message DnD 0387 */ 0388 void dragMoveEvent(QDragMoveEvent *e) override; 0389 0390 /** 0391 * Reimplemented in order to handle message DnD 0392 */ 0393 void dropEvent(QDropEvent *e) override; 0394 0395 /** 0396 * Reimplemented in order to resize columns when header is not visible 0397 */ 0398 void resizeEvent(QResizeEvent *e) override; 0399 0400 void paintEvent(QPaintEvent *event) override; 0401 /** 0402 * Reimplemented in order to kill the QTreeView column auto-resizing 0403 */ 0404 int sizeHintForColumn(int logicalColumnIndex) const override; 0405 0406 /** 0407 * Reimplemented in order to disable update of the geometries 0408 * while a job step is running (as it takes a very long time and it's called for every item insertion...) 0409 * TODO: not true anymore, it's called after a delay. 0410 */ 0411 void updateGeometries() override; 0412 0413 /** 0414 * Returns true if the vertical scrollbar should keep to the top or bottom 0415 * while inserting items. 0416 */ 0417 bool isScrollingLocked() const; 0418 0419 /** 0420 * Used to enable/disable the ignoring of updateGeometries() calls. 0421 */ 0422 void ignoreUpdateGeometries(bool ignore); 0423 0424 void modelAboutToEmitLayoutChanged(); 0425 void modelEmittedLayoutChanged(); 0426 0427 /** 0428 * This is called by the model to insulate us from certain QTreeView signals 0429 * This is because they may be spurious (caused by Model item rearrangements). 0430 */ 0431 void ignoreCurrentChanges(bool ignore); 0432 0433 /** 0434 * Expands or collapses the children of the specified item, recursively. 0435 */ 0436 void setChildrenExpanded(const Item *parent, bool expand); 0437 0438 /** 0439 * Finds the next message item with respect to the current item. 0440 * If there is no current item then the search starts from the beginning. 0441 * Returns 0 if no next message could be found. 0442 * 0443 * messageTypeFilter can be used to limit the selection to 0444 * a certain category of messages. 0445 * If loop is true then restarts from the beginning if end is 0446 * reached, otherwise it just returns 0 in this case. 0447 */ 0448 Item *nextMessageItem(MessageTypeFilter messageTypeFilter, bool loop); 0449 0450 /** 0451 * Finds message item that comes "after" the reference item. 0452 * If reference item is 0 then the search starts from the beginning. 0453 * Returns 0 if no next message could be found. 0454 * 0455 * messageTypeFilter can be used to limit the selection to 0456 * a certain category of messages. 0457 * If loop is true then restarts from the beginning if end is 0458 * reached, otherwise it just returns 0 in this case. 0459 */ 0460 Item *messageItemAfter(Item *referenceItem, MessageTypeFilter messageTypeFilter, bool loop); 0461 0462 /** 0463 * Finds the first message item in the view. 0464 * 0465 * messageTypeFilter can be used to limit the selection to 0466 * a certain category of messages. 0467 * 0468 * Returns 0 if the view is empty. 0469 */ 0470 Item *firstMessageItem(MessageTypeFilter messageTypeFilter); 0471 0472 /** 0473 * Finds the previous message item with respect to the current item. 0474 * If there is no current item then the search starts from the end. 0475 * Returns 0 if no previous message could be found. 0476 * 0477 * messageTypeFilter can be used to limit the selection to 0478 * a certain category of messages. 0479 * If loop is true then restarts from the end if beginning is 0480 * reached, otherwise it just return 0 in this case. 0481 */ 0482 Item *previousMessageItem(MessageTypeFilter messageTypeFilter, bool loop); 0483 0484 /** 0485 * Returns the deepest child that is visible (i.e. not in a collapsed tree) of 0486 * the specified reference item. 0487 */ 0488 Item *deepestExpandedChild(Item *referenceItem) const; 0489 0490 /** 0491 * Finds message item that comes "before" the reference item. 0492 * If reference item is 0 then the search starts from the end. 0493 * Returns 0 if no next message could be found. 0494 * 0495 * messageTypeFilter can be used to limit the selection to 0496 * a certain category of messages. 0497 * If loop is true then restarts from the beginning if end is 0498 * reached, otherwise it just returns 0 in this case. 0499 */ 0500 Item *messageItemBefore(Item *referenceItem, MessageTypeFilter messageTypeFilter, bool loop); 0501 0502 /** 0503 * Finds the last message item in the view. 0504 * 0505 * messageTypeFilter can be used to limit the selection to 0506 * a certain category of messages. 0507 * 0508 * Returns nullptr if the view is empty. 0509 */ 0510 Item *lastMessageItem(MessageTypeFilter messageTypeFilter); 0511 0512 /** 0513 * This is called by Model to signal that the initial loading stage of a newly 0514 * attached StorageModel is terminated. 0515 */ 0516 void modelFinishedLoading(); 0517 0518 /** 0519 * Performs a change in the specified MessageItem status. 0520 * It first applies the change to the cached state in MessageItem and 0521 * then requests our parent widget to act on the storage. 0522 */ 0523 void changeMessageStatus(MessageItem *it, Akonadi::MessageStatus set, Akonadi::MessageStatus unset); 0524 void changeMessageStatusRead(MessageItem *it, bool read); 0525 0526 /** 0527 * Starts a short-delay timer connected to saveThemeColumnState(). 0528 * Used to accumulate consecutive changes and break out of the call stack 0529 * up to the main event loop (since in the call stack the column state might be left undefined). 0530 */ 0531 void triggerDelayedSaveThemeColumnState(); 0532 0533 /** 0534 * Starts a short-delay timer connected to applyThemeColumns(). 0535 * Used to accumulate consecutive changes and break out of the call stack 0536 * up to the main event loop (since multiple resize events tend to be sent by Qt at startup). 0537 */ 0538 void triggerDelayedApplyThemeColumns(); 0539 0540 /** 0541 * This is used by the selection functions to grow/shrink the existing selection 0542 * according to the newly selected item passed as parameter. 0543 * If movingUp is true then: if the newly selected item is above the current selection top 0544 * then the selection is expanded, otherwise it's shrunk. If movingUp is false then: if the 0545 * newly selected item is below the current selection bottom then the selection is expanded 0546 * otherwise it's shrunk. 0547 */ 0548 void growOrShrinkExistingSelection(const QModelIndex &newSelectedIndex, bool movingUp); 0549 0550 public Q_SLOTS: 0551 /** 0552 * Collapses all the group headers (if present in the current Aggregation) 0553 */ 0554 void slotCollapseAllGroups(); 0555 0556 /** 0557 * Expands all the group headers (if present in the current Aggregation) 0558 */ 0559 void slotExpandAllGroups(); 0560 0561 /** 0562 * Expands the current item. 0563 * If it's a Message, it expands its thread, if its a group header it expands the group 0564 */ 0565 void slotExpandCurrentItem(); 0566 0567 /** 0568 * Collapses the current item. 0569 * If it's a Message, it collapses its thread, if its a group header it collapses the group 0570 */ 0571 void slotCollapseCurrentItem(); 0572 0573 void slotExpandAllThreads(); 0574 0575 void slotCollapseAllThreads(); 0576 0577 protected Q_SLOTS: 0578 0579 /** 0580 * Handles context menu requests for the header. 0581 */ 0582 void slotHeaderContextMenuRequested(const QPoint &pnt); 0583 0584 /** 0585 * Handles the actions of the header context menu for showing/hiding a column. 0586 */ 0587 void slotShowHideColumn(int columnIndex); 0588 0589 /** 0590 * Handles the Adjust Column Sizes action of the header context menu. 0591 */ 0592 void slotAdjustColumnSizes(); 0593 0594 /** 0595 * Handles the Show Default Columns action of the header context menu. 0596 */ 0597 void slotShowDefaultColumns(); 0598 0599 /** 0600 * Handles the Display Tooltips action of the header context menu. 0601 */ 0602 void slotDisplayTooltips(bool showTooltips); 0603 0604 /** 0605 * Handles section resizes in order to save the column widths 0606 */ 0607 void slotHeaderSectionResized(int logicalIndex, int oldWidth, int newWidth); 0608 0609 /** 0610 * Handles selection item management 0611 */ 0612 void slotSelectionChanged(const QItemSelection ¤t, const QItemSelection &); 0613 0614 /** 0615 * Saves the state of the columns (width and visibility) to the currently selected theme object. 0616 */ 0617 void saveThemeColumnState(); 0618 0619 /** 0620 * Applies the theme columns to this view. 0621 * Columns visible by default are shown, the other are hidden. 0622 * Visible columns are assigned space inside the view by using the size hints and some heuristics. 0623 */ 0624 void applyThemeColumns(); 0625 0626 private: 0627 class ViewPrivate; 0628 std::unique_ptr<ViewPrivate> const d; 0629 }; // class View 0630 } // namespace Core 0631 } // namespace MessageList