File indexing completed on 2024-12-15 04:54:47
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/QuickSearchLine> 0010 #include <MessageList/View> 0011 #include <MessageList/WidgetBase> 0012 0013 #include <Akonadi/Item> 0014 0015 #include <KMime/KMimeMessage> 0016 0017 #include "messagelist_export.h" 0018 #include <QList> 0019 class KXMLGUIClient; 0020 class QWidget; 0021 class KJob; 0022 namespace MessageList 0023 { 0024 /** 0025 * The Akonadi specific implementation of the Core::Widget. 0026 * 0027 * When a KXmlGuiWindow is passed to setXmlGuiClient, the XMLGUI 0028 * defined context menu @c akonadi_messagelist_contextmenu is 0029 * used if available. 0030 * 0031 */ 0032 class MESSAGELIST_EXPORT Widget : public MessageList::Core::Widget 0033 { 0034 Q_OBJECT 0035 0036 public: 0037 /** 0038 * Create a new message list widget. 0039 */ 0040 explicit Widget(QWidget *parent); 0041 ~Widget() override; 0042 0043 /** 0044 * Sets the XML GUI client which the view is used in. 0045 * 0046 * This is needed if you want to use the built-in context menu. 0047 * Passing 0 is ok and will disable the builtin context menu. 0048 * 0049 * @param xmlGuiClient The KXMLGUIClient the view is used in. 0050 */ 0051 void setXmlGuiClient(KXMLGUIClient *xmlGuiClient); 0052 0053 /** 0054 * Returns the current message for the list as Akonadi::Item. 0055 * May return an invalid Item if there is no current message or no current folder. 0056 */ 0057 [[nodiscard]] Akonadi::Item currentItem() const; 0058 0059 /** 0060 * Returns the current message for the list as KMime::Message::Ptr. 0061 * May return 0 if there is no current message or no current folder. 0062 */ 0063 [[nodiscard]] KMime::Message::Ptr currentMessage() const; 0064 0065 /** 0066 * Returns true if this drag can be accepted by the underlying view 0067 */ 0068 [[nodiscard]] bool canAcceptDrag(const QDropEvent *e); 0069 0070 /** 0071 * Selects the next message item in the view. 0072 * 0073 * messageTypeFilter can be used to restrict the selection to only certain message types. 0074 * 0075 * existingSelectionBehaviour specifies how the existing selection 0076 * is manipulated. It may be cleared, expanded or grown/shrunk. 0077 * 0078 * If centerItem is true then the specified item will be positioned 0079 * at the center of the view, if possible. 0080 * If loop is true then the "next" algorithm will restart from the beginning 0081 * of the list if the end is reached, otherwise it will just stop returning false. 0082 */ 0083 [[nodiscard]] bool selectNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, 0084 MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, 0085 bool centerItem, 0086 bool loop); 0087 0088 /** 0089 * Selects the previous message item in the view. 0090 * If centerItem is true then the specified item will be positioned 0091 * at the center of the view, if possible. 0092 * 0093 * messageTypeFilter can be used to restrict the selection to only certain message types. 0094 * 0095 * existingSelectionBehaviour specifies how the existing selection 0096 * is manipulated. It may be cleared, expanded or grown/shrunk. 0097 * 0098 * If loop is true then the "previous" algorithm will restart from the end 0099 * of the list if the beginning is reached, otherwise it will just stop returning false. 0100 */ 0101 [[nodiscard]] bool selectPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, 0102 MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, 0103 bool centerItem, 0104 bool loop); 0105 0106 /** 0107 * Focuses the next message item in the view without actually selecting it. 0108 * 0109 * messageTypeFilter can be used to restrict the selection to only certain message types. 0110 * 0111 * If centerItem is true then the specified item will be positioned 0112 * at the center of the view, if possible. 0113 * If loop is true then the "next" algorithm will restart from the beginning 0114 * of the list if the end is reached, otherwise it will just stop returning false. 0115 */ 0116 [[nodiscard]] bool focusNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop); 0117 0118 /** 0119 * Focuses the previous message item in the view without actually selecting it. 0120 * 0121 * messageTypeFilter can be used to restrict the selection to only certain message types. 0122 * 0123 * If centerItem is true then the specified item will be positioned 0124 * at the center of the view, if possible. 0125 * If loop is true then the "previous" algorithm will restart from the end 0126 * of the list if the beginning is reached, otherwise it will just stop returning false. 0127 */ 0128 [[nodiscard]] bool focusPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop); 0129 0130 /** 0131 * Selects the currently focused message item. May do nothing if the 0132 * focused message item is already selected (which is very likely). 0133 * If centerItem is true then the specified item will be positioned 0134 * at the center of the view, if possible. 0135 */ 0136 void selectFocusedMessageItem(bool centerItem); 0137 0138 /** 0139 * Selects the first message item in the view that matches the specified Core::MessageTypeFilter. 0140 * If centerItem is true then the specified item will be positioned 0141 * at the center of the view, if possible. 0142 * 0143 * If the current view is already loaded then the request will 0144 * be satisfied immediately (well... if an unread message exists at all). 0145 * If the current view is still loading then the selection of the first 0146 * message will be scheduled to be executed when loading terminates. 0147 * 0148 * So this function doesn't actually guarantee that an unread or new message 0149 * was selected when the call returns. Take care :) 0150 * 0151 * The function returns true if a message was selected and false otherwise. 0152 */ 0153 [[nodiscard]] bool selectFirstMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem); 0154 0155 /** 0156 * Selects the last message item in the view that matches the specified Core::MessageTypeFilter. 0157 * If centerItem is true then the specified item will be positioned 0158 * at the center of the view, if possible. 0159 * 0160 * The function returns true if a message was selected and false otherwise. 0161 */ 0162 [[nodiscard]] bool selectLastMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem); 0163 0164 /** 0165 * Selects all the items in the current folder. 0166 */ 0167 void selectAll(); 0168 /** 0169 * If expand is true then it expands the current thread, otherwise 0170 * collapses it. 0171 */ 0172 void setCurrentThreadExpanded(bool expand); 0173 0174 /** 0175 * If expand is true then it expands all the threads, otherwise 0176 * collapses them. 0177 */ 0178 void setAllThreadsExpanded(bool expand); 0179 0180 /** 0181 * If expand is true then it expands all the groups (only the toplevel 0182 * group item: inner threads are NOT expanded). If expand is false 0183 * then it collapses all the groups. If no grouping is in effect 0184 * then this function does nothing. 0185 */ 0186 void setAllGroupsExpanded(bool expand); 0187 0188 /** 0189 * Sets the focus on the quick search line of the currently active tab. 0190 */ 0191 void focusQuickSearch(const QString &selectedText); 0192 0193 /** 0194 * Returns the currently selected KMime::Message::Ptr (bound to current StorageModel). 0195 * The list may be empty if there are no selected messages or no StorageModel. 0196 * 0197 * If includeCollapsedChildren is true then the children of the selected but 0198 * collapsed items are also added to the list. 0199 * 0200 * The returned list is guaranteed to be valid only until you return control 0201 * to the main even loop. Don't store it for any longer. If you need to reference 0202 * this set of messages at a later stage then take a look at createPersistentSet(). 0203 */ 0204 [[nodiscard]] QList<KMime::Message::Ptr> selectionAsMessageList(bool includeCollapsedChildren = true) const; 0205 0206 /** 0207 * Returns the currently selected Items (bound to current StorageModel). 0208 * The list may be empty if there are no selected messages or no StorageModel. 0209 * 0210 * If includeCollapsedChildren is true then the children of the selected but 0211 * collapsed items are also added to the list. 0212 * 0213 * The returned list is guaranteed to be valid only until you return control 0214 * to the main even loop. Don't store it for any longer. If you need to reference 0215 * this set of messages at a later stage then take a look at createPersistentSet(). 0216 */ 0217 [[nodiscard]] Akonadi::Item::List selectionAsMessageItemList(bool includeCollapsedChildren = true) const; 0218 0219 /** 0220 * Returns the currently selected Items id (bound to current StorageModel). 0221 * The list may be empty if there are no selected messages or no StorageModel. 0222 * 0223 * If includeCollapsedChildren is true then the children of the selected but 0224 * collapsed items are also added to the list. 0225 * 0226 * The returned list is guaranteed to be valid only until you return control 0227 * to the main even loop. Don't store it for any longer. If you need to reference 0228 * this set of messages at a later stage then take a look at createPersistentSet(). 0229 */ 0230 0231 [[nodiscard]] QList<qlonglong> selectionAsMessageItemListId(bool includeCollapsedChildren) const; 0232 [[nodiscard]] QList<Akonadi::Item::Id> selectionAsListMessageId(bool includeCollapsedChildren) const; 0233 0234 /** 0235 * Returns the Akonadi::Item bound to the current StorageModel that 0236 * are part of the current thread. The current thread is the thread 0237 * that contains currentMessageItem(). 0238 * The list may be empty if there is no currentMessageItem() or no StorageModel. 0239 * 0240 * The returned list is guaranteed to be valid only until you return control 0241 * to the main even loop. Don't store it for any longer. If you need to reference 0242 * this set of messages at a later stage then take a look at createPersistentSet(). 0243 */ 0244 [[nodiscard]] Akonadi::Item::List currentThreadAsMessageList() const; 0245 0246 /** 0247 * Returns the Akonadi::MessageStatus in the current quicksearch field. 0248 */ 0249 [[nodiscard]] QList<Akonadi::MessageStatus> currentFilterStatus() const; 0250 0251 /** 0252 * Returns the search term in the current quicksearch field. 0253 */ 0254 [[nodiscard]] QString currentFilterSearchString() const; 0255 0256 /** 0257 * Returns true if the current Aggregation is threaded, false otherwise 0258 * (or if there is no current Aggregation). 0259 */ 0260 [[nodiscard]] bool isThreaded() const; 0261 0262 /** 0263 * Fast function that determines if the selection is empty 0264 */ 0265 [[nodiscard]] bool selectionEmpty() const; 0266 0267 /** 0268 * Fills the lists of the selected message serial numbers and of the selected+visible ones. 0269 * Returns true if the returned stats are valid (there is a current folder after all) 0270 * and false otherwise. This is called by KMMainWidget in a single place so we optimize by 0271 * making it a single sweep on the selection. 0272 * 0273 * If includeCollapsedChildren is true then the children of the selected but 0274 * collapsed items are also included in the stats 0275 */ 0276 0277 [[nodiscard]] bool getSelectionStats(Akonadi::Item::List &selectedSernums, 0278 Akonadi::Item::List &selectedVisibleSernums, 0279 bool *allSelectedBelongToSameThread, 0280 bool includeCollapsedChildren = true) const; 0281 /** 0282 * Deletes the persistent set pointed by the specified reference. 0283 * If the set does not exist anymore, nothing happens. 0284 */ 0285 void deletePersistentSet(MessageList::Core::MessageItemSetReference ref); 0286 0287 /** 0288 * If bMark is true this function marks the messages as "about to be removed" 0289 * so they appear dimmer and aren't selectable in the view. 0290 * If bMark is false then this function clears the "about to be removed" state 0291 * for the specified MessageItems. 0292 */ 0293 void markMessageItemsAsAboutToBeRemoved(MessageList::Core::MessageItemSetReference ref, bool bMark); 0294 0295 /** 0296 * Return Akonadi::Item from messageItemReference 0297 */ 0298 [[nodiscard]] Akonadi::Item::List itemListFromPersistentSet(MessageList::Core::MessageItemSetReference ref); 0299 0300 /** 0301 * Return a persistent set from current selection 0302 */ 0303 [[nodiscard]] MessageList::Core::MessageItemSetReference selectionAsPersistentSet(bool includeCollapsedChildren = true) const; 0304 0305 /** 0306 * Return a persistent set from current thread 0307 */ 0308 [[nodiscard]] MessageList::Core::MessageItemSetReference currentThreadAsPersistentSet() const; 0309 [[nodiscard]] Akonadi::Collection currentCollection() const; 0310 0311 void setQuickSearchClickMessage(const QString &msg); 0312 MessageList::Core::QuickSearchLine::SearchOptions currentOptions() const; 0313 0314 protected: 0315 /** 0316 * Reimplemented from MessageList::Core::Widget 0317 */ 0318 void fillMessageTagCombo() override; 0319 0320 /** 0321 * Reimplemented from MessageList::Core::Widget 0322 */ 0323 void viewMessageSelected(MessageList::Core::MessageItem *msg) override; 0324 0325 /** 0326 * Reimplemented from MessageList::Core::Widget 0327 */ 0328 void viewMessageActivated(MessageList::Core::MessageItem *msg) override; 0329 0330 /** 0331 * Reimplemented from MessageList::Core::Widget 0332 */ 0333 void viewSelectionChanged() override; 0334 0335 /** 0336 * Reimplemented from MessageList::Core::Widget 0337 */ 0338 void viewMessageListContextPopupRequest(const QList<Core::MessageItem *> &selectedItems, const QPoint &globalPos) override; 0339 0340 /** 0341 * Reimplemented from MessageList::Core::Widget 0342 */ 0343 void viewGroupHeaderContextPopupRequest(MessageList::Core::GroupHeaderItem *group, const QPoint &globalPos) override; 0344 0345 /** 0346 * Reimplemented from MessageList::Core::Widget 0347 */ 0348 void viewDragEnterEvent(QDragEnterEvent *e) override; 0349 0350 /** 0351 * Reimplemented from MessageList::Core::Widget 0352 */ 0353 void viewDragMoveEvent(QDragMoveEvent *e) override; 0354 0355 /** 0356 * Reimplemented from MessageList::Core::Widget 0357 */ 0358 void viewDropEvent(QDropEvent *e) override; 0359 0360 /** 0361 * Reimplemented from MessageList::Core::Widget 0362 */ 0363 void viewStartDragRequest() override; 0364 0365 /** 0366 * Reimplemented from MessageList::Core::Widget 0367 */ 0368 void viewMessageStatusChangeRequest(MessageList::Core::MessageItem *msg, Akonadi::MessageStatus set, Akonadi::MessageStatus clear) override; 0369 0370 private Q_SLOTS: 0371 MESSAGELIST_NO_EXPORT void slotCollapseItem(); 0372 MESSAGELIST_NO_EXPORT void slotExpandItem(); 0373 MESSAGELIST_NO_EXPORT void slotTagsFetched(KJob *job); 0374 0375 Q_SIGNALS: 0376 /** 0377 * Emitted when a message is selected (that is, single clicked and thus made current in the view) 0378 * Note that this message CAN be 0 (when the current item is cleared, for example). 0379 * 0380 * This signal is emitted when a SINGLE message is selected in the view, probably 0381 * by clicking on it or by simple keyboard navigation. When multiple items 0382 * are selected at once (by shift+clicking, for example) then you will get 0383 * this signal only for the last clicked message (or at all, if the last shift+clicked 0384 * thing is a group header...). You should handle selection changed in this case. 0385 */ 0386 void messageSelected(const Akonadi::Item &item); 0387 0388 /** 0389 * Emitted when a message is doubleclicked or activated by other input means 0390 */ 0391 void messageActivated(const Akonadi::Item &item); 0392 0393 /** 0394 * Emitted when the selection in the view changes. 0395 */ 0396 void selectionChanged(); 0397 0398 /** 0399 * Emitted when a message wants its status to be changed 0400 */ 0401 void messageStatusChangeRequest(const Akonadi::Item &item, const Akonadi::MessageStatus &set, const Akonadi::MessageStatus &clear); 0402 0403 private: 0404 class WidgetPrivate; 0405 std::unique_ptr<WidgetPrivate> const d; 0406 }; 0407 } // namespace MessageList