Messagelib

view.h
1 /******************************************************************************
2  *
3  * SPDX-FileCopyrightText: 2008 Szymon Tomasz Stefanek <[email protected]>
4  *
5  * SPDX-License-Identifier: GPL-2.0-or-later
6  *
7  *******************************************************************************/
8 
9 #pragma once
10 
11 #include <QList>
12 #include <QPoint>
13 #include <QTreeView>
14 #include <QVector>
15 
16 #include <messagelist/enums.h>
17 #include <messagelist/quicksearchline.h>
18 
19 class QMenu;
20 
21 namespace Akonadi
22 {
23 class MessageStatus;
24 }
25 
26 namespace MessageList
27 {
28 namespace Core
29 {
30 using MessageItemSetReference = long;
31 
32 class Aggregation;
33 class Delegate;
34 class Item;
35 class MessageItem;
36 class Model;
37 class Theme;
38 class SortOrder;
39 class StorageModel;
40 class Widget;
41 
42 /**
43  * The MessageList::View is the real display of the message list. It is
44  * based on QTreeView, has a Model that manipulates the underlying message storage
45  * and a Delegate that is responsible of painting the items.
46  */
47 class View : public QTreeView
48 {
49  friend class Model;
50  friend class ModelPrivate;
51  Q_OBJECT
52 public:
53  explicit View(Widget *parent);
54  ~View() override;
55 
56  /**
57  * Returns the Model attached to this View. You probably never need to manipulate
58  * it directly.
59  */
60  Model *model() const;
61 
62  /**
63  * Returns the Delegate attached to this View. You probably never need to manipulate
64  * it directly. Model uses it to obtain size hints.
65  */
66  Delegate *delegate() const;
67 
68  /**
69  * Sets the StorageModel to be displayed in this view. The StorageModel may be 0 (so no content is displayed).
70  * Setting the StorageModel will obviously trigger a view reload.
71  * Be sure to set the Aggregation and the Theme BEFORE calling this function.
72  *
73  * Pre-selection is the action of automatically selecting a message just after the folder
74  * has finished loading. See Model::setStorageModel() for more information.
75  */
76  void setStorageModel(StorageModel *storageModel, PreSelectionMode preSelectionMode = PreSelectLastSelected);
77 
78  /**
79  * Returns the currently displayed StorageModel. May be 0.
80  */
81  StorageModel *storageModel() const;
82 
83  /**
84  * Sets the aggregation for this view.
85  * Does not trigger a reload of the view: you *MUST* trigger it manually.
86  */
87  void setAggregation(const Aggregation *aggregation);
88 
89  /**
90  * Sets the specified theme for this view.
91  * Does not trigger a reload of the view: you *MUST* trigger it manually.
92  */
93  void setTheme(Theme *theme);
94 
95  /**
96  * Sets the specified sort order.
97  * Does not trigger a reload of the view: you *MUST* trigger it manually.
98  */
99  void setSortOrder(const SortOrder *sortOrder);
100 
101  /**
102  * Triggers a reload of the view in order to re-display the current folder.
103  * Call this function after changing the Aggregation or the Theme.
104  */
105  void reload();
106 
107  /**
108  * Returns the current MessageItem (that is bound to current StorageModel).
109  * May return 0 if there is no current message or no current StorageModel.
110  * If the current message item isn't currently selected (so is only focused)
111  * then it's selected when this function is called, unless selectIfNeeded is false.
112  */
113  MessageItem *currentMessageItem(bool selectIfNeeded = true) const;
114 
115  /**
116  * Returns the current Item (that is bound to current StorageModel).
117  * May return 0 if there is no current item or no current StorageModel.
118  * If the current item isn't currently selected (so is only focused)
119  * then it's selected when this function is called.
120  */
121  Item *currentItem() const;
122 
123  /**
124  * Sets the current message item.
125  */
126  void setCurrentMessageItem(MessageItem *it, bool center = false);
127 
128  /**
129  * Returns true if the specified item is currently displayed in the tree
130  * and has all the parents expanded. This means that the user can
131  * see the message (by eventually scrolling the view).
132  */
133  bool isDisplayedWithParentsExpanded(Item *it) const;
134 
135  /**
136  * Makes sure that the specified is currently viewable by the user.
137  * This means that the user can see the message (by eventually scrolling the view).
138  */
139  void ensureDisplayedWithParentsExpanded(Item *it);
140 
141  /**
142  * Returns the currently selected MessageItems (bound to current StorageModel).
143  * The list may be empty if there are no selected messages or no StorageModel.
144  *
145  * If includeCollapsedChildren is true then the children of the selected but
146  * collapsed items are also added to the list.
147  *
148  * The returned list is guaranteed to be valid only until you return control
149  * to the main even loop. Don't store it for any longer. If you need to reference
150  * this set of messages at a later stage then take a look at createPersistentSet().
151  */
152  QVector<MessageItem *> selectionAsMessageItemList(bool includeCollapsedChildren = true) const;
153 
154  /**
155  * Returns the MessageItems bound to the current StorageModel that
156  * are part of the current thread. The current thread is the thread
157  * that contains currentMessageItem().
158  * The list may be empty if there is no currentMessageItem() or no StorageModel.
159  *
160  * The returned list is guaranteed to be valid only until you return control
161  * to the main even loop. Don't store it for any longer. If you need to reference
162  * this set of messages at a later stage then take a look at createPersistentSet().
163  */
164  QVector<MessageItem *> currentThreadAsMessageItemList() const;
165 
166  /**
167  * Fast function that determines if the selection is empty
168  */
169  bool selectionEmpty() const;
170 
171  /**
172  * Selects the specified MessageItems. The current selection is NOT removed.
173  * Use clearSelection() for that purpose.
174  */
175  void selectMessageItems(const QVector<MessageItem *> &list);
176 
177  /**
178  * Creates a persistent set for the specified MessageItems and
179  * returns its reference. Later you can use this reference
180  * to retrieve the list of MessageItems that are still valid.
181  * See persistentSetCurrentMessageList() for that.
182  *
183  * Persistent sets consume resources (both memory and CPU time
184  * while manipulating the view) so be sure to call deletePersistentSet()
185  * when you no longer need it.
186  */
187  MessageItemSetReference createPersistentSet(const QVector<MessageItem *> &items);
188 
189  /**
190  * Returns the list of MessageItems that are still existing in the
191  * set pointed by the specified reference. This list will contain
192  * at most the messages that you have passed to createPersistentSet()
193  * but may contain less (even 0) if these MessageItem object were removed
194  * from the view for some reason.
195  */
196  QList<MessageItem *> persistentSetCurrentMessageItemList(MessageItemSetReference ref);
197 
198  /**
199  * Deletes the persistent set pointed by the specified reference.
200  * If the set does not exist anymore, nothing happens.
201  */
202  void deletePersistentSet(MessageItemSetReference ref);
203 
204  /**
205  * If bMark is true this function marks the messages as "about to be removed"
206  * so they appear dimmer and aren't selectable in the view.
207  * If bMark is false then this function clears the "about to be removed" state
208  * for the specified MessageItems.
209  */
210  void markMessageItemsAsAboutToBeRemoved(const QList<MessageItem *> &items, bool bMark);
211 
212  /**
213  * Returns true if the current Aggregation is threaded, false otherwise
214  * (or if there is no current Aggregation).
215  */
216  bool isThreaded() const;
217 
218  /**
219  * If expand is true then it expands the current thread, otherwise
220  * collapses it.
221  */
222  void setCurrentThreadExpanded(bool expand);
223 
224  /**
225  * If expand is true then it expands all the threads, otherwise
226  * collapses them.
227  */
228  void setAllThreadsExpanded(bool expand);
229 
230  /**
231  * If expand is true then it expands all the groups (only the toplevel
232  * group item: inner threads are NOT expanded). If expand is false
233  * then it collapses all the groups. If no grouping is in effect
234  * then this function does nothing.
235  */
236  void setAllGroupsExpanded(bool expand);
237 
238  /**
239  * Selects the next message item in the view.
240  *
241  * messageTypeFilter can be used to limit the selection to
242  * a certain category of messages.
243  *
244  * existingSelectionBehaviour specifies how the existing selection
245  * is manipulated. It may be cleared, expanded or grown/shrunk.
246  *
247  * If centerItem is true then the specified item will be positioned
248  * at the center of the view, if possible.
249  * If loop is true then the "next" algorithm will restart from the beginning
250  * of the list if the end is reached, otherwise it will just stop returning false.
251  *
252  * \sa MessageList::Core::MessageTypeFilter
253  * \sa MessageList::Core::ExistingSelectionBehaviour
254  */
255  bool selectNextMessageItem(MessageTypeFilter messageTypeFilter, ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop);
256 
257  /**
258  * Selects the previous message item in the view.
259  *
260  * messageTypeFilter can be used to limit the selection to
261  * a certain category of messages.
262  *
263  * existingSelectionBehaviour specifies how the existing selection
264  * is manipulated. It may be cleared, expanded or grown/shrunk.
265  *
266  * If centerItem is true then the specified item will be positioned
267  * at the center of the view, if possible.
268  * If loop is true then the "previous" algorithm will restart from the end
269  * of the list if the beginning is reached, otherwise it will just stop returning false.
270  *
271  * \sa MessageList::Core::MessageTypeFilter
272  * \sa MessageList::Core::ExistingSelectionBehaviour
273  */
274  bool selectPreviousMessageItem(MessageTypeFilter messageTypeFilter, ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop);
275 
276  /**
277  * Focuses the next message item in the view without actually selecting it.
278  *
279  * messageTypeFilter can be used to limit the selection to
280  * a certain category of messages.
281  *
282  * If centerItem is true then the specified item will be positioned
283  * at the center of the view, if possible.
284  * If loop is true then the "next" algorithm will restart from the beginning
285  * of the list if the end is reached, otherwise it will just stop returning false.
286  */
287  bool focusNextMessageItem(MessageTypeFilter messageTypeFilter, bool centerItem, bool loop);
288 
289  /**
290  * Focuses the previous message item in the view without actually selecting it.
291  * If unread is true then focuses the previous unread message item.
292  * If centerItem is true then the specified item will be positioned
293  * at the center of the view, if possible.
294  * If loop is true then the "previous" algorithm will restart from the end
295  * of the list if the beginning is reached, otherwise it will just stop returning false.
296  */
297  bool focusPreviousMessageItem(MessageTypeFilter messageTypeFilter, bool centerItem, bool loop);
298 
299  /**
300  * Selects the currently focused message item. If the currently focused
301  * message is already selected (which is very likely) nothing happens.
302  * If centerItem is true then the specified item will be positioned
303  * at the center of the view, if possible.
304  */
305  void selectFocusedMessageItem(bool centerItem);
306 
307  /**
308  * Selects the first message item in the view that matches messageTypeFilter.
309  * If centerItem is true then the specified item will be positioned
310  * at the center of the view, if possible.
311  */
312  bool selectFirstMessageItem(MessageTypeFilter messageTypeFilter, bool centerItem);
313 
314  /**
315  * Selects the last message item in the view that matches messageTypeFilter.
316  * If centerItem is true then the specified item will be positioned
317  * at the center of the view, if possible.
318  */
319  bool selectLastMessageItem(MessageTypeFilter messageTypeFilter, bool centerItem);
320 
321  /**
322  * Sets the focus on the quick search line of the currently active tab.
323  */
324  void focusQuickSearch(const QString &selectedText);
325 
326  /**
327  * Returns the Akonadi::MessageStatus in the current quicksearch field.
328  */
329  QVector<Akonadi::MessageStatus> currentFilterStatus() const;
330 
331  /**
332  * Returns the search term in the current quicksearch field.
333  */
334  QString currentFilterSearchString() const;
335 
336  /**
337  * Called to hide or show the specified row from the view.
338  * @reimp
339  */
340  virtual void setRowHidden(int row, const QModelIndex &parent, bool hide);
341 
342  void sortOrderMenuAboutToShow(QMenu *menu);
343 
344  void aggregationMenuAboutToShow(QMenu *menu);
345 
346  void themeMenuAboutToShow(QMenu *menu);
347 
348  void setCollapseItem(const QModelIndex &index);
349  void setExpandItem(const QModelIndex &index);
350 
351  void setQuickSearchClickMessage(const QString &msg);
352 
354 
355 protected:
356  /**
357  * Reimplemented in order to catch QHelpEvent
358  */
359  bool event(QEvent *e) override;
360 
361  /**
362  * Reimplemented in order to catch palette, font and style changes
363  */
364  void changeEvent(QEvent *e) override;
365 
366  /**
367  * Reimplemented in order to apply theme column widths on the first show
368  */
369  void showEvent(QShowEvent *e) override;
370 
371  /**
372  * Reimplemented in order to handle clicks with sub-item precision.
373  */
374  void mousePressEvent(QMouseEvent *e) override;
375 
376  /**
377  * Reimplemented in order to handle double clicks with sub-item precision.
378  */
379  void mouseDoubleClickEvent(QMouseEvent *e) override;
380 
381  /**
382  * Reimplemented in order to handle DnD
383  */
384  void mouseMoveEvent(QMouseEvent *e) override;
385 
386  /**
387  * Reimplemented in order to handle message DnD
388  */
389  void dragEnterEvent(QDragEnterEvent *e) override;
390 
391  /**
392  * Reimplemented in order to handle message DnD
393  */
394  void dragMoveEvent(QDragMoveEvent *e) override;
395 
396  /**
397  * Reimplemented in order to handle message DnD
398  */
399  void dropEvent(QDropEvent *e) override;
400 
401  /**
402  * Reimplemented in order to resize columns when header is not visible
403  */
404  void resizeEvent(QResizeEvent *e) override;
405 
406  void paintEvent(QPaintEvent *event) override;
407  /**
408  * Reimplemented in order to kill the QTreeView column auto-resizing
409  */
410  int sizeHintForColumn(int logicalColumnIndex) const override;
411 
412  /**
413  * Reimplemented in order to disable update of the geometries
414  * while a job step is running (as it takes a very long time and it's called for every item insertion...)
415  * TODO: not true anymore, it's called after a delay.
416  */
417  void updateGeometries() override;
418 
419  /**
420  * Returns true if the vertical scrollbar should keep to the top or bottom
421  * while inserting items.
422  */
423  bool isScrollingLocked() const;
424 
425  /**
426  * Used to enable/disable the ignoring of updateGeometries() calls.
427  */
428  void ignoreUpdateGeometries(bool ignore);
429 
430  void modelAboutToEmitLayoutChanged();
431  void modelEmittedLayoutChanged();
432 
433  /**
434  * This is called by the model to insulate us from certain QTreeView signals
435  * This is because they may be spurious (caused by Model item rearrangements).
436  */
437  void ignoreCurrentChanges(bool ignore);
438 
439  /**
440  * Expands or collapses the children of the specified item, recursively.
441  */
442  void setChildrenExpanded(const Item *parent, bool expand);
443 
444  /**
445  * Finds the next message item with respect to the current item.
446  * If there is no current item then the search starts from the beginning.
447  * Returns 0 if no next message could be found.
448  *
449  * messageTypeFilter can be used to limit the selection to
450  * a certain category of messages.
451  * If loop is true then restarts from the beginning if end is
452  * reached, otherwise it just returns 0 in this case.
453  */
454  Item *nextMessageItem(MessageTypeFilter messageTypeFilter, bool loop);
455 
456  /**
457  * Finds message item that comes "after" the reference item.
458  * If reference item is 0 then the search starts from the beginning.
459  * Returns 0 if no next message could be found.
460  *
461  * messageTypeFilter can be used to limit the selection to
462  * a certain category of messages.
463  * If loop is true then restarts from the beginning if end is
464  * reached, otherwise it just returns 0 in this case.
465  */
466  Item *messageItemAfter(Item *referenceItem, MessageTypeFilter messageTypeFilter, bool loop);
467 
468  /**
469  * Finds the first message item in the view.
470  *
471  * messageTypeFilter can be used to limit the selection to
472  * a certain category of messages.
473  *
474  * Returns 0 if the view is empty.
475  */
476  Item *firstMessageItem(MessageTypeFilter messageTypeFilter);
477 
478  /**
479  * Finds the previous message item with respect to the current item.
480  * If there is no current item then the search starts from the end.
481  * Returns 0 if no previous message could be found.
482  *
483  * messageTypeFilter can be used to limit the selection to
484  * a certain category of messages.
485  * If loop is true then restarts from the end if beginning is
486  * reached, otherwise it just return 0 in this case.
487  */
488  Item *previousMessageItem(MessageTypeFilter messageTypeFilter, bool loop);
489 
490  /**
491  * Returns the deepest child that is visible (i.e. not in a collapsed tree) of
492  * the specified reference item.
493  */
494  Item *deepestExpandedChild(Item *referenceItem) const;
495 
496  /**
497  * Finds message item that comes "before" the reference item.
498  * If reference item is 0 then the search starts from the end.
499  * Returns 0 if no next message could be found.
500  *
501  * messageTypeFilter can be used to limit the selection to
502  * a certain category of messages.
503  * If loop is true then restarts from the beginning if end is
504  * reached, otherwise it just returns 0 in this case.
505  */
506  Item *messageItemBefore(Item *referenceItem, MessageTypeFilter messageTypeFilter, bool loop);
507 
508  /**
509  * Finds the last message item in the view.
510  *
511  * messageTypeFilter can be used to limit the selection to
512  * a certain category of messages.
513  *
514  * Returns nullptr if the view is empty.
515  */
516  Item *lastMessageItem(MessageTypeFilter messageTypeFilter);
517 
518  /**
519  * This is called by Model to signal that the initial loading stage of a newly
520  * attached StorageModel is terminated.
521  */
522  void modelFinishedLoading();
523 
524  /**
525  * Performs a change in the specified MessageItem status.
526  * It first applies the change to the cached state in MessageItem and
527  * then requests our parent widget to act on the storage.
528  */
529  void changeMessageStatus(MessageItem *it, Akonadi::MessageStatus set, Akonadi::MessageStatus unset);
530  void changeMessageStatusRead(MessageItem *it, bool read);
531 
532  /**
533  * Starts a short-delay timer connected to saveThemeColumnState().
534  * Used to accumulate consecutive changes and break out of the call stack
535  * up to the main event loop (since in the call stack the column state might be left undefined).
536  */
537  void triggerDelayedSaveThemeColumnState();
538 
539  /**
540  * Starts a short-delay timer connected to applyThemeColumns().
541  * Used to accumulate consecutive changes and break out of the call stack
542  * up to the main event loop (since multiple resize events tend to be sent by Qt at startup).
543  */
544  void triggerDelayedApplyThemeColumns();
545 
546  /**
547  * This is used by the selection functions to grow/shrink the existing selection
548  * according to the newly selected item passed as parameter.
549  * If movingUp is true then: if the newly selected item is above the current selection top
550  * then the selection is expanded, otherwise it's shrunk. If movingUp is false then: if the
551  * newly selected item is below the current selection bottom then the selection is expanded
552  * otherwise it's shrunk.
553  */
554  void growOrShrinkExistingSelection(const QModelIndex &newSelectedIndex, bool movingUp);
555 
556 public Q_SLOTS:
557  /**
558  * Collapses all the group headers (if present in the current Aggregation)
559  */
560  void slotCollapseAllGroups();
561 
562  /**
563  * Expands all the group headers (if present in the current Aggregation)
564  */
565  void slotExpandAllGroups();
566 
567  /**
568  * Expands the current item.
569  * If it's a Message, it expands its thread, if its a group header it expands the group
570  */
571  void slotExpandCurrentItem();
572 
573  /**
574  * Collapses the current item.
575  * If it's a Message, it collapses its thread, if its a group header it collapses the group
576  */
577  void slotCollapseCurrentItem();
578 
579 protected Q_SLOTS:
580 
581  /**
582  * Handles context menu requests for the header.
583  */
584  void slotHeaderContextMenuRequested(const QPoint &pnt);
585 
586  /**
587  * Handles the actions of the header context menu for showing/hiding a column.
588  */
589  void slotShowHideColumn(int columnIndex);
590 
591  /**
592  * Handles the Adjust Column Sizes action of the header context menu.
593  */
594  void slotAdjustColumnSizes();
595 
596  /**
597  * Handles the Show Default Columns action of the header context menu.
598  */
599  void slotShowDefaultColumns();
600 
601  /**
602  * Handles the Display Tooltips action of the header context menu.
603  */
604  void slotDisplayTooltips(bool showTooltips);
605 
606  /**
607  * Handles section resizes in order to save the column widths
608  */
609  void slotHeaderSectionResized(int logicalIndex, int oldWidth, int newWidth);
610 
611  /**
612  * Handles selection item management
613  */
614  void slotSelectionChanged(const QItemSelection &current, const QItemSelection &);
615 
616  /**
617  * Saves the state of the columns (width and visibility) to the currently selected theme object.
618  */
619  void saveThemeColumnState();
620 
621  /**
622  * Applies the theme columns to this view.
623  * Columns visible by default are shown, the other are hidden.
624  * Visible columns are assigned space inside the view by using the size hints and some heuristics.
625  */
626  void applyThemeColumns();
627 
628 private:
629  class ViewPrivate;
630  std::unique_ptr<ViewPrivate> const d;
631 }; // class View
632 } // namespace Core
633 } // namespace MessageList
634 
A class which holds information about sorting, e.g.
Definition: sortorder.h:22
A set of aggregation options that can be applied to the MessageList::Model in a single shot...
Definition: aggregation.h:28
The MessageItem class.
Definition: messageitem.h:34
Provides a widget which has the messagelist and the most important helper widgets, like the search line and the comboboxes for changing status filtering, aggregation etc.
Definition: widgetbase.h:40
Model
Definition: item.h:37
The MessageList::View is the real display of the message list.
Definition: view.h:47
This class manages the huge tree of displayable objects: GroupHeaderItems and MessageItems.
Definition: model.h:54
PreSelectionMode
Pre-selection is the action of automatically selecting a message just after the folder has finished l...
A single item of the MessageList tree managed by MessageList::Model.
Definition: item.h:35
The QAbstractItemModel based interface that you need to provide for your storage to work with Message...
MessageTypeFilter
This enum is used in the view message selection functions (for instance View::nextMessageItem()).
The Theme class defines the visual appearance of the MessageList.
Definition: theme.h:48
ExistingSelectionBehaviour
This enum is used in the view message selection functions (for instance View::selectNextMessage()) ...
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Dec 5 2021 23:04:55 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.