File indexing completed on 2024-05-12 05:11:13

0001 /*
0002     This file is part of Akonadi Contact.
0003 
0004     Copyright (c) 2009 - 2010 Tobias Koenig <tokoe@kde.org>
0005     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>
0006     SPDX-FileCopyrightText: 2010 Andras Mantia <andras@kdab.com>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-or-later
0009 */
0010 
0011 #include "standardmailactionmanager.h"
0012 
0013 #include "akonadi_mime_debug.h"
0014 #include "emptytrashcommand.h"
0015 #include "markascommand.h"
0016 #include "movetotrashcommand.h"
0017 #include "removeduplicatesjob.h"
0018 #include "specialmailcollections.h"
0019 #include "util_p.h"
0020 
0021 #include <Akonadi/AgentFilterProxyModel>
0022 #include <Akonadi/AgentInstance>
0023 #include <Akonadi/AgentInstanceCreateJob>
0024 #include <Akonadi/AgentManager>
0025 #include <Akonadi/AgentTypeDialog>
0026 #include <Akonadi/CollectionStatistics>
0027 #include <Akonadi/EntityTreeModel>
0028 #include <Akonadi/MimeTypeChecker>
0029 #include <Akonadi/SubscriptionDialog>
0030 
0031 #include "messagestatus.h"
0032 #include <KMime/KMimeMessage>
0033 
0034 #include <KActionCollection>
0035 #include <QAction>
0036 
0037 #include <KLocalizedString>
0038 
0039 #include <QItemSelectionModel>
0040 
0041 using namespace Akonadi;
0042 
0043 class Akonadi::StandardMailActionManagerPrivate
0044 {
0045 public:
0046     StandardMailActionManagerPrivate(KActionCollection *actionCollection, QWidget *parentWidget, StandardMailActionManager *parent)
0047         : mActionCollection(actionCollection)
0048         , mParentWidget(parentWidget)
0049         , mParent(parent)
0050     {
0051         mGenericManager = new StandardActionManager(actionCollection, parentWidget);
0052 
0053         QObject::connect(
0054             mGenericManager,
0055             &StandardActionManager::selectionsChanged,
0056             mParent,
0057             [this](const Collection::List &selectedCollections, const Collection::List &selectedFavoriteCollections, const Item::List &selectedItems) {
0058                 // Optimization: pass along the lists to avoid recalculating them here
0059                 updateActions(selectedCollections, selectedFavoriteCollections, selectedItems);
0060             });
0061 
0062         QObject::connect(mGenericManager, &StandardActionManager::actionStateUpdated, mParent, &StandardMailActionManager::actionStateUpdated);
0063 
0064         mGenericManager->setMimeTypeFilter(QStringList() << KMime::Message::mimeType());
0065         mGenericManager->setCapabilityFilter(QStringList() << QStringLiteral("Resource"));
0066     }
0067 
0068     ~StandardMailActionManagerPrivate()
0069     {
0070         delete mGenericManager;
0071     }
0072 
0073     void updateGenericAllActions()
0074     {
0075         updateGenericAction(StandardActionManager::CreateCollection);
0076         updateGenericAction(StandardActionManager::CopyCollections);
0077         updateGenericAction(StandardActionManager::DeleteCollections);
0078         updateGenericAction(StandardActionManager::SynchronizeCollections);
0079         updateGenericAction(StandardActionManager::CollectionProperties);
0080         updateGenericAction(StandardActionManager::CopyItems);
0081         updateGenericAction(StandardActionManager::Paste);
0082         updateGenericAction(StandardActionManager::DeleteItems);
0083         updateGenericAction(StandardActionManager::ManageLocalSubscriptions);
0084         updateGenericAction(StandardActionManager::AddToFavoriteCollections);
0085         updateGenericAction(StandardActionManager::RemoveFromFavoriteCollections);
0086         updateGenericAction(StandardActionManager::RenameFavoriteCollection);
0087         updateGenericAction(StandardActionManager::CopyCollectionToMenu);
0088         updateGenericAction(StandardActionManager::CopyItemToMenu);
0089         updateGenericAction(StandardActionManager::MoveItemToMenu);
0090         updateGenericAction(StandardActionManager::MoveCollectionToMenu);
0091         updateGenericAction(StandardActionManager::CutItems);
0092         updateGenericAction(StandardActionManager::CutCollections);
0093         updateGenericAction(StandardActionManager::CreateResource);
0094         updateGenericAction(StandardActionManager::DeleteResources);
0095         updateGenericAction(StandardActionManager::ResourceProperties);
0096         updateGenericAction(StandardActionManager::SynchronizeResources);
0097         updateGenericAction(StandardActionManager::ToggleWorkOffline);
0098         updateGenericAction(StandardActionManager::CopyCollectionToDialog);
0099         updateGenericAction(StandardActionManager::MoveCollectionToDialog);
0100         updateGenericAction(StandardActionManager::CopyItemToDialog);
0101         updateGenericAction(StandardActionManager::MoveItemToDialog);
0102         updateGenericAction(StandardActionManager::SynchronizeCollectionsRecursive);
0103         updateGenericAction(StandardActionManager::MoveCollectionsToTrash);
0104         updateGenericAction(StandardActionManager::MoveItemsToTrash);
0105         updateGenericAction(StandardActionManager::RestoreCollectionsFromTrash);
0106         updateGenericAction(StandardActionManager::RestoreItemsFromTrash);
0107         updateGenericAction(StandardActionManager::MoveToTrashRestoreCollection);
0108         updateGenericAction(StandardActionManager::MoveToTrashRestoreCollectionAlternative);
0109         updateGenericAction(StandardActionManager::MoveToTrashRestoreItem);
0110         updateGenericAction(StandardActionManager::MoveToTrashRestoreItemAlternative);
0111         updateGenericAction(StandardActionManager::SynchronizeFavoriteCollections);
0112     }
0113 
0114     void updateGenericAction(StandardActionManager::Type type)
0115     {
0116         switch (type) {
0117         case Akonadi::StandardActionManager::CreateCollection: {
0118             QAction *act = mGenericManager->action(Akonadi::StandardActionManager::CreateCollection);
0119             act->setText(i18n("Add Folder..."));
0120             act->setWhatsThis(i18n("Add a new folder to the currently selected account."));
0121             setHelpText(act, i18n("Add a new folder to the currently selected account."));
0122 
0123             mGenericManager->setContextText(StandardActionManager::CreateCollection, StandardActionManager::DialogTitle, i18nc("@title:window", "New Folder"));
0124 
0125             mGenericManager->setContextText(StandardActionManager::CreateCollection,
0126                                             StandardActionManager::ErrorMessageText,
0127                                             ki18n("Could not create folder: %1"));
0128 
0129             mGenericManager->setContextText(StandardActionManager::CreateCollection, StandardActionManager::ErrorMessageTitle, i18n("Folder creation failed"));
0130             break;
0131         }
0132         case Akonadi::StandardActionManager::CopyCollections:
0133             mGenericManager->setActionText(Akonadi::StandardActionManager::CopyCollections, ki18np("Copy Folder", "Copy %1 Folders"));
0134             mGenericManager->action(Akonadi::StandardActionManager::CopyCollections)->setWhatsThis(i18n("Copy the selected folders to the clipboard."));
0135             setHelpText(mGenericManager->action(Akonadi::StandardActionManager::CopyCollections), i18n("Copy the selected folders to the clipboard."));
0136             mGenericManager->setContextText(StandardActionManager::CollectionProperties,
0137                                             StandardActionManager::DialogTitle,
0138                                             ki18nc("@title:window", "Properties of Folder %1"));
0139             break;
0140         case Akonadi::StandardActionManager::DeleteCollections:
0141             mGenericManager->setActionText(Akonadi::StandardActionManager::DeleteCollections, ki18np("Delete Folder", "Delete %1 Folders"));
0142             mGenericManager->action(Akonadi::StandardActionManager::DeleteCollections)->setWhatsThis(i18n("Delete the selected folders from the account."));
0143             setHelpText(mGenericManager->action(Akonadi::StandardActionManager::DeleteCollections), i18n("Delete the selected folders from the account."));
0144             mGenericManager->setContextText(StandardActionManager::DeleteCollections,
0145                                             StandardActionManager::MessageBoxText,
0146                                             ki18np("Do you really want to delete this folder and all its sub-folders?",
0147                                                    "Do you really want to delete %1 folders and all their sub-folders?"));
0148 
0149             mGenericManager->setContextText(StandardActionManager::DeleteCollections,
0150                                             StandardActionManager::MessageBoxTitle,
0151                                             ki18ncp("@title:window", "Delete folder?", "Delete folders?"));
0152 
0153             mGenericManager->setContextText(StandardActionManager::DeleteCollections,
0154                                             StandardActionManager::ErrorMessageText,
0155                                             ki18n("Could not delete folder: %1"));
0156 
0157             mGenericManager->setContextText(StandardActionManager::DeleteCollections, StandardActionManager::ErrorMessageTitle, i18n("Folder deletion failed"));
0158 
0159             break;
0160         case Akonadi::StandardActionManager::SynchronizeCollections:
0161             mGenericManager->setActionText(Akonadi::StandardActionManager::SynchronizeCollections, ki18np("Update Folder", "Update Folders"));
0162 
0163             mGenericManager->action(Akonadi::StandardActionManager::SynchronizeCollections)->setWhatsThis(i18n("Update the contents of the selected folders."));
0164             setHelpText(mGenericManager->action(Akonadi::StandardActionManager::SynchronizeCollections), i18n("Update the contents of the selected folders."));
0165             break;
0166         case Akonadi::StandardActionManager::CutCollections:
0167             mGenericManager->setActionText(Akonadi::StandardActionManager::CutCollections, ki18np("Cut Folder", "Cut %1 Folders"));
0168             mGenericManager->action(Akonadi::StandardActionManager::CutCollections)->setWhatsThis(i18n("Cut the selected folders from the account."));
0169             setHelpText(mGenericManager->action(Akonadi::StandardActionManager::CutCollections), i18n("Cut the selected folders from the account."));
0170             break;
0171         case Akonadi::StandardActionManager::CollectionProperties:
0172             mGenericManager->action(Akonadi::StandardActionManager::CollectionProperties)->setText(i18n("Folder Properties"));
0173             mGenericManager->action(Akonadi::StandardActionManager::CollectionProperties)
0174                 ->setWhatsThis(i18n("Open a dialog to edit the properties of the selected folder."));
0175             setHelpText(mGenericManager->action(Akonadi::StandardActionManager::CollectionProperties),
0176                         i18n("Open a dialog to edit the properties of the selected folder."));
0177             break;
0178         case Akonadi::StandardActionManager::CopyItems:
0179             mGenericManager->setActionText(Akonadi::StandardActionManager::CopyItems, ki18np("Copy Message", "Copy %1 Messages"));
0180             mGenericManager->action(Akonadi::StandardActionManager::CopyItems)->setWhatsThis(i18n("Copy the selected messages to the clipboard."));
0181             setHelpText(mGenericManager->action(Akonadi::StandardActionManager::CopyItems), i18n("Copy the selected messages to the clipboard."));
0182             break;
0183         case Akonadi::StandardActionManager::DeleteItems:
0184             mGenericManager->setActionText(Akonadi::StandardActionManager::DeleteItems, ki18np("Delete Message", "Delete %1 Messages"));
0185             mGenericManager->action(Akonadi::StandardActionManager::DeleteItems)->setWhatsThis(i18n("Delete the selected messages from the folder."));
0186             setHelpText(mGenericManager->action(Akonadi::StandardActionManager::DeleteItems), i18n("Delete the selected messages from the folder."));
0187             mGenericManager->setContextText(StandardActionManager::DeleteItems,
0188                                             StandardActionManager::MessageBoxText,
0189                                             ki18np("Do you really want to delete the selected message?", "Do you really want to delete %1 messages?"));
0190 
0191             mGenericManager->setContextText(StandardActionManager::DeleteItems,
0192                                             StandardActionManager::MessageBoxTitle,
0193                                             ki18ncp("@title:window", "Delete Message?", "Delete Messages?"));
0194 
0195             mGenericManager->setContextText(StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageText, ki18n("Could not delete message: %1"));
0196 
0197             mGenericManager->setContextText(StandardActionManager::DeleteItems, StandardActionManager::ErrorMessageTitle, i18n("Message deletion failed"));
0198             break;
0199         case Akonadi::StandardActionManager::CutItems:
0200             mGenericManager->setActionText(Akonadi::StandardActionManager::CutItems, ki18np("Cut Message", "Cut %1 Messages"));
0201             mGenericManager->action(Akonadi::StandardActionManager::CutItems)->setWhatsThis(i18n("Cut the selected message from the folder."));
0202             setHelpText(mGenericManager->action(Akonadi::StandardActionManager::CutItems), i18n("Cut the selected message from the folder."));
0203             break;
0204         case Akonadi::StandardActionManager::CreateResource:
0205             mGenericManager->action(Akonadi::StandardActionManager::CreateResource)->setText(i18n("Add &Account..."));
0206             mGenericManager->action(Akonadi::StandardActionManager::CreateResource)
0207                 ->setWhatsThis(i18n("Add a new account<p>"
0208                                     "You will be presented with a dialog where you can select "
0209                                     "the type of the account that shall be added.</p>"));
0210             mGenericManager->setContextText(StandardActionManager::CreateResource, StandardActionManager::DialogTitle, i18nc("@title:window", "Add Account"));
0211 
0212             mGenericManager->setContextText(StandardActionManager::CreateResource,
0213                                             StandardActionManager::ErrorMessageText,
0214                                             ki18n("Could not create account: %1"));
0215 
0216             mGenericManager->setContextText(StandardActionManager::CreateResource, StandardActionManager::ErrorMessageTitle, i18n("Account creation failed"));
0217             break;
0218         case Akonadi::StandardActionManager::DeleteResources:
0219             mGenericManager->setActionText(Akonadi::StandardActionManager::DeleteResources, ki18np("&Delete Account", "&Delete %1 Accounts"));
0220             mGenericManager->action(Akonadi::StandardActionManager::DeleteResources)
0221                 ->setWhatsThis(i18n("Delete the selected accounts<p>"
0222                                     "The currently selected accounts will be deleted, "
0223                                     "along with all the messages they contain.</p>"));
0224             mGenericManager->setContextText(StandardActionManager::DeleteResources,
0225                                             StandardActionManager::MessageBoxText,
0226                                             ki18np("Do you really want to delete this account?", "Do you really want to delete %1 accounts?"));
0227 
0228             mGenericManager->setContextText(StandardActionManager::DeleteResources,
0229                                             StandardActionManager::MessageBoxTitle,
0230                                             ki18ncp("@title:window", "Delete Account?", "Delete Accounts?"));
0231             break;
0232         case Akonadi::StandardActionManager::ResourceProperties:
0233             mGenericManager->action(Akonadi::StandardActionManager::ResourceProperties)->setText(i18n("Account Properties..."));
0234             mGenericManager->action(Akonadi::StandardActionManager::ResourceProperties)
0235                 ->setWhatsThis(i18n("Open a dialog to edit properties of the selected account."));
0236             break;
0237         case Akonadi::StandardActionManager::SynchronizeResources:
0238             mGenericManager->setActionText(Akonadi::StandardActionManager::SynchronizeResources, ki18np("Update Account", "Update %1 Accounts"));
0239             mGenericManager->action(Akonadi::StandardActionManager::SynchronizeResources)
0240                 ->setWhatsThis(i18n("Updates the content of all folders of the selected accounts."));
0241             break;
0242         case Akonadi::StandardActionManager::SynchronizeCollectionsRecursive:
0243             mGenericManager->setActionText(Akonadi::StandardActionManager::SynchronizeCollectionsRecursive,
0244                                            ki18np("Update This Folder and All Its Subfolders", "Update These Folders and All Their Subfolders"));
0245 
0246             mGenericManager->action(Akonadi::StandardActionManager::SynchronizeCollectionsRecursive)
0247                 ->setWhatsThis(i18n("Update the contents of the selected folders and all their subfolders."));
0248             setHelpText(mGenericManager->action(Akonadi::StandardActionManager::SynchronizeCollectionsRecursive),
0249                         i18n("Update the contents of the selected folders and all their subfolders."));
0250             break;
0251         case Akonadi::StandardActionManager::Paste:
0252             mGenericManager->setContextText(StandardActionManager::Paste, StandardActionManager::ErrorMessageText, ki18n("Could not paste message: %1"));
0253 
0254             mGenericManager->setContextText(StandardActionManager::Paste, StandardActionManager::ErrorMessageTitle, i18n("Paste failed"));
0255             break;
0256         default:
0257             break;
0258         }
0259     }
0260 
0261     void setHelpText(QAction *act, const QString &text)
0262     {
0263         act->setStatusTip(text);
0264         act->setToolTip(text);
0265         if (act->whatsThis().isEmpty()) {
0266             act->setWhatsThis(text);
0267         }
0268     }
0269 
0270     void updateActions()
0271     {
0272         const Akonadi::Item::List selectedItems = mGenericManager->selectedItems();
0273         const Akonadi::Collection::List selectedCollections = mGenericManager->selectedCollections();
0274         updateActions(selectedCollections, {}, selectedItems);
0275     }
0276 
0277     void updateActionsFromSelectedItems(const Item::List &selectedItems)
0278     {
0279         const bool itemIsSelected = !selectedItems.isEmpty();
0280         if (itemIsSelected) {
0281             bool allMarkedAsImportant = true;
0282             bool allMarkedAsRead = true;
0283             bool allMarkedAsUnread = true;
0284             bool allMarkedAsActionItem = true;
0285 
0286             for (const Akonadi::Item &item : selectedItems) {
0287                 Akonadi::MessageStatus status;
0288                 status.setStatusFromFlags(item.flags());
0289                 if (!status.isImportant()) {
0290                     allMarkedAsImportant = false;
0291                 }
0292                 if (!status.isRead()) {
0293                     allMarkedAsRead = false;
0294                 } else {
0295                     allMarkedAsUnread = false;
0296                 }
0297                 if (!status.isToAct()) {
0298                     allMarkedAsActionItem = false;
0299                 }
0300             }
0301 
0302             QAction *action = mActions.value(Akonadi::StandardMailActionManager::MarkMailAsRead);
0303             if (action) {
0304                 updateMarkAction(action, allMarkedAsRead);
0305                 action->setEnabled(!allMarkedAsRead);
0306             }
0307 
0308             action = mActions.value(Akonadi::StandardMailActionManager::MarkMailAsUnread);
0309             if (action) {
0310                 updateMarkAction(action, allMarkedAsUnread);
0311                 action->setEnabled(!allMarkedAsUnread);
0312             }
0313 
0314             action = mActions.value(Akonadi::StandardMailActionManager::MarkMailAsImportant);
0315             if (action) {
0316                 action->setCheckable(true);
0317                 updateMarkAction(action, allMarkedAsImportant);
0318                 action->setChecked(allMarkedAsImportant);
0319                 if (allMarkedAsImportant) {
0320                     action->setText(i18n("Remove Important Mark"));
0321                 } else {
0322                     action->setText(i18n("&Mark Message as Important"));
0323                 }
0324                 action->setEnabled(true);
0325             }
0326 
0327             action = mActions.value(Akonadi::StandardMailActionManager::MarkMailAsActionItem);
0328             if (action) {
0329                 action->setCheckable(true);
0330                 updateMarkAction(action, allMarkedAsActionItem);
0331                 action->setChecked(allMarkedAsActionItem);
0332                 if (allMarkedAsActionItem) {
0333                     action->setText(i18n("Remove Action Item Mark"));
0334                 } else {
0335                     action->setText(i18n("&Mark Message as Action Item"));
0336                 }
0337                 action->setEnabled(true);
0338             }
0339         } else {
0340             QAction *action = mActions.value(Akonadi::StandardMailActionManager::MarkMailAsRead);
0341             if (action) {
0342                 action->setEnabled(false);
0343             }
0344 
0345             action = mActions.value(Akonadi::StandardMailActionManager::MarkMailAsImportant);
0346             if (action) {
0347                 action->setEnabled(false);
0348             }
0349 
0350             action = mActions.value(Akonadi::StandardMailActionManager::MarkMailAsActionItem);
0351             if (action) {
0352                 action->setEnabled(false);
0353             }
0354         }
0355     }
0356 
0357     void updateActions(const Collection::List &selectedCollections, const Collection::List &selectedFavoriteCollections, const Item::List &selectedItems)
0358     {
0359         Q_UNUSED(selectedFavoriteCollections)
0360         const bool itemIsSelected = !selectedItems.isEmpty();
0361         const bool collectionIsSelected = !selectedCollections.isEmpty();
0362         updateActionsFromSelectedItems(selectedItems);
0363 
0364         bool enableMarkAllAsRead = false;
0365         bool enableMarkAllAsUnread = false;
0366         bool canDeleteItem = true;
0367         bool isSystemFolder = false;
0368         if (collectionIsSelected) {
0369             for (const Collection &collection : std::as_const(selectedCollections)) {
0370                 if (collection.isValid()) {
0371                     const Akonadi::CollectionStatistics stats = collection.statistics();
0372                     if (!enableMarkAllAsRead) {
0373                         enableMarkAllAsRead = (stats.unreadCount() > 0);
0374                     }
0375                     if (!enableMarkAllAsUnread) {
0376                         enableMarkAllAsUnread = (stats.count() != stats.unreadCount());
0377                     }
0378                     if (canDeleteItem) {
0379                         canDeleteItem = collection.rights() & Akonadi::Collection::CanDeleteItem;
0380                     }
0381                     if (!isSystemFolder) {
0382                         isSystemFolder = (collection == SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Inbox)
0383                                           || collection == SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Outbox)
0384                                           || collection == SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::SentMail)
0385                                           || collection == SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Trash)
0386                                           || collection == SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Drafts)
0387                                           || collection == SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Templates));
0388                     }
0389                     // We will not change after that.
0390                     if (enableMarkAllAsRead && enableMarkAllAsUnread && !canDeleteItem && isSystemFolder) {
0391                         break;
0392                     }
0393                 }
0394             }
0395         }
0396         QAction *action = nullptr;
0397         if (isSystemFolder) {
0398             action = mGenericManager->action(StandardActionManager::DeleteCollections);
0399             if (action) {
0400                 action->setEnabled(false);
0401             }
0402         }
0403 
0404         action = mActions.value(Akonadi::StandardMailActionManager::MoveToTrash);
0405         if (action) {
0406             action->setEnabled(itemIsSelected && canDeleteItem);
0407         }
0408         action = mActions.value(Akonadi::StandardMailActionManager::RemoveDuplicates);
0409         if (action) {
0410             action->setEnabled(canDeleteItem);
0411         }
0412 
0413         action = mActions.value(Akonadi::StandardMailActionManager::MarkAllMailAsRead);
0414         if (action) {
0415             action->setEnabled(enableMarkAllAsRead);
0416         }
0417 
0418         action = mActions.value(Akonadi::StandardMailActionManager::MarkAllMailAsUnread);
0419         if (action) {
0420             action->setEnabled(enableMarkAllAsUnread);
0421         }
0422 
0423         Q_EMIT mParent->actionStateUpdated();
0424     }
0425 
0426     void updateMarkAction(QAction *action, bool allMarked)
0427     {
0428         QByteArray data = action->data().toByteArray();
0429         if (allMarked) {
0430             if (!data.startsWith('!')) {
0431                 data.prepend('!');
0432             }
0433         } else {
0434             if (data.startsWith('!')) {
0435                 data.remove(0, 1);
0436             }
0437         }
0438         action->setData(data);
0439     }
0440 
0441     void markItemsAs(QByteArray typeStr, const Akonadi::Item::List &items, bool checkIntercept = true)
0442     {
0443         qCDebug(AKONADIMIME_LOG) << "Mark mail as: " << typeStr;
0444 
0445         bool invert = false;
0446         if (typeStr.startsWith('!')) {
0447             invert = true;
0448             typeStr.remove(0, 1);
0449         }
0450 
0451         Akonadi::MessageStatus targetStatus;
0452         targetStatus.setStatusFromStr(QLatin1StringView(typeStr));
0453 
0454         StandardMailActionManager::Type type = StandardMailActionManager::MarkMailAsRead;
0455         if (typeStr == "U") {
0456             type = StandardMailActionManager::MarkMailAsUnread;
0457             targetStatus.setRead(true);
0458             invert = true;
0459         } else if (typeStr == "K") {
0460             type = StandardMailActionManager::MarkMailAsActionItem;
0461         } else if (typeStr == "G") {
0462             type = StandardMailActionManager::MarkMailAsImportant;
0463         }
0464 
0465         if (mInterceptedActions.contains(type) && checkIntercept) {
0466             return;
0467         }
0468 
0469         auto command = new MarkAsCommand(targetStatus, items, invert, mParent);
0470         command->execute();
0471     }
0472 
0473     void slotMarkAs()
0474     {
0475         const Akonadi::Item::List items = mGenericManager->selectedItems();
0476         if (items.isEmpty()) {
0477             return;
0478         }
0479         const QAction *action = qobject_cast<QAction *>(mParent->sender());
0480         Q_ASSERT(action);
0481 
0482         const QByteArray typeStr = action->data().toByteArray();
0483 
0484         markItemsAs(typeStr, items, true);
0485     }
0486 
0487     void markAllItemsAs(QByteArray typeStr, const Akonadi::Collection::List &collections, bool checkIntercept = true)
0488     {
0489         qCDebug(AKONADIMIME_LOG) << "Mark all as: " << typeStr;
0490 
0491         Akonadi::MessageStatus targetStatus;
0492         targetStatus.setStatusFromStr(QLatin1StringView(typeStr));
0493 
0494         bool invert = false;
0495         if (typeStr.startsWith('!')) {
0496             invert = true;
0497             typeStr.remove(0, 1);
0498         }
0499         bool recursive = false;
0500         if (typeStr.startsWith(':')) {
0501             recursive = true;
0502             typeStr.remove(0, 1);
0503         }
0504 
0505         StandardMailActionManager::Type type = StandardMailActionManager::MarkAllMailAsRead;
0506         if (typeStr == "U") {
0507             type = StandardMailActionManager::MarkAllMailAsUnread;
0508             targetStatus.setRead(true);
0509             invert = true;
0510         } else if (typeStr == "K") {
0511             type = StandardMailActionManager::MarkAllMailAsActionItem;
0512         } else if (typeStr == "G") {
0513             type = StandardMailActionManager::MarkAllMailAsImportant;
0514         }
0515 
0516         if (mInterceptedActions.contains(type) && checkIntercept) {
0517             return;
0518         }
0519 
0520         auto command = new MarkAsCommand(targetStatus, collections, invert, recursive, mParent);
0521         command->execute();
0522     }
0523 
0524     void slotMarkAllAs()
0525     {
0526         const Akonadi::Collection::List collections = mGenericManager->selectedCollections();
0527         if (collections.isEmpty()) {
0528             return;
0529         }
0530 
0531         const QAction *action = qobject_cast<QAction *>(mParent->sender());
0532         Q_ASSERT(action);
0533         const QByteArray typeStr = action->data().toByteArray();
0534         markAllItemsAs(typeStr, collections, true);
0535     }
0536 
0537     void slotMoveToTrash()
0538     {
0539         if (mInterceptedActions.contains(StandardMailActionManager::MoveToTrash)) {
0540             return;
0541         }
0542 
0543         if (mCollectionSelectionModel->selection().indexes().isEmpty()) {
0544             return;
0545         }
0546 
0547         const Item::List items = mGenericManager->selectedItems();
0548         if (items.isEmpty()) {
0549             return;
0550         }
0551 
0552         auto command = new MoveToTrashCommand(mCollectionSelectionModel->model(), items, mParent);
0553         command->execute();
0554     }
0555 
0556     void slotMoveAllToTrash()
0557     {
0558         if (mInterceptedActions.contains(StandardMailActionManager::MoveAllToTrash)) {
0559             return;
0560         }
0561 
0562         if (mCollectionSelectionModel->selection().indexes().isEmpty()) {
0563             return;
0564         }
0565 
0566         const Collection::List collections = mGenericManager->selectedCollections();
0567         if (collections.isEmpty()) {
0568             return;
0569         }
0570 
0571         auto command = new MoveToTrashCommand(mCollectionSelectionModel->model(), collections, mParent);
0572         command->execute();
0573     }
0574 
0575     void slotRemoveDuplicates()
0576     {
0577         if (mInterceptedActions.contains(StandardMailActionManager::RemoveDuplicates)) {
0578             return;
0579         }
0580 
0581         const Collection::List collections = mGenericManager->selectedCollections();
0582         if (collections.isEmpty()) {
0583             return;
0584         }
0585 
0586         auto job = new RemoveDuplicatesJob(collections, mParent);
0587         QObject::connect(job, &RemoveDuplicatesJob::finished, mParent, [this](KJob *job) {
0588             slotJobFinished(job);
0589         });
0590     }
0591 
0592     void slotJobFinished(KJob *job)
0593     {
0594         if (job->error()) {
0595             Util::showJobError(job);
0596         }
0597     }
0598 
0599     void slotEmptyAllTrash()
0600     {
0601         if (mInterceptedActions.contains(StandardMailActionManager::EmptyAllTrash)) {
0602             return;
0603         }
0604 
0605         auto command = new EmptyTrashCommand(const_cast<QAbstractItemModel *>(mCollectionSelectionModel->model()), mParent);
0606         command->execute();
0607     }
0608 
0609     void slotEmptyTrash()
0610     {
0611         if (mInterceptedActions.contains(StandardMailActionManager::EmptyTrash)) {
0612             return;
0613         }
0614 
0615         if (mCollectionSelectionModel->selection().indexes().isEmpty()) {
0616             return;
0617         }
0618 
0619         const Collection::List collections = mGenericManager->selectedCollections();
0620         if (collections.count() != 1) {
0621             return;
0622         }
0623 
0624         auto command = new EmptyTrashCommand(collections.first(), mParent);
0625         command->execute();
0626     }
0627 
0628     KActionCollection *const mActionCollection;
0629     QWidget *const mParentWidget;
0630     StandardActionManager *mGenericManager = nullptr;
0631     QItemSelectionModel *mCollectionSelectionModel = nullptr;
0632     QItemSelectionModel *mItemSelectionModel = nullptr;
0633     QHash<StandardMailActionManager::Type, QAction *> mActions;
0634     QSet<StandardMailActionManager::Type> mInterceptedActions;
0635     StandardMailActionManager *const mParent;
0636 };
0637 
0638 StandardMailActionManager::StandardMailActionManager(KActionCollection *actionCollection, QWidget *parent)
0639     : QObject(parent)
0640     , d(new StandardMailActionManagerPrivate(actionCollection, parent, this))
0641 {
0642 }
0643 
0644 StandardMailActionManager::~StandardMailActionManager() = default;
0645 
0646 void StandardMailActionManager::setCollectionSelectionModel(QItemSelectionModel *selectionModel)
0647 {
0648     d->mCollectionSelectionModel = selectionModel;
0649     d->mGenericManager->setCollectionSelectionModel(selectionModel);
0650 
0651     connect(selectionModel->model(), &QAbstractItemModel::rowsInserted, this, [this]() {
0652         d->updateActions();
0653     });
0654     connect(selectionModel->model(), &QAbstractItemModel::rowsRemoved, this, [this]() {
0655         d->updateActions();
0656     });
0657     // connecting to QItemSelectionModel::selectionChanged is done by mGenericManager
0658 
0659     d->updateActions();
0660 }
0661 
0662 void StandardMailActionManager::setItems(const Item::List &selectedItems)
0663 {
0664     d->updateActionsFromSelectedItems(selectedItems);
0665 }
0666 
0667 void StandardMailActionManager::setItemSelectionModel(QItemSelectionModel *selectionModel)
0668 {
0669     d->mItemSelectionModel = selectionModel;
0670     d->mGenericManager->setItemSelectionModel(selectionModel);
0671 
0672     // connecting to QItemSelectionModel::selectionChanged is done by mGenericManager
0673 
0674     // to catch item modifications, listen to the model's dataChanged signal as well
0675     connect(selectionModel->model(), &QAbstractItemModel::dataChanged, this, [this]() {
0676         d->updateActions();
0677     });
0678 
0679     d->updateActions();
0680 }
0681 
0682 QAction *StandardMailActionManager::createAction(Type type)
0683 {
0684     if (d->mActions.contains(type)) {
0685         return d->mActions.value(type);
0686     }
0687 
0688     QAction *action = nullptr;
0689 
0690     switch (type) {
0691     case MarkMailAsRead:
0692         action = new QAction(d->mParentWidget);
0693         action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-read")));
0694         action->setText(i18n("&Mark Message as Read"));
0695         action->setIconText(i18n("Mark as Read"));
0696         d->setHelpText(action, i18n("Mark selected messages as read."));
0697         action->setWhatsThis(i18n("Mark selected messages as read."));
0698         d->mActions.insert(MarkMailAsRead, action);
0699         d->mActionCollection->addAction(QStringLiteral("akonadi_mark_as_read"), action);
0700         action->setData(QByteArray("R"));
0701         d->mActionCollection->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_R));
0702         connect(action, &QAction::triggered, this, [this]() {
0703             d->slotMarkAs();
0704         });
0705         break;
0706     case MarkMailAsUnread:
0707         action = new QAction(d->mParentWidget);
0708         action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-unread")));
0709         action->setText(i18n("&Mark Message as Unread"));
0710         action->setIconText(i18n("Mark as Unread"));
0711         d->setHelpText(action, i18n("Mark selected messages as unread."));
0712         action->setWhatsThis(i18n("Mark selected messages as unread."));
0713         d->mActions.insert(MarkMailAsUnread, action);
0714         d->mActionCollection->addAction(QStringLiteral("akonadi_mark_as_unread"), action);
0715         d->mActionCollection->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_U));
0716         action->setData(QByteArray("U"));
0717         connect(action, &QAction::triggered, this, [this]() {
0718             d->slotMarkAs();
0719         });
0720         break;
0721     case MarkMailAsImportant:
0722         action = new QAction(d->mParentWidget);
0723         action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-important")));
0724         action->setText(i18n("&Mark Message as Important"));
0725         action->setIconText(i18n("Mark as Important"));
0726         d->setHelpText(action, i18n("Mark selected messages as important."));
0727 
0728         d->mActions.insert(MarkMailAsImportant, action);
0729         d->mActionCollection->addAction(QStringLiteral("akonadi_mark_as_important"), action);
0730         action->setData(QByteArray("G"));
0731         connect(action, &QAction::triggered, this, [this]() {
0732             d->slotMarkAs();
0733         });
0734         break;
0735     case MarkMailAsActionItem:
0736         action = new QAction(d->mParentWidget);
0737         action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-task")));
0738         action->setText(i18n("&Mark Message as Action Item"));
0739         action->setIconText(i18n("Mark as Action Item"));
0740         d->setHelpText(action, i18n("Mark selected messages as action items."));
0741         action->setWhatsThis(i18n("Mark selected messages as action items."));
0742         d->mActions.insert(MarkMailAsActionItem, action);
0743         d->mActionCollection->addAction(QStringLiteral("akonadi_mark_as_action_item"), action);
0744         action->setData(QByteArray("K"));
0745         connect(action, &QAction::triggered, this, [this]() {
0746             d->slotMarkAs();
0747         });
0748         break;
0749     case MarkAllMailAsRead:
0750         action = new QAction(d->mParentWidget);
0751         action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-read")));
0752         action->setText(i18n("Mark &All Messages as Read"));
0753         action->setIconText(i18n("Mark All as Read"));
0754         d->setHelpText(action, i18n("Mark all messages as read."));
0755         action->setWhatsThis(i18n("Mark all messages as read."));
0756         d->mActions.insert(MarkAllMailAsRead, action);
0757         d->mActionCollection->addAction(QStringLiteral("akonadi_mark_all_as_read"), action);
0758         action->setData(QByteArray("R"));
0759         connect(action, &QAction::triggered, this, [this]() {
0760             d->slotMarkAllAs();
0761         });
0762         break;
0763     case MarkAllMailAsReadRecursive:
0764         action = new QAction(d->mParentWidget);
0765         action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-read")));
0766         action->setText(i18n("Mark &All Messages as Read Recursively"));
0767         action->setIconText(i18n("Mark All as Read Recursively"));
0768         d->setHelpText(action, i18n("Mark all messages as read recursively."));
0769         action->setWhatsThis(i18n("Mark all messages as read recursively."));
0770         d->mActions.insert(MarkAllMailAsReadRecursive, action);
0771         d->mActionCollection->addAction(QStringLiteral("akonadi_mark_all_as_read_recursive"), action);
0772         action->setData(QByteArray(":R"));
0773         connect(action, &QAction::triggered, this, [this]() {
0774             d->slotMarkAllAs();
0775         });
0776         break;
0777     case MarkAllMailAsUnread:
0778         action = new QAction(d->mParentWidget);
0779         action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-unread")));
0780         action->setText(i18n("Mark &All Messages as Unread"));
0781         action->setIconText(i18n("Mark All as Unread"));
0782         d->setHelpText(action, i18n("Mark all messages as unread."));
0783         action->setWhatsThis(i18n("Mark all messages as unread."));
0784         d->mActions.insert(MarkAllMailAsUnread, action);
0785         d->mActionCollection->addAction(QStringLiteral("akonadi_mark_all_as_unread"), action);
0786         action->setData(QByteArray("U"));
0787         connect(action, &QAction::triggered, this, [this]() {
0788             d->slotMarkAllAs();
0789         });
0790         break;
0791     case MarkAllMailAsImportant:
0792         action = new QAction(d->mParentWidget);
0793         action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-important")));
0794         action->setText(i18n("Mark &All Messages as Important"));
0795         action->setIconText(i18n("Mark All as Important"));
0796         d->setHelpText(action, i18n("Mark all messages as important."));
0797         action->setWhatsThis(i18n("Mark all messages as important."));
0798         d->mActions.insert(MarkAllMailAsImportant, action);
0799         d->mActionCollection->addAction(QStringLiteral("akonadi_mark_all_as_important"), action);
0800         action->setData(QByteArray("G"));
0801         connect(action, &QAction::triggered, this, [this]() {
0802             d->slotMarkAllAs();
0803         });
0804         break;
0805     case MarkAllMailAsActionItem:
0806         action = new QAction(d->mParentWidget);
0807         action->setIconText(i18n("Mark All as Action Item"));
0808         action->setText(i18n("Mark &All Messages as Action Item"));
0809         action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-task")));
0810         d->setHelpText(action, i18n("Mark all messages as action items."));
0811         action->setWhatsThis(i18n("Mark all messages as action items."));
0812         d->mActions.insert(MarkAllMailAsActionItem, action);
0813         d->mActionCollection->addAction(QStringLiteral("akonadi_mark_all_as_action_item"), action);
0814         action->setData(QByteArray("K"));
0815         connect(action, &QAction::triggered, this, [this]() {
0816             d->slotMarkAllAs();
0817         });
0818         break;
0819     case MoveToTrash:
0820         action = new QAction(d->mParentWidget);
0821         action->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
0822         action->setText(i18n("Move to &Trash"));
0823         d->mActionCollection->setDefaultShortcut(action, QKeySequence(Qt::Key_Delete));
0824         d->setHelpText(action, i18n("Move selected messages to the trash folder."));
0825         action->setWhatsThis(i18n("Move selected messages to the trash folder."));
0826         d->mActions.insert(MoveToTrash, action);
0827         d->mActionCollection->addAction(QStringLiteral("akonadi_move_to_trash"), action);
0828         connect(action, &QAction::triggered, this, [this]() {
0829             d->slotMoveToTrash();
0830         });
0831         break;
0832     case MoveAllToTrash:
0833         action = new QAction(d->mParentWidget);
0834         action->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
0835         action->setText(i18n("Move All to &Trash"));
0836         d->setHelpText(action, i18n("Move all messages to the trash folder."));
0837         action->setWhatsThis(i18n("Move all messages to the trash folder."));
0838         d->mActions.insert(MoveAllToTrash, action);
0839         d->mActionCollection->addAction(QStringLiteral("akonadi_move_all_to_trash"), action);
0840         connect(action, &QAction::triggered, this, [this]() {
0841             d->slotMoveAllToTrash();
0842         });
0843         break;
0844     case RemoveDuplicates:
0845         action = new QAction(d->mParentWidget);
0846         action->setText(i18n("Remove &Duplicate Messages"));
0847         d->setHelpText(action, i18n("Remove duplicate messages."));
0848         action->setWhatsThis(i18n("Remove duplicate messages."));
0849         d->mActionCollection->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_Asterisk));
0850         d->mActions.insert(RemoveDuplicates, action);
0851         d->mActionCollection->addAction(QStringLiteral("akonadi_remove_duplicates"), action);
0852         connect(action, &QAction::triggered, this, [this]() {
0853             d->slotRemoveDuplicates();
0854         });
0855         break;
0856     case EmptyAllTrash:
0857         action = new QAction(d->mParentWidget);
0858         action->setText(i18n("Empty All &Trash Folders"));
0859         d->setHelpText(action, i18n("Permanently delete all messages from all trash folders."));
0860         action->setWhatsThis(i18n("Permanently delete all messages from all trash folders."));
0861         d->mActions.insert(EmptyAllTrash, action);
0862         d->mActionCollection->addAction(QStringLiteral("akonadi_empty_all_trash"), action);
0863         connect(action, &QAction::triggered, this, [this]() {
0864             d->slotEmptyAllTrash();
0865         });
0866         break;
0867     case EmptyTrash:
0868         action = new QAction(d->mParentWidget);
0869         action->setText(i18n("E&mpty Trash"));
0870         d->setHelpText(action, i18n("Permanently delete all messages from the trash folder."));
0871         action->setWhatsThis(i18n("Permanently delete all messages from the trash folder."));
0872         d->mActions.insert(EmptyTrash, action);
0873         d->mActionCollection->addAction(QStringLiteral("akonadi_empty_trash"), action);
0874         connect(action, &QAction::triggered, this, [this]() {
0875             d->slotEmptyTrash();
0876         });
0877         break;
0878     default:
0879         Q_ASSERT(false); // should never happen
0880         break;
0881     }
0882 
0883     return action;
0884 }
0885 
0886 QAction *StandardMailActionManager::createAction(StandardActionManager::Type type)
0887 {
0888     QAction *act = d->mGenericManager->action(type);
0889     if (!act) {
0890         act = d->mGenericManager->createAction(type);
0891     }
0892     d->updateGenericAction(type);
0893     return act;
0894 }
0895 
0896 void StandardMailActionManager::createAllActions()
0897 {
0898     createAction(MarkMailAsRead);
0899     createAction(MarkMailAsUnread);
0900     createAction(MarkMailAsImportant);
0901     createAction(MarkMailAsActionItem);
0902     createAction(MarkAllMailAsRead);
0903     createAction(MarkAllMailAsReadRecursive);
0904     createAction(MarkAllMailAsUnread);
0905     createAction(MarkAllMailAsImportant);
0906     createAction(MarkAllMailAsActionItem);
0907     createAction(MoveToTrash);
0908     createAction(MoveAllToTrash);
0909     createAction(RemoveDuplicates);
0910     createAction(EmptyAllTrash);
0911     createAction(EmptyTrash);
0912 
0913     d->mGenericManager->createAllActions();
0914     d->updateGenericAllActions();
0915 
0916     d->updateActions();
0917 }
0918 
0919 QAction *StandardMailActionManager::action(Type type) const
0920 {
0921     return d->mActions.value(type);
0922 }
0923 
0924 QAction *StandardMailActionManager::action(StandardActionManager::Type type) const
0925 {
0926     return d->mGenericManager->action(type);
0927 }
0928 
0929 void StandardMailActionManager::setActionText(StandardActionManager::Type type, const KLocalizedString &text)
0930 {
0931     d->mGenericManager->setActionText(type, text);
0932 }
0933 
0934 void StandardMailActionManager::interceptAction(Type type, bool intercept)
0935 {
0936     if (intercept) {
0937         d->mInterceptedActions.insert(type);
0938     } else {
0939         d->mInterceptedActions.remove(type);
0940     }
0941 }
0942 
0943 void StandardMailActionManager::interceptAction(StandardActionManager::Type type, bool intercept)
0944 {
0945     d->mGenericManager->interceptAction(type, intercept);
0946 }
0947 
0948 Akonadi::Collection::List StandardMailActionManager::selectedCollections() const
0949 {
0950     return d->mGenericManager->selectedCollections();
0951 }
0952 
0953 Akonadi::Item::List StandardMailActionManager::selectedItems() const
0954 {
0955     return d->mGenericManager->selectedItems();
0956 }
0957 
0958 void StandardMailActionManager::setFavoriteCollectionsModel(FavoriteCollectionsModel *favoritesModel)
0959 {
0960     d->mGenericManager->setFavoriteCollectionsModel(favoritesModel);
0961 }
0962 
0963 void StandardMailActionManager::setFavoriteSelectionModel(QItemSelectionModel *selectionModel)
0964 {
0965     d->mGenericManager->setFavoriteSelectionModel(selectionModel);
0966 }
0967 
0968 void StandardMailActionManager::setCollectionPropertiesPageNames(const QStringList &names)
0969 {
0970     d->mGenericManager->setCollectionPropertiesPageNames(names);
0971 }
0972 
0973 Akonadi::StandardActionManager *StandardMailActionManager::standardActionManager() const
0974 {
0975     return d->mGenericManager;
0976 }
0977 
0978 void StandardMailActionManager::markItemsAs(const QByteArray &typeStr, const Item::List &items, bool checkIntercept)
0979 {
0980     d->markItemsAs(typeStr, items, checkIntercept);
0981 }
0982 
0983 void StandardMailActionManager::markAllItemsAs(const QByteArray &typeStr, const Collection::List &collections, bool checkIntercept)
0984 {
0985     d->markAllItemsAs(typeStr, collections, checkIntercept);
0986 }
0987 
0988 #include "moc_standardmailactionmanager.cpp"