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