File indexing completed on 2024-12-15 04:54:46
0001 /* 0002 SPDX-FileCopyrightText: 2009 Kevin Ottens <ervin@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include "messagelist/enums.h" 0010 #include "messagelist/view.h" 0011 #include <QHash> 0012 #include <QList> 0013 #include <QTabWidget> 0014 0015 #include "messagelist_export.h" 0016 #include <Akonadi/Collection> 0017 #include <Akonadi/Item> 0018 #include <KMime/KMimeMessage> 0019 0020 class KXMLGUIClient; 0021 class QAbstractItemModel; 0022 class QItemSelectionModel; 0023 class QItemSelection; 0024 0025 namespace KPIM 0026 { 0027 class MessageStatus; 0028 } 0029 0030 namespace MessageList 0031 { 0032 class Widget; 0033 class StorageModel; 0034 0035 /** 0036 * This is the main MessageList panel for Akonadi applications. 0037 * It contains multiple MessageList::Widget tabs 0038 * so it can actually display multiple folder sets at once. 0039 * 0040 * When a KXmlGuiWindow is passed to setXmlGuiClient, the XMLGUI 0041 * defined context menu @c akonadi_messagelist_contextmenu is 0042 * used if available. 0043 * 0044 */ 0045 class MESSAGELIST_EXPORT Pane : public QTabWidget 0046 { 0047 Q_OBJECT 0048 0049 public: 0050 /** 0051 * Create a Pane wrapping the specified model and selection. 0052 */ 0053 explicit Pane(bool restoreSession, QAbstractItemModel *model, QItemSelectionModel *selectionModel, QWidget *parent = nullptr); 0054 ~Pane() override; 0055 0056 virtual MessageList::StorageModel *createStorageModel(QAbstractItemModel *model, QItemSelectionModel *selectionModel, QObject *parent); 0057 0058 virtual void writeConfig(bool restoreSession); 0059 0060 /** 0061 * Sets the XML GUI client which the pane is used in. 0062 * 0063 * This is needed if you want to use the built-in context menu. 0064 * Passing 0 is ok and will disable the builtin context menu. 0065 * 0066 * @param xmlGuiClient The KXMLGUIClient the view is used in. 0067 */ 0068 void setXmlGuiClient(KXMLGUIClient *xmlGuiClient); 0069 0070 /** 0071 * Returns the current message for the list as Akonadi::Item. 0072 * May return an invalid Item if there is no current message or no current folder. 0073 */ 0074 [[nodiscard]] Akonadi::Item currentItem() const; 0075 0076 /** 0077 * Returns the current message for the list as KMime::Message::Ptr. 0078 * May return 0 if there is no current message or no current folder. 0079 */ 0080 KMime::Message::Ptr currentMessage() const; 0081 0082 /** 0083 * Returns the currently selected KMime::Message::Ptr (bound to current StorageModel). 0084 * The list may be empty if there are no selected messages or no StorageModel. 0085 * 0086 * If includeCollapsedChildren is true then the children of the selected but 0087 * collapsed items are also added to the list. 0088 * 0089 * The returned list is guaranteed to be valid only until you return control 0090 * to the main even loop. Don't store it for any longer. If you need to reference 0091 * this set of messages at a later stage then take a look at createPersistentSet(). 0092 */ 0093 [[nodiscard]] QList<KMime::Message::Ptr> selectionAsMessageList(bool includeCollapsedChildren = true) const; 0094 0095 /** 0096 * Returns the currently selected Items (bound to current StorageModel). 0097 * The list may be empty if there are no selected messages or no StorageModel. 0098 * 0099 * If includeCollapsedChildren is true then the children of the selected but 0100 * collapsed items are also added to the list. 0101 * 0102 * The returned list is guaranteed to be valid only until you return control 0103 * to the main even loop. Don't store it for any longer. If you need to reference 0104 * this set of messages at a later stage then take a look at createPersistentSet(). 0105 */ 0106 [[nodiscard]] Akonadi::Item::List selectionAsMessageItemList(bool includeCollapsedChildren = true) const; 0107 0108 /** 0109 * Returns the currently selected Items id(bound to current StorageModel). 0110 * The list may be empty if there are no selected messages or no StorageModel. 0111 * 0112 * If includeCollapsedChildren is true then the children of the selected but 0113 * collapsed items are also added to the list. 0114 * 0115 * The returned list is guaranteed to be valid only until you return control 0116 * to the main even loop. Don't store it for any longer. If you need to reference 0117 * this set of messages at a later stage then take a look at createPersistentSet(). 0118 */ 0119 [[nodiscard]] QList<qlonglong> selectionAsMessageItemListId(bool includeCollapsedChildren = true) const; 0120 0121 [[nodiscard]] QList<Akonadi::Item::Id> selectionAsListMessageId(bool includeCollapsedChildren = true) const; 0122 0123 /** 0124 * Returns the Akonadi::Item bound to the current StorageModel that 0125 * are part of the current thread. The current thread is the thread 0126 * that contains currentMessageItem(). 0127 * The list may be empty if there is no currentMessageItem() or no StorageModel. 0128 * 0129 * The returned list is guaranteed to be valid only until you return control 0130 * to the main even loop. Don't store it for any longer. If you need to reference 0131 * this set of messages at a later stage then take a look at createPersistentSet(). 0132 */ 0133 [[nodiscard]] Akonadi::Item::List currentThreadAsMessageList() const; 0134 0135 /** 0136 * Selects the next message item in the view. 0137 * 0138 * messageTypeFilter can be used to restrict the selection to only certain message types. 0139 * 0140 * existingSelectionBehaviour specifies how the existing selection 0141 * is manipulated. It may be cleared, expanded or grown/shrunk. 0142 * 0143 * If centerItem is true then the specified item will be positioned 0144 * at the center of the view, if possible. 0145 * If loop is true then the "next" algorithm will restart from the beginning 0146 * of the list if the end is reached, otherwise it will just stop returning false. 0147 */ 0148 [[nodiscard]] bool selectNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, 0149 MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, 0150 bool centerItem, 0151 bool loop); 0152 0153 /** 0154 * Selects the previous message item in the view. 0155 * If centerItem is true then the specified item will be positioned 0156 * at the center of the view, if possible. 0157 * 0158 * messageTypeFilter can be used to restrict the selection to only certain message types. 0159 * 0160 * existingSelectionBehaviour specifies how the existing selection 0161 * is manipulated. It may be cleared, expanded or grown/shrunk. 0162 * 0163 * If loop is true then the "previous" algorithm will restart from the end 0164 * of the list if the beginning is reached, otherwise it will just stop returning false. 0165 */ 0166 [[nodiscard]] bool selectPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, 0167 MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, 0168 bool centerItem, 0169 bool loop); 0170 0171 /** 0172 * Focuses the next message item in the view without actually selecting it. 0173 * 0174 * messageTypeFilter can be used to restrict the selection to only certain message types. 0175 * 0176 * If centerItem is true then the specified item will be positioned 0177 * at the center of the view, if possible. 0178 * If loop is true then the "next" algorithm will restart from the beginning 0179 * of the list if the end is reached, otherwise it will just stop returning false. 0180 */ 0181 [[nodiscard]] bool focusNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop); 0182 0183 /** 0184 * Focuses the previous message item in the view without actually selecting it. 0185 * 0186 * messageTypeFilter can be used to restrict the selection to only certain message types. 0187 * 0188 * If centerItem is true then the specified item will be positioned 0189 * at the center of the view, if possible. 0190 * If loop is true then the "previous" algorithm will restart from the end 0191 * of the list if the beginning is reached, otherwise it will just stop returning false. 0192 */ 0193 [[nodiscard]] bool focusPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop); 0194 0195 /** 0196 * Selects the currently focused message item. May do nothing if the 0197 * focused message item is already selected (which is very likely). 0198 * If centerItem is true then the specified item will be positioned 0199 * at the center of the view, if possible. 0200 */ 0201 void selectFocusedMessageItem(bool centerItem); 0202 0203 /** 0204 * Selects the first message item in the view that matches the specified Core::MessageTypeFilter. 0205 * If centerItem is true then the specified item will be positioned 0206 * at the center of the view, if possible. 0207 * 0208 * If the current view is already loaded then the request will 0209 * be satisfied immediately (well... if an unread message exists at all). 0210 * If the current view is still loading then the selection of the first 0211 * message will be scheduled to be executed when loading terminates. 0212 * 0213 * So this function doesn't actually guarantee that an unread or new message 0214 * was selected when the call returns. Take care :) 0215 * 0216 * The function returns true if a message was selected and false otherwise. 0217 */ 0218 [[nodiscard]] bool selectFirstMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem); 0219 0220 /** 0221 * Selects the last message item in the view that matches the specified Core::MessageTypeFilter. 0222 * If centerItem is true then the specified item will be positioned 0223 * at the center of the view, if possible. 0224 * 0225 * The function returns true if a message was selected and false otherwise. 0226 */ 0227 [[nodiscard]] bool selectLastMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem); 0228 0229 /** 0230 * If expand is true then it expands the current thread, otherwise 0231 * collapses it. 0232 */ 0233 void setCurrentThreadExpanded(bool expand); 0234 0235 /** 0236 * If expand is true then it expands all the threads, otherwise 0237 * collapses them. 0238 */ 0239 void setAllThreadsExpanded(bool expand); 0240 0241 /** 0242 * If expand is true then it expands all the groups (only the toplevel 0243 * group item: inner threads are NOT expanded). If expand is false 0244 * then it collapses all the groups. If no grouping is in effect 0245 * then this function does nothing. 0246 */ 0247 void setAllGroupsExpanded(bool expand); 0248 0249 /** 0250 * Sets the focus on the quick search line of the currently active tab. 0251 */ 0252 void focusQuickSearch(const QString &selectedText = QString()); 0253 0254 /** 0255 * Returns the Akonadi::MessageStatus in the current quicksearch field. 0256 */ 0257 [[nodiscard]] QList<Akonadi::MessageStatus> currentFilterStatus() const; 0258 0259 /** 0260 * Returns the search term in the current quicksearch field. 0261 */ 0262 [[nodiscard]] QString currentFilterSearchString() const; 0263 0264 /** 0265 * Returns true if the current Aggregation is threaded, false otherwise 0266 * (or if there is no current Aggregation). 0267 */ 0268 [[nodiscard]] bool isThreaded() const; 0269 0270 /** 0271 * Fast function that determines if the selection is empty 0272 */ 0273 [[nodiscard]] bool selectionEmpty() const; 0274 /** 0275 * Fills the lists of the selected message serial numbers and of the selected+visible ones. 0276 * Returns true if the returned stats are valid (there is a current folder after all) 0277 * and false otherwise. This is called by KMMainWidget in a single place so we optimize by 0278 * making it a single sweep on the selection. 0279 * 0280 * If includeCollapsedChildren is true then the children of the selected but 0281 * collapsed items are also included in the stats 0282 */ 0283 [[nodiscard]] bool getSelectionStats(Akonadi::Item::List &selectedItems, 0284 Akonadi::Item::List &selectedVisibleItems, 0285 bool *allSelectedBelongToSameThread, 0286 bool includeCollapsedChildren = true) const; 0287 /** 0288 * Deletes the persistent set pointed by the specified reference. 0289 * If the set does not exist anymore, nothing happens. 0290 */ 0291 void deletePersistentSet(MessageList::Core::MessageItemSetReference ref); 0292 0293 /** 0294 * If bMark is true this function marks the messages as "about to be removed" 0295 * so they appear dimmer and aren't selectable in the view. 0296 * If bMark is false then this function clears the "about to be removed" state 0297 * for the specified MessageItems. 0298 */ 0299 void markMessageItemsAsAboutToBeRemoved(MessageList::Core::MessageItemSetReference ref, bool bMark); 0300 0301 /** 0302 * Return Akonadi::Item from messageItemReference 0303 */ 0304 [[nodiscard]] Akonadi::Item::List itemListFromPersistentSet(MessageList::Core::MessageItemSetReference ref); 0305 0306 /** 0307 * Return a persistent set from current selection 0308 */ 0309 [[nodiscard]] MessageList::Core::MessageItemSetReference selectionAsPersistentSet(bool includeCollapsedChildren = true) const; 0310 0311 /** 0312 * Return a persistent set from current thread 0313 */ 0314 [[nodiscard]] MessageList::Core::MessageItemSetReference currentThreadAsPersistentSet() const; 0315 /** 0316 * Sets the focus on the view of the currently active tab. 0317 */ 0318 void focusView(); 0319 0320 /** 0321 * Reloads global configuration and eventually reloads all the views. 0322 */ 0323 void reloadGlobalConfiguration(); 0324 0325 /** 0326 * Returns the QItemSelectionModel for the currently displayed tab. 0327 */ 0328 QItemSelectionModel *currentItemSelectionModel(); 0329 0330 /** 0331 * Sets the current folder to be displayed by this Pane. 0332 * If the specified folder is already open in one of the tabs 0333 * then that tab is made current (and no reloading happens). 0334 * If the specified folder is not open yet then behaviour 0335 * depends on the preferEmptyTab value as follows. 0336 * 0337 * @param etmIndex the index for the collection in the EntityTreeModel (source model) 0338 * 0339 * If preferEmptyTab is set to false then the (new) folder is loaded 0340 * in the current tab. If preferEmptyTab is set to true then the (new) folder is 0341 * loaded in the first empty tab (or a new one if there are no empty ones). 0342 * 0343 * Pre-selection is the action of automatically selecting a message just after the folder 0344 * has finished loading. See Model::setStorageModel() for more information. 0345 * 0346 * If overrideLabel is not empty then it's used as the tab text for the 0347 * specified folder. This is useful to signal a particular folder state 0348 * like "loading..." 0349 */ 0350 void setCurrentFolder(const Akonadi::Collection &fld, 0351 const QModelIndex &etmIndex, 0352 bool preferEmptyTab = false, 0353 MessageList::Core::PreSelectionMode preSelectionMode = MessageList::Core::PreSelectLastSelected, 0354 const QString &overrideLabel = QString()); 0355 0356 void resetModelStorage(); 0357 0358 void setPreferEmptyTab(bool emptyTab); 0359 0360 void updateTabIconText(const Akonadi::Collection &collection, const QString &label, const QIcon &icon); 0361 0362 void saveCurrentSelection(); 0363 0364 void updateTagComboBox(); 0365 0366 bool searchEditHasFocus() const; 0367 0368 void setQuickSearchClickMessage(const QString &msg); 0369 0370 void populateStatusFilterCombo(); 0371 0372 Core::QuickSearchLine::SearchOptions currentOptions() const; 0373 0374 [[nodiscard]] Akonadi::Collection currentFolder() const; 0375 0376 public Q_SLOTS: 0377 /** 0378 * Selects all the items in the current folder. 0379 */ 0380 void selectAll(); 0381 0382 /** 0383 * Add a new tab to the Pane and select it. 0384 */ 0385 QItemSelectionModel *createNewTab(); 0386 0387 void sortOrderMenuAboutToShow(); 0388 0389 void aggregationMenuAboutToShow(); 0390 0391 void themeMenuAboutToShow(); 0392 0393 Q_SIGNALS: 0394 /** 0395 * Emitted when a message is selected (that is, single clicked and thus made current in the view) 0396 * Note that this message CAN be 0 (when the current item is cleared, for example). 0397 * 0398 * This signal is emitted when a SINGLE message is selected in the view, probably 0399 * by clicking on it or by simple keyboard navigation. When multiple items 0400 * are selected at once (by shift+clicking, for example) then you will get 0401 * this signal only for the last clicked message (or at all, if the last shift+clicked 0402 * thing is a group header...). You should handle selection changed in this case. 0403 */ 0404 void messageSelected(const Akonadi::Item &item); 0405 0406 /** 0407 * Emitted when a message is doubleclicked or activated by other input means 0408 */ 0409 void messageActivated(const Akonadi::Item &item); 0410 0411 /** 0412 * Emitted when the selection in the view changes. 0413 */ 0414 void selectionChanged(); 0415 0416 /** 0417 * Emitted when a message wants its status to be changed 0418 */ 0419 void messageStatusChangeRequest(const Akonadi::Item &item, const Akonadi::MessageStatus &set, const Akonadi::MessageStatus &clear); 0420 0421 /** 0422 * Notify the outside when updating the status bar with a message 0423 * could be useful 0424 */ 0425 void statusMessage(const QString &message); 0426 0427 /** 0428 * Emitted when the current tab has changed. Clients using the 0429 * selection model from currentItemSelectionModel() should 0430 * ask for it again, as it may be different now. 0431 */ 0432 void currentTabChanged(); 0433 0434 void forceLostFocus(); 0435 0436 private: 0437 MESSAGELIST_NO_EXPORT void restoreHeaderSettings(int index, bool restoreSession); 0438 MESSAGELIST_NO_EXPORT void readConfig(bool restoreSession); 0439 0440 bool eventFilter(QObject *obj, QEvent *event) override; 0441 0442 class PanePrivate; 0443 std::unique_ptr<PanePrivate> const d; 0444 }; 0445 } // namespace MessageList