File indexing completed on 2024-05-12 05:20:43

0001 /*
0002   This file is part of KMail, the KDE mail client.
0003   SPDX-FileCopyrightText: 2002 Don Sanders <sanders@kde.org>
0004   SPDX-FileCopyrightText: 2009-2024 Laurent Montel <montel@kde.org>
0005 
0006   Based on the work of Stefan Taferner <taferner@kde.org>
0007 
0008   SPDX-License-Identifier: GPL-2.0-only
0009 */
0010 
0011 // KMail includes
0012 #include "kmmainwidget.h"
0013 #include "editor/composer.h"
0014 #include "job/clearcachejobinfolderandsubfolderjob.h"
0015 #include "job/composenewmessagejob.h"
0016 #include "kmcommands.h"
0017 #include "kmreadermainwin.h"
0018 #include "searchdialog/searchwindow.h"
0019 #include "undostack.h"
0020 #include "util.h"
0021 #include "widgets/vacationscriptindicatorwidget.h"
0022 #include "widgets/zoomlabelwidget.h"
0023 #include <MailCommon/FolderSelectionDialog>
0024 #include <PimCommonAkonadi/MailUtil>
0025 #include <TemplateParser/CustomTemplatesMenu>
0026 
0027 #include "dialog/archivefolderdialog.h"
0028 #include "foldershortcutactionmanager.h"
0029 #include "job/markallmessagesasreadinfolderandsubfolderjob.h"
0030 #include "job/removeduplicatemessageinfolderandsubfolderjob.h"
0031 #include "manageshowcollectionproperties.h"
0032 #include "plugininterface/kmailplugininterface.h"
0033 #include "settings/kmailsettings.h"
0034 #include "sieveimapinterface/kmsieveimappasswordprovider.h"
0035 #include "tag/tagactionmanager.h"
0036 #include "widgets/collectionpane.h"
0037 #include "widgets/kactionmenuaccount.h"
0038 #include "widgets/kactionmenutransport.h"
0039 #include <KSieveUi/SieveDebugDialog>
0040 #include <MailCommon/FolderTreeView>
0041 #include <MailCommon/MailKernel>
0042 #include <MailCommon/MailUtil>
0043 #include <MailCommon/SearchRuleStatus>
0044 
0045 #include "collectionpage/collectionmailinglistpage.h"
0046 #include "collectionpage/collectionquotapage.h"
0047 #include "collectionpage/collectionshortcutpage.h"
0048 #include "collectionpage/collectiontemplatespage.h"
0049 #include "collectionpage/collectionviewpage.h"
0050 #include "folderarchive/folderarchivemanager.h"
0051 #include "folderarchive/folderarchiveutil.h"
0052 #include "job/createnewcontactjob.h"
0053 #include "tag/tagselectdialog.h"
0054 #include <Akonadi/CollectionMaintenancePage>
0055 
0056 #include <Akonadi/AgentConfigurationDialog>
0057 #include <MailCommon/CollectionExpiryPage>
0058 #include <MailCommon/CollectionGeneralPage>
0059 #include <MailCommon/ExpireCollectionAttribute>
0060 #include <MailCommon/FavoriteCollectionOrderProxyModel>
0061 #include <MailCommon/FavoriteCollectionWidget>
0062 #include <MailCommon/FilterManager>
0063 #include <MailCommon/MailFilter>
0064 #include <PimCommon/PimUtil>
0065 #include <PimCommonAkonadi/CollectionAclPage>
0066 #include <mailcommon/mailcommonsettings_base.h>
0067 
0068 #include <MessageViewer/HeaderStyle>
0069 #include <MessageViewer/HeaderStylePlugin>
0070 #include <MessageViewer/MessageViewerSettings>
0071 #include <MessageViewer/Viewer>
0072 
0073 #include <MessageViewer/AttachmentStrategy>
0074 #include <MessageViewer/MessageViewerCheckBeforeDeletingPluginManager>
0075 
0076 #include <KColorSchemeMenu>
0077 #include <KCursorSaver>
0078 
0079 #include <MessageComposer/MessageHelper>
0080 #include <MessageComposer/MessageSender>
0081 
0082 #include <MessageCore/MailingList>
0083 #include <MessageCore/MessageCoreSettings>
0084 
0085 #include "dialog/kmknotify.h"
0086 #include "widgets/displaymessageformatactionmenu.h"
0087 
0088 #include "kmlaunchexternalcomponent.h"
0089 #include <KSieveUi/VacationManager>
0090 
0091 #include <Libkdepim/ProgressManager>
0092 #include <PimCommon/BroadcastStatus>
0093 
0094 #include <Akonadi/AgentInstance>
0095 #include <Akonadi/AgentManager>
0096 #include <Akonadi/AgentType>
0097 #include <Akonadi/AttributeFactory>
0098 #include <Akonadi/CachePolicy>
0099 #include <Akonadi/ChangeRecorder>
0100 #include <Akonadi/ClearCacheFoldersJob>
0101 #include <Akonadi/CollectionAttributesSynchronizationJob>
0102 #include <Akonadi/CollectionDialog>
0103 #include <Akonadi/CollectionFetchJob>
0104 #include <Akonadi/CollectionFetchScope>
0105 #include <Akonadi/CollectionPropertiesDialog>
0106 #include <Akonadi/CollectionStatistics>
0107 #include <Akonadi/ContactSearchJob>
0108 #include <Akonadi/ControlGui>
0109 #include <Akonadi/ETMViewStateSaver>
0110 #include <Akonadi/EntityDisplayAttribute>
0111 #include <Akonadi/EntityListView>
0112 #include <Akonadi/EntityMimeTypeFilterModel>
0113 #include <Akonadi/EntityTreeModel>
0114 #include <Akonadi/FavoriteCollectionsModel>
0115 #include <Akonadi/ItemFetchJob>
0116 #include <Akonadi/ItemFetchScope>
0117 #include <Akonadi/ItemModifyJob>
0118 #include <Akonadi/MessageFlags>
0119 #include <Akonadi/Session>
0120 #include <Akonadi/StandardActionManager>
0121 
0122 #include <KEmailAddress>
0123 #include <KIdentityManagementCore/Identity>
0124 #include <KIdentityManagementCore/IdentityManager>
0125 #include <KMime/HeaderParsing>
0126 #include <KMime/KMimeMessage>
0127 #include <KSieveCore/Util>
0128 #include <KSieveUi/ManageSieveScriptsDialog>
0129 #include <MailTransport/Transport>
0130 #include <MailTransport/TransportManager>
0131 
0132 #include "kmail_debug.h"
0133 
0134 #include <KAcceleratorManager>
0135 #include <KActionMenu>
0136 #include <KMessageBox>
0137 #include <KStandardShortcut>
0138 #include <KWindowSystem>
0139 
0140 #include <KConfigGroup>
0141 #include <KNotification>
0142 #include <KRecentFilesMenu>
0143 #include <KStandardAction>
0144 #include <KStringHandler>
0145 #include <KToggleAction>
0146 #include <KXMLGUIFactory>
0147 
0148 #include <QAction>
0149 #include <QByteArray>
0150 #include <QHeaderView>
0151 #include <QKeyCombination>
0152 #include <QList>
0153 #include <QMenu>
0154 #include <QProcess>
0155 #include <QSplitter>
0156 #include <QStatusBar>
0157 #include <QVBoxLayout>
0158 #include <WebEngineViewer/WebHitTestResult>
0159 
0160 #include <KColorSchemeManager>
0161 #include <QDBusConnection>
0162 #include <QDBusInterface>
0163 #include <QDBusReply>
0164 #include <QStandardPaths>
0165 
0166 #include "historyclosedreader/historyclosedreadermanager.h"
0167 #include "historyclosedreader/historyclosedreadermenu.h"
0168 #include "job/removecollectionjob.h"
0169 #include "job/removeduplicatemailjob.h"
0170 #include <PimCommonAkonadi/ManageServerSideSubscriptionJob>
0171 
0172 #include <MessageViewer/DKIMViewerMenu>
0173 #include <MessageViewer/DKIMWidgetInfo>
0174 #include <MessageViewer/RemoteContentMenu>
0175 
0176 #include "historyswitchfolder/collectionswitchertreeviewmanager.h"
0177 #include "plugininterface/kmailplugincheckbeforedeletingmanagerinterface.h"
0178 
0179 #ifdef WITH_KUSERFEEDBACK
0180 #include <KUserFeedback/NotificationPopup>
0181 #include <KUserFeedback/Provider>
0182 #endif
0183 
0184 using namespace std::chrono_literals;
0185 #include <chrono>
0186 
0187 using namespace KMime;
0188 using namespace Akonadi;
0189 using namespace MailCommon;
0190 using KMail::SearchWindow;
0191 using KMime::Types::AddrSpecList;
0192 using KPIM::ProgressManager;
0193 using PimCommon::BroadcastStatus;
0194 
0195 static KMMainWidget *myMainWidget = nullptr;
0196 //-----------------------------------------------------------------------------
0197 KMMainWidget::KMMainWidget(QWidget *parent, KXMLGUIClient *aGUIClient, KActionCollection *actionCollection, const KSharedConfig::Ptr &config)
0198     : QWidget(parent)
0199     , mToolbarActionSeparator(new QAction(this))
0200     , mSievePasswordProvider(new KMSieveImapPasswordProvider(this))
0201     , mLaunchExternalComponent(new KMLaunchExternalComponent(this, this))
0202     , mManageShowCollectionProperties(new ManageShowCollectionProperties(this, this))
0203     , mCollectionSwitcherTreeViewManager(new CollectionSwitcherTreeViewManager(this))
0204 {
0205     // must be the first line of the constructor:
0206     mActionCollection = actionCollection;
0207     mTopLayout = new QVBoxLayout(this);
0208     mTopLayout->setContentsMargins({});
0209     mConfig = config;
0210     mGUIClient = aGUIClient;
0211     Akonadi::ControlGui::widgetNeedsAkonadi(this);
0212     mVacationManager = new KSieveUi::VacationManager(mSievePasswordProvider, this);
0213     connect(mVacationManager,
0214             &KSieveUi::VacationManager::updateVacationScriptStatus,
0215             this,
0216             qOverload<bool, const QString &>(&KMMainWidget::updateVacationScriptStatus));
0217 
0218 #ifdef WITH_KUSERFEEDBACK
0219     auto userFeedBackNotificationPopup = new KUserFeedback::NotificationPopup(this);
0220     userFeedBackNotificationPopup->setFeedbackProvider(kmkernel->userFeedbackProvider());
0221 #endif
0222 
0223     mToolbarActionSeparator->setSeparator(true);
0224 
0225     KMailPluginInterface::self()->setActionCollection(mActionCollection);
0226     KMailPluginInterface::self()->initializePlugins();
0227     KMailPluginInterface::self()->setMainWidget(this);
0228     mPluginCheckBeforeDeletingManagerInterface = new KMailPluginCheckBeforeDeletingManagerInterface(this);
0229     mPluginCheckBeforeDeletingManagerInterface->setParentWidget(this);
0230     mPluginCheckBeforeDeletingManagerInterface->setActionCollection(mActionCollection);
0231     mPluginCheckBeforeDeletingManagerInterface->initializePlugins();
0232 
0233     myMainWidget = this;
0234 
0235     readPreConfig();
0236     createWidgets();
0237     setupActions();
0238 
0239     readConfig();
0240 
0241     if (!kmkernel->isOffline()) { // kmail is set to online mode, make sure the agents are also online
0242         kmkernel->setAccountStatus(true);
0243     }
0244 
0245     QTimer::singleShot(0, this, &KMMainWidget::slotShowStartupFolder);
0246 
0247     connect(kmkernel, &KMKernel::startCheckMail, this, &KMMainWidget::slotStartCheckMail);
0248 
0249     connect(kmkernel, &KMKernel::endCheckMail, this, &KMMainWidget::slotEndCheckMail);
0250 
0251     connect(kmkernel, &KMKernel::configChanged, this, &KMMainWidget::slotConfigChanged);
0252 
0253     connect(kmkernel, &KMKernel::onlineStatusChanged, this, &KMMainWidget::slotUpdateOnlineStatus);
0254 
0255     connect(mTagActionManager, &KMail::TagActionManager::tagActionTriggered, this, &KMMainWidget::slotUpdateMessageTagList);
0256 
0257     connect(mTagActionManager, &KMail::TagActionManager::tagMoreActionClicked, this, &KMMainWidget::slotSelectMoreMessageTagList);
0258 
0259     kmkernel->toggleSystemTray();
0260     connect(HistoryClosedReaderManager::self(), &HistoryClosedReaderManager::historyClosedReaderChanged, this, &KMMainWidget::slotHistoryClosedReaderChanged);
0261     slotHistoryClosedReaderChanged();
0262 
0263     {
0264         // make sure the pages are registered only once, since there can be multiple instances of KMMainWidget
0265         static bool pagesRegistered = false;
0266 
0267         if (!pagesRegistered) {
0268             Akonadi::CollectionPropertiesDialog::registerPage(new PimCommon::CollectionAclPageFactory);
0269             Akonadi::CollectionPropertiesDialog::registerPage(new MailCommon::CollectionGeneralPageFactory);
0270             Akonadi::CollectionPropertiesDialog::registerPage(new CollectionMaintenancePageFactory);
0271             Akonadi::CollectionPropertiesDialog::registerPage(new CollectionQuotaPageFactory);
0272             Akonadi::CollectionPropertiesDialog::registerPage(new CollectionTemplatesPageFactory);
0273             Akonadi::CollectionPropertiesDialog::registerPage(new MailCommon::CollectionExpiryPageFactory);
0274             Akonadi::CollectionPropertiesDialog::registerPage(new CollectionViewPageFactory);
0275             Akonadi::CollectionPropertiesDialog::registerPage(new CollectionMailingListPageFactory);
0276             Akonadi::CollectionPropertiesDialog::registerPage(new CollectionShortcutPageFactory);
0277 
0278             pagesRegistered = true;
0279         }
0280     }
0281 
0282     auto mainWin = qobject_cast<KMainWindow *>(window());
0283     mCurrentStatusBar = mainWin ? mainWin->statusBar() : nullptr;
0284     mVacationScriptIndicator = new KMail::VacationScriptIndicatorWidget(mCurrentStatusBar);
0285     mVacationScriptIndicator->hide();
0286 
0287     mZoomLabelIndicator = new ZoomLabelWidget(mCurrentStatusBar);
0288     if (mMsgView) {
0289         setZoomChanged(mMsgView->viewer()->webViewZoomFactor());
0290     }
0291     connect(mVacationScriptIndicator, &KMail::VacationScriptIndicatorWidget::clicked, this, &KMMainWidget::slotEditVacation);
0292     if (KSieveCore::Util::checkOutOfOfficeOnStartup()) {
0293         QTimer::singleShot(0, this, &KMMainWidget::slotCheckVacation);
0294     }
0295 
0296     connect(mFolderTreeWidget->folderTreeView()->model(), &QAbstractItemModel::modelReset, this, &KMMainWidget::restoreCollectionFolderViewConfig);
0297     restoreCollectionFolderViewConfig();
0298 
0299     if (kmkernel->firstStart()) {
0300         const QStringList listOfMailerFound = MailCommon::Util::foundMailer();
0301         if (!listOfMailerFound.isEmpty()) {
0302             const int answer = KMessageBox::questionTwoActionsList(this,
0303                                                                    i18n("Another mailer was found on system. Do you want to import data from it?"),
0304                                                                    listOfMailerFound,
0305                                                                    QString(),
0306                                                                    KGuiItem(i18nc("@action:button", "Import"), QStringLiteral("document-import")),
0307                                                                    KGuiItem(i18nc("@action:button", "Do Not Import"), QStringLiteral("dialog-cancel")));
0308             if (answer == KMessageBox::ButtonCode::PrimaryAction) {
0309                 const QString path = QStandardPaths::findExecutable(QStringLiteral("akonadiimportwizard"));
0310                 if (path.isEmpty() || !QProcess::startDetached(path, QStringList())) {
0311                     KMessageBox::error(this,
0312                                        i18n("Could not start the import wizard. "
0313                                             "Please check your installation."),
0314                                        i18n("Unable to start import wizard"));
0315                 }
0316             } else {
0317                 mLaunchExternalComponent->slotAccountWizard();
0318             }
0319         } else {
0320             mLaunchExternalComponent->slotAccountWizard();
0321         }
0322     }
0323     // must be the last line of the constructor:
0324     mStartupDone = true;
0325 
0326     mCheckMailTimer.setInterval(3s);
0327     mCheckMailTimer.setSingleShot(true);
0328     connect(&mCheckMailTimer, &QTimer::timeout, this, &KMMainWidget::slotUpdateActionsAfterMailChecking);
0329 
0330     setupUnifiedMailboxChecker();
0331     mCollectionSwitcherTreeViewManager->setParentWidget(this);
0332     connect(mCollectionSwitcherTreeViewManager, &CollectionSwitcherTreeViewManager::switchToFolder, this, &KMMainWidget::slotHistorySwitchFolder);
0333 }
0334 
0335 QWidget *KMMainWidget::dkimWidgetInfo() const
0336 {
0337     if (mMsgView) {
0338         return mMsgView->viewer()->dkimWidgetInfo();
0339     }
0340     return nullptr;
0341 }
0342 
0343 void KMMainWidget::restoreCollectionFolderViewConfig()
0344 {
0345     if (!myMainWidget) // We are in the destructor
0346         return;
0347 
0348     auto saver = new ETMViewStateSaver;
0349     saver->setView(mFolderTreeWidget->folderTreeView());
0350     const KConfigGroup cfg(KMKernel::self()->config(), QStringLiteral("CollectionFolderView"));
0351     mFolderTreeWidget->restoreHeaderState(cfg.readEntry("HeaderState", QByteArray()));
0352     saver->restoreState(cfg);
0353     // Restore startup folder
0354 
0355     Akonadi::Collection::Id id = -1;
0356     if (mCurrentCollection.isValid()) {
0357         id = mCurrentCollection.id();
0358     }
0359 
0360     if (id == -1) {
0361         if (KMailSettings::self()->startSpecificFolderAtStartup()) {
0362             const Akonadi::Collection::Id startupFolder = KMailSettings::self()->startupFolder();
0363             if (startupFolder > 0) {
0364                 saver->restoreCurrentItem(QStringLiteral("c%1").arg(startupFolder));
0365             }
0366         }
0367     } else {
0368         saver->restoreCurrentItem(QStringLiteral("c%1").arg(id));
0369     }
0370 }
0371 
0372 //-----------------------------------------------------------------------------
0373 // The kernel may have already been deleted when this method is called,
0374 // perform all cleanup that requires the kernel in destruct()
0375 KMMainWidget::~KMMainWidget()
0376 {
0377     myMainWidget = nullptr;
0378     qDeleteAll(mFilterCommands);
0379     destruct();
0380 }
0381 
0382 //-----------------------------------------------------------------------------
0383 // This method performs all cleanup that requires the kernel to exist.
0384 void KMMainWidget::destruct()
0385 {
0386     if (mDestructed) {
0387         return;
0388     }
0389     if (mSearchWin) {
0390         mSearchWin->close();
0391     }
0392     disconnect(mFolderTreeWidget->folderTreeView()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KMMainWidget::updateFolderMenu);
0393     writeConfig(false); /* don't force kmkernel sync when close BUG: 289287 */
0394     writeFolderConfig();
0395     deleteWidgets();
0396     clearCurrentFolder();
0397     delete mMoveOrCopyToDialog;
0398     delete mSelectFromAllFoldersDialog;
0399     delete mSievePasswordProvider;
0400 
0401     // clang-format off
0402     disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)), this, nullptr);
0403     disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)), this, nullptr);
0404     disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>)), this, nullptr);
0405     disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), this, nullptr);
0406     // clang-format on
0407     disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemRemoved(Akonadi::Item)), this, nullptr);
0408 
0409     mDestructed = true;
0410 }
0411 
0412 void KMMainWidget::clearCurrentFolder()
0413 {
0414     mCurrentFolderSettings.clear();
0415     mCurrentCollection = Akonadi::Collection();
0416 }
0417 
0418 void KMMainWidget::slotStartCheckMail()
0419 {
0420     if (mCheckMailTimer.isActive()) {
0421         mCheckMailTimer.stop();
0422     }
0423 }
0424 
0425 void KMMainWidget::slotEndCheckMail()
0426 {
0427     if (!mCheckMailTimer.isActive()) {
0428         mCheckMailTimer.start();
0429     }
0430 }
0431 
0432 void KMMainWidget::slotUpdateActionsAfterMailChecking()
0433 {
0434     const bool sendOnAll = KMailSettings::self()->sendOnCheck() == KMailSettings::EnumSendOnCheck::SendOnAllChecks;
0435     const bool sendOnManual = KMailSettings::self()->sendOnCheck() == KMailSettings::EnumSendOnCheck::SendOnManualChecks;
0436     if (!kmkernel->isOffline() && (sendOnAll || sendOnManual)) {
0437         slotSendQueued();
0438     }
0439     // update folder menus in case some mail got filtered to trash/current folder
0440     // and we can enable "empty trash/move all to trash" action etc.
0441     updateFolderMenu();
0442 }
0443 
0444 void KMMainWidget::setCurrentCollection(const Akonadi::Collection &col)
0445 {
0446     mCurrentCollection = col;
0447     if (mCurrentFolderSettings) {
0448         mCurrentFolderSettings->setCollection(col);
0449     }
0450 }
0451 
0452 void KMMainWidget::slotCollectionFetched(int collectionId)
0453 {
0454     // Called when a collection is fetched for the first time by the ETM.
0455     // This is the right time to update the caption (which still says "Loading...")
0456     // and to update the actions that depend on the number of mails in the folder.
0457     if (collectionId == mCurrentCollection.id()) {
0458         setCurrentCollection(CommonKernel->collectionFromId(mCurrentCollection.id()));
0459         updateMessageActions();
0460         updateFolderMenu();
0461     }
0462     // We call this for any collection, it could be one of our parents...
0463     if (mCurrentCollection.isValid()) {
0464         Q_EMIT captionChangeRequest(fullCollectionPath());
0465     }
0466 }
0467 
0468 QString KMMainWidget::fullCollectionPath() const
0469 {
0470     if (mCurrentCollection.isValid()) {
0471         return MailCommon::Util::fullCollectionPath(mCurrentCollection);
0472     }
0473     return {};
0474 }
0475 
0476 // Connected to the currentChanged signals from the folderTreeView and favorites view.
0477 void KMMainWidget::slotFolderChanged(const Akonadi::Collection &collection)
0478 {
0479     if (mCurrentCollection == collection) {
0480         return;
0481     }
0482     mCollectionSwitcherTreeViewManager->addHistory(collection, MailCommon::Util::fullCollectionPath(collection));
0483     slotHistorySwitchFolder(collection);
0484 }
0485 
0486 void KMMainWidget::slotHistorySwitchFolder(const Akonadi::Collection &collection)
0487 {
0488     if (mCurrentCollection == collection) {
0489         return;
0490     }
0491     if (mFolderTreeWidget) {
0492         mFolderTreeWidget->selectCollectionFolder(collection, false); // Don't expand treewidget
0493     }
0494     // Store previous collection
0495     if (mGoToFirstUnreadMessageInSelectedFolder) {
0496         // the default action has been overridden from outside
0497         mPreSelectionMode = MessageList::Core::PreSelectFirstUnreadCentered;
0498     } else {
0499         // use the default action
0500         switch (KMailSettings::self()->actionEnterFolder()) {
0501         case KMailSettings::EnumActionEnterFolder::SelectFirstUnread:
0502             mPreSelectionMode = MessageList::Core::PreSelectFirstUnreadCentered;
0503             break;
0504         case KMailSettings::EnumActionEnterFolder::SelectLastSelected:
0505             mPreSelectionMode = MessageList::Core::PreSelectLastSelected;
0506             break;
0507         case KMailSettings::EnumActionEnterFolder::SelectNewest:
0508             mPreSelectionMode = MessageList::Core::PreSelectNewestCentered;
0509             break;
0510         case KMailSettings::EnumActionEnterFolder::SelectOldest:
0511             mPreSelectionMode = MessageList::Core::PreSelectOldestCentered;
0512             break;
0513         default:
0514             mPreSelectionMode = MessageList::Core::PreSelectNone;
0515             break;
0516         }
0517     }
0518 
0519     mGoToFirstUnreadMessageInSelectedFolder = false;
0520     KCursorSaver saver(Qt::WaitCursor);
0521 
0522     if (mMsgView) {
0523         mMsgView->clear(true);
0524     }
0525     const bool newFolder = mCurrentCollection != collection;
0526 
0527     // Delete any pending timer, if needed it will be recreated below
0528     delete mShowBusySplashTimer;
0529     mShowBusySplashTimer = nullptr;
0530     if (newFolder && mCurrentCollection.isValid()) {
0531         // We're changing folder: write configuration for the old one
0532         writeFolderConfig();
0533     }
0534 
0535     mCurrentFolderSettings = FolderSettings::forCollection(collection);
0536     mCurrentCollection = collection;
0537 
0538     readFolderConfig();
0539     if (mMsgView) {
0540         assignLoadExternalReference();
0541     }
0542 
0543     if (!mCurrentFolderSettings->isValid() && (mMessagePane->count() < 2)) {
0544         slotIntro();
0545     } else {
0546         if (mMessagePane->isHidden()) {
0547             mMessagePane->show();
0548         }
0549     }
0550 
0551     // The message pane uses the selection model of the folder view to load the correct aggregation model and theme
0552     //  settings. At this point the selection model hasn't been updated yet to the user's new choice, so it would load
0553     //  the old folder settings instead.
0554     QTimer::singleShot(0, this, &KMMainWidget::slotShowSelectedFolderInPane);
0555     if (collection.cachePolicy().syncOnDemand()) {
0556         AgentManager::self()->synchronizeCollection(collection, false);
0557     }
0558     mMsgActions->setCurrentMessage(Akonadi::Item());
0559     Q_EMIT captionChangeRequest(MailCommon::Util::fullCollectionPath(collection));
0560 }
0561 
0562 void KMMainWidget::slotShowSelectedFolderInPane()
0563 {
0564     if (mCurrentCollection.isValid()) {
0565         const QModelIndex idx = Akonadi::EntityTreeModel::modelIndexForCollection(KMKernel::self()->entityTreeModel(), mCurrentCollection);
0566         if (idx.isValid()) {
0567             mMessagePane->setCurrentFolder(mCurrentCollection, idx, false, mPreSelectionMode);
0568         }
0569     }
0570     updateMessageActions();
0571     updateFolderMenu();
0572 }
0573 
0574 //-----------------------------------------------------------------------------
0575 void KMMainWidget::readPreConfig()
0576 {
0577     mLongFolderList = KMailSettings::self()->folderList() == KMailSettings::EnumFolderList::longlist;
0578     mReaderWindowActive = KMailSettings::self()->readerWindowMode() != KMailSettings::EnumReaderWindowMode::hide;
0579     mReaderWindowBelow = KMailSettings::self()->readerWindowMode() == KMailSettings::EnumReaderWindowMode::below;
0580 
0581     mHtmlGlobalSetting = MessageViewer::MessageViewerSettings::self()->htmlMail();
0582     mHtmlLoadExtGlobalSetting = MessageViewer::MessageViewerSettings::self()->htmlLoadExternal();
0583 
0584     mEnableFavoriteFolderView =
0585         (KMKernel::self()->mailCommonSettings()->favoriteCollectionViewMode() != MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::HiddenMode);
0586     mEnableFolderQuickSearch = KMailSettings::self()->enableFolderQuickSearch();
0587 }
0588 
0589 void KMMainWidget::updateDisplayFormatMessage()
0590 {
0591     readFolderConfig();
0592     updateHtmlMenuEntry();
0593     if (mMsgView) {
0594         mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference);
0595         mMsgView->update(true);
0596     }
0597 }
0598 
0599 //-----------------------------------------------------------------------------
0600 void KMMainWidget::readFolderConfig()
0601 {
0602     if (!mCurrentCollection.isValid()) {
0603         return;
0604     }
0605     KSharedConfig::Ptr config = KMKernel::self()->config();
0606     KConfigGroup group(config, MailCommon::FolderSettings::configGroupName(mCurrentCollection));
0607     if (group.hasKey("htmlMailOverride")) {
0608         const bool useHtml = group.readEntry("htmlMailOverride", false);
0609         mFolderDisplayFormatPreference = useHtml ? MessageViewer::Viewer::Html : MessageViewer::Viewer::Text;
0610         group.deleteEntry("htmlMailOverride");
0611         group.sync();
0612     } else {
0613         mFolderDisplayFormatPreference = static_cast<MessageViewer::Viewer::DisplayFormatMessage>(
0614             group.readEntry("displayFormatOverride", static_cast<int>(MessageViewer::Viewer::UseGlobalSetting)));
0615     }
0616     mFolderHtmlLoadExtPreference = group.readEntry("htmlLoadExternalOverride", false);
0617 }
0618 
0619 //-----------------------------------------------------------------------------
0620 void KMMainWidget::writeFolderConfig()
0621 {
0622     if (mCurrentFolderSettings) {
0623         mCurrentFolderSettings->setFolderHtmlLoadExtPreference(mFolderHtmlLoadExtPreference);
0624         mCurrentFolderSettings->setFormatMessage(mFolderDisplayFormatPreference);
0625         mCurrentFolderSettings->writeConfig();
0626     }
0627 }
0628 
0629 //-----------------------------------------------------------------------------
0630 void KMMainWidget::layoutSplitters()
0631 {
0632     // This function can only be called when the old splitters are already deleted
0633     Q_ASSERT(!mSplitter1);
0634     Q_ASSERT(!mSplitter2);
0635 
0636     // For some reason, this is necessary here so that the copy action still
0637     // works after changing the folder layout.
0638     if (mMsgView) {
0639         disconnect(mMsgView->copyAction(), &QAction::triggered, mMsgView, &KMReaderWin::slotCopySelectedText);
0640     }
0641 
0642     // If long folder list is enabled, the splitters are:
0643     // Splitter 1: FolderView vs (HeaderAndSearch vs MessageViewer)
0644     // Splitter 2: HeaderAndSearch vs MessageViewer
0645     //
0646     // If long folder list is disabled, the splitters are:
0647     // Splitter 1: (FolderView vs HeaderAndSearch) vs MessageViewer
0648     // Splitter 2: FolderView vs HeaderAndSearch
0649 
0650     // The folder view is both the folder tree and the favorite folder view, if
0651     // enabled
0652 
0653     const bool readerWindowAtSide = !mReaderWindowBelow && mReaderWindowActive;
0654     const bool readerWindowBelow = mReaderWindowBelow && mReaderWindowActive;
0655 
0656     mSplitter1 = new QSplitter(this);
0657     mSplitter2 = new QSplitter(mSplitter1);
0658 
0659     QWidget *folderTreeWidget = mFolderTreeWidget;
0660     if (mFavoriteCollectionsView) {
0661         mFolderViewSplitter = new QSplitter(Qt::Vertical);
0662         mFolderViewSplitter->setChildrenCollapsible(false);
0663         mFolderViewSplitter->addWidget(mFavoriteCollectionsView);
0664         mFolderViewSplitter->addWidget(mFolderTreeWidget);
0665         folderTreeWidget = mFolderViewSplitter;
0666     }
0667 
0668     if (mLongFolderList) {
0669         // add folder tree
0670         mSplitter1->setOrientation(Qt::Horizontal);
0671         mSplitter1->addWidget(folderTreeWidget);
0672 
0673         // and the rest to the right
0674         mSplitter1->addWidget(mSplitter2);
0675 
0676         // add the message list to the right or below
0677         if (readerWindowAtSide) {
0678             mSplitter2->setOrientation(Qt::Horizontal);
0679         } else {
0680             mSplitter2->setOrientation(Qt::Vertical);
0681         }
0682         mSplitter2->addWidget(mMessagePane);
0683 
0684         // add the preview window, if there is one
0685         if (mMsgView) {
0686             mSplitter2->addWidget(mMsgView);
0687         }
0688     } else { // short folder list
0689         if (mReaderWindowBelow) {
0690             mSplitter1->setOrientation(Qt::Vertical);
0691             mSplitter2->setOrientation(Qt::Horizontal);
0692         } else { // at side or none
0693             mSplitter1->setOrientation(Qt::Horizontal);
0694             mSplitter2->setOrientation(Qt::Vertical);
0695         }
0696 
0697         mSplitter1->addWidget(mSplitter2);
0698 
0699         // add folder tree
0700         mSplitter2->addWidget(folderTreeWidget);
0701         // add message list to splitter 2
0702         mSplitter2->addWidget(mMessagePane);
0703 
0704         // add the preview window, if there is one
0705         if (mMsgView) {
0706             mSplitter1->addWidget(mMsgView);
0707         }
0708     }
0709 
0710     //
0711     // Set splitter properties
0712     //
0713     mSplitter1->setObjectName(QLatin1StringView("splitter1"));
0714     mSplitter1->setChildrenCollapsible(false);
0715     mSplitter2->setObjectName(QLatin1StringView("splitter2"));
0716     mSplitter2->setChildrenCollapsible(false);
0717 
0718     //
0719     // Set the stretch factors
0720     //
0721     mSplitter1->setStretchFactor(0, 0);
0722     mSplitter2->setStretchFactor(0, 0);
0723     mSplitter1->setStretchFactor(1, 1);
0724     mSplitter2->setStretchFactor(1, 1);
0725 
0726     if (mFavoriteCollectionsView) {
0727         mFolderViewSplitter->setStretchFactor(0, 0);
0728         mFolderViewSplitter->setStretchFactor(1, 1);
0729     }
0730 
0731     // Because the reader windows's width increases a tiny bit after each
0732     // restart in short folder list mode with message window at side, disable
0733     // the stretching as a workaround here
0734     if (readerWindowAtSide && !mLongFolderList) {
0735         mSplitter1->setStretchFactor(0, 1);
0736         mSplitter1->setStretchFactor(1, 0);
0737     }
0738 
0739     //
0740     // Set the sizes of the splitters to the values stored in the config
0741     //
0742     QList<int> splitter1Sizes;
0743     QList<int> splitter2Sizes;
0744 
0745     const int folderViewWidth = KMailSettings::self()->folderViewWidth();
0746     const int ftHeight = KMailSettings::self()->folderTreeHeight();
0747     int headerHeight = KMailSettings::self()->searchAndHeaderHeight();
0748     const int messageViewerWidth = KMailSettings::self()->readerWindowWidth();
0749     const int headerWidth = KMailSettings::self()->searchAndHeaderWidth();
0750     int messageViewerHeight = KMailSettings::self()->readerWindowHeight();
0751 
0752     int ffvHeight = mFolderViewSplitter ? KMKernel::self()->mailCommonSettings()->favoriteCollectionViewHeight() : 0;
0753 
0754     // If the message viewer was hidden before, make sure it is not zero height
0755     if (messageViewerHeight < 10 && readerWindowBelow) {
0756         headerHeight /= 2;
0757         messageViewerHeight = headerHeight;
0758     }
0759 
0760     if (mLongFolderList) {
0761         if (!readerWindowAtSide) {
0762             splitter1Sizes << folderViewWidth << headerWidth;
0763             splitter2Sizes << headerHeight << messageViewerHeight;
0764         } else {
0765             splitter1Sizes << folderViewWidth << (headerWidth + messageViewerWidth);
0766             splitter2Sizes << headerWidth << messageViewerWidth;
0767         }
0768     } else {
0769         if (!readerWindowAtSide) {
0770             splitter1Sizes << headerHeight << messageViewerHeight;
0771             splitter2Sizes << folderViewWidth << headerWidth;
0772         } else {
0773             splitter1Sizes << headerWidth << messageViewerWidth;
0774             splitter2Sizes << ftHeight + ffvHeight << messageViewerHeight;
0775         }
0776     }
0777 
0778     mSplitter1->setSizes(splitter1Sizes);
0779     mSplitter2->setSizes(splitter2Sizes);
0780 
0781     if (mFolderViewSplitter) {
0782         QList<int> splitterSizes;
0783         splitterSizes << ffvHeight << ftHeight;
0784         mFolderViewSplitter->setSizes(splitterSizes);
0785     }
0786 
0787     //
0788     // Now add the splitters to the main layout
0789     //
0790     mTopLayout->addWidget(mSplitter1);
0791 
0792     // Make sure the focus is on the view, and not on the quick search line edit, because otherwise
0793     // shortcuts like + or j go to the wrong place.
0794     // This would normally be done in the message list itself, but apparently something resets the focus
0795     // again, probably all the reparenting we do here.
0796     mMessagePane->focusView();
0797 
0798     // By default hide th unread and size columns on first run.
0799     if (kmkernel->firstStart()) {
0800         mFolderTreeWidget->folderTreeView()->hideColumn(1);
0801         mFolderTreeWidget->folderTreeView()->hideColumn(3);
0802         mFolderTreeWidget->folderTreeView()->header()->resizeSection(0, static_cast<int>(folderViewWidth * 0.8));
0803     }
0804 
0805     // Make the copy action work, see disconnect comment above
0806     if (mMsgView) {
0807         connect(mMsgView->copyAction(), &QAction::triggered, mMsgView, &KMReaderWin::slotCopySelectedText);
0808     }
0809 }
0810 
0811 //-----------------------------------------------------------------------------
0812 void KMMainWidget::refreshFavoriteFoldersViewProperties()
0813 {
0814     if (mFavoriteCollectionsView) {
0815         if (KMKernel::self()->mailCommonSettings()->favoriteCollectionViewMode() == MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::IconMode) {
0816             mFavoriteCollectionsView->changeViewMode(QListView::IconMode);
0817         } else if (KMKernel::self()->mailCommonSettings()->favoriteCollectionViewMode()
0818                    == MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::ListMode) {
0819             mFavoriteCollectionsView->changeViewMode(QListView::ListMode);
0820         } else {
0821             Q_ASSERT(false); // we should never get here in hidden mode
0822         }
0823         mFavoriteCollectionsView->setDropActionMenuEnabled(kmkernel->showPopupAfterDnD());
0824         mFolderTreeWidget->folderTreeView()->setEnableDragDrop(KMailSettings::self()->enableFolderDnD());
0825         mFavoriteCollectionsView->setWordWrap(true);
0826         mFavoriteCollectionsView->updateMode();
0827     }
0828 }
0829 
0830 //-----------------------------------------------------------------------------
0831 void KMMainWidget::readConfig()
0832 {
0833     const bool oldLongFolderList = mLongFolderList;
0834     const bool oldReaderWindowActive = mReaderWindowActive;
0835     const bool oldReaderWindowBelow = mReaderWindowBelow;
0836     const bool oldFavoriteFolderView = mEnableFavoriteFolderView;
0837     const bool oldFolderQuickSearch = mEnableFolderQuickSearch;
0838 
0839     // on startup, the layout is always new and we need to relayout the widgets
0840     bool layoutChanged = !mStartupDone;
0841 
0842     if (mStartupDone) {
0843         readPreConfig();
0844 
0845         layoutChanged = (oldLongFolderList != mLongFolderList) || (oldReaderWindowActive != mReaderWindowActive) || (oldReaderWindowBelow != mReaderWindowBelow)
0846             || (oldFavoriteFolderView != mEnableFavoriteFolderView);
0847 
0848         if (layoutChanged) {
0849             deleteWidgets();
0850             createWidgets();
0851             restoreCollectionFolderViewConfig();
0852             slotShowSelectedFolderInPane();
0853             Q_EMIT recreateGui();
0854         } else if (oldFolderQuickSearch != mEnableFolderQuickSearch) {
0855             if (mEnableFolderQuickSearch) {
0856                 mFolderTreeWidget->filterFolderLineEdit()->show();
0857             } else {
0858                 mFolderTreeWidget->filterFolderLineEdit()->hide();
0859             }
0860         }
0861     }
0862 
0863     {
0864         // Read the config of the folder views and the header
0865         if (mMsgView) {
0866             mMsgView->readConfig();
0867         }
0868         mMessagePane->reloadGlobalConfiguration();
0869         mFolderTreeWidget->readConfig();
0870         if (mFavoriteCollectionsView) {
0871             mFavoriteCollectionsView->readConfig();
0872         }
0873         refreshFavoriteFoldersViewProperties();
0874     }
0875 
0876     {
0877         // area for config group "General"
0878         if (!mStartupDone) {
0879             // check mail on startup
0880             // do it after building the kmmainwin, so that the progressdialog is available
0881             QTimer::singleShot(0, this, &KMMainWidget::slotCheckMailOnStartup);
0882         }
0883     }
0884 
0885     if (layoutChanged) {
0886         layoutSplitters();
0887     }
0888 
0889     updateMessageMenu();
0890     updateFileMenu();
0891     kmkernel->toggleSystemTray();
0892     mAccountActionMenu->setAccountOrder(KMKernel::self()->mailCommonSettings()->order());
0893 
0894     connect(Akonadi::AgentManager::self(), &AgentManager::instanceAdded, this, &KMMainWidget::updateFileMenu);
0895     connect(Akonadi::AgentManager::self(), &AgentManager::instanceRemoved, this, &KMMainWidget::updateFileMenu);
0896 }
0897 
0898 //-----------------------------------------------------------------------------
0899 void KMMainWidget::writeConfig(bool force)
0900 {
0901     // Don't save the sizes of all the widgets when we were never shown.
0902     // This can happen in Kontact, where the KMail plugin is automatically
0903     // loaded, but not necessarily shown.
0904     // This prevents invalid sizes from being saved
0905     if (mWasEverShown) {
0906         // The height of the header widget can be 0, this happens when the user
0907         // did not switch to the header widget onced and the "Welcome to KMail"
0908         // HTML widget was shown the whole time
0909         int headersHeight = mMessagePane->height();
0910         if (headersHeight == 0) {
0911             headersHeight = height() / 2;
0912         }
0913 
0914         KMailSettings::self()->setSearchAndHeaderHeight(headersHeight);
0915         KMailSettings::self()->setSearchAndHeaderWidth(mMessagePane->width());
0916         if (mFavoriteCollectionsView) {
0917             KMKernel::self()->mailCommonSettings()->setFavoriteCollectionViewHeight(mFavoriteCollectionsView->height());
0918             KMailSettings::self()->setFolderTreeHeight(mFolderTreeWidget->height());
0919             if (!mLongFolderList) {
0920                 KMailSettings::self()->setFolderViewHeight(mFolderViewSplitter->height());
0921             }
0922         } else if (!mLongFolderList && mFolderTreeWidget) {
0923             KMailSettings::self()->setFolderTreeHeight(mFolderTreeWidget->height());
0924         }
0925         if (mFolderTreeWidget) {
0926             KMailSettings::self()->setFolderViewWidth(mFolderTreeWidget->width());
0927             KSharedConfig::Ptr config = KMKernel::self()->config();
0928             KConfigGroup group(config, QStringLiteral("CollectionFolderView"));
0929 
0930             ETMViewStateSaver saver;
0931             saver.setView(mFolderTreeWidget->folderTreeView());
0932             saver.saveState(group);
0933 
0934             group.writeEntry("HeaderState", mFolderTreeWidget->folderTreeView()->header()->saveState());
0935             // Work around from startup folder
0936             group.deleteEntry("Selection");
0937 #if 0
0938             if (!KMailSettings::self()->startSpecificFolderAtStartup()) {
0939                 group.deleteEntry("Current");
0940             }
0941 #endif
0942             group.sync();
0943         }
0944 
0945         if (mMsgView) {
0946             if (!mReaderWindowBelow) {
0947                 KMailSettings::self()->setReaderWindowWidth(mMsgView->width());
0948             }
0949             mMsgView->viewer()->writeConfig(force);
0950             KMailSettings::self()->setReaderWindowHeight(mMsgView->height());
0951         }
0952     }
0953 }
0954 
0955 void KMMainWidget::writeReaderConfig()
0956 {
0957     if (mWasEverShown) {
0958         if (mMsgView) {
0959             mMsgView->viewer()->writeConfig();
0960         }
0961     }
0962 }
0963 
0964 KMReaderWin *KMMainWidget::messageView() const
0965 {
0966     return mMsgView;
0967 }
0968 
0969 CollectionPane *KMMainWidget::messageListPane() const
0970 {
0971     return mMessagePane;
0972 }
0973 
0974 Collection KMMainWidget::currentCollection() const
0975 {
0976     return mCurrentCollection;
0977 }
0978 
0979 //-----------------------------------------------------------------------------
0980 void KMMainWidget::deleteWidgets()
0981 {
0982     // Simply delete the top splitter, which always is mSplitter1, regardless
0983     // of the layout. This deletes all children.
0984     // akonadi action manager is created in createWidgets(), parented to this
0985     //  so not autocleaned up.
0986     delete mAkonadiStandardActionManager;
0987     mAkonadiStandardActionManager = nullptr;
0988     auto splitter = mSplitter1;
0989     mMsgView = nullptr;
0990     mFolderViewSplitter = nullptr;
0991     if (mFavoriteCollectionsView) {
0992         static_cast<MailCommon::FavoriteCollectionOrderProxyModel*>(mFavoriteCollectionsView->model())->setSourceModel(nullptr);
0993         mFavoriteCollectionsView->setModel(nullptr);
0994         mFavoriteCollectionsView = nullptr;
0995     }
0996     mFolderTreeWidget = nullptr;
0997     mSplitter1 = nullptr;
0998     mSplitter2 = nullptr;
0999     mFavoritesModel = nullptr;
1000     delete splitter;
1001 }
1002 
1003 //-----------------------------------------------------------------------------
1004 void KMMainWidget::createWidgets()
1005 {
1006     // Note that all widgets we create in this function have the parent 'this'.
1007     // They will be properly reparented in layoutSplitters()
1008 
1009     //
1010     // Create the folder tree
1011     //
1012     FolderTreeWidget::TreeViewOptions opt = FolderTreeWidget::ShowUnreadCount;
1013     opt |= FolderTreeWidget::UseLineEditForFiltering;
1014     opt |= FolderTreeWidget::ShowCollectionStatisticAnimation;
1015     opt |= FolderTreeWidget::DontKeyFilter;
1016     mFolderTreeWidget = new FolderTreeWidget(this, mGUIClient, opt);
1017     mFolderTreeWidget->folderTreeView()->setEnableDragDrop(KMailSettings::self()->enableFolderDnD());
1018 
1019     connect(mFolderTreeWidget->folderTreeView(),
1020             qOverload<const Akonadi::Collection &>(&EntityTreeView::currentChanged),
1021             this,
1022             &KMMainWidget::slotFolderChanged);
1023 
1024     connect(mFolderTreeWidget->folderTreeView()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KMMainWidget::updateFolderMenu);
1025 
1026     connect(mFolderTreeWidget->folderTreeView(), &FolderTreeView::newTabRequested, this, &KMMainWidget::slotCreateNewTab);
1027 
1028     //
1029     // Create the message pane
1030     //
1031     mMessagePane = new CollectionPane(!KMailSettings::self()->startSpecificFolderAtStartup(),
1032                                       KMKernel::self()->entityTreeModel(),
1033                                       mFolderTreeWidget->folderTreeView()->selectionModel(),
1034                                       this);
1035     connect(KMKernel::self()->entityTreeModel(), &Akonadi::EntityTreeModel::collectionFetched, this, &KMMainWidget::slotCollectionFetched);
1036 
1037     mMessagePane->setXmlGuiClient(mGUIClient);
1038     connect(mMessagePane, &MessageList::Pane::messageSelected, this, &KMMainWidget::slotMessageSelected);
1039     connect(mMessagePane, &MessageList::Pane::selectionChanged, this, &KMMainWidget::startUpdateMessageActionsTimer);
1040     connect(mMessagePane, &CollectionPane::currentTabChanged, this, &KMMainWidget::refreshMessageListSelection);
1041     connect(mMessagePane, &MessageList::Pane::messageActivated, this, &KMMainWidget::slotMessageActivated);
1042     connect(mMessagePane, &MessageList::Pane::messageStatusChangeRequest, this, &KMMainWidget::slotMessageStatusChangeRequest);
1043 
1044     connect(mMessagePane, &MessageList::Pane::statusMessage, this, &KMMainWidget::showMessageActivities);
1045 
1046     connect(mMessagePane, &MessageList::Pane::forceLostFocus, this, &KMMainWidget::slotSetFocusToViewer);
1047 
1048     //
1049     // Create the reader window
1050     //
1051     if (mReaderWindowActive) {
1052         mMsgView = new KMReaderWin(this, this, actionCollection());
1053         if (mMsgActions) {
1054             mMsgActions->setMessageView(mMsgView);
1055         }
1056         connect(mMsgView->viewer(), &MessageViewer::Viewer::displayPopupMenu, this, &KMMainWidget::slotMessagePopup);
1057         connect(mMsgView->viewer(), &MessageViewer::Viewer::moveMessageToTrash, this, &KMMainWidget::slotMoveMessageToTrash);
1058         connect(mMsgView->viewer(), &MessageViewer::Viewer::pageIsScrolledToBottom, this, &KMMainWidget::slotPageIsScrolledToBottom);
1059         connect(mMsgView->viewer(), &MessageViewer::Viewer::replyMessageTo, this, &KMMainWidget::slotReplyMessageTo);
1060         connect(mMsgView->viewer(), &MessageViewer::Viewer::showStatusBarMessage, this, &KMMainWidget::setShowStatusBarMessage);
1061         connect(mMsgView->viewer(), &MessageViewer::Viewer::zoomChanged, this, &KMMainWidget::setZoomChanged);
1062         if (mShowIntroductionAction) {
1063             mShowIntroductionAction->setEnabled(true);
1064         }
1065     } else {
1066         if (mMsgActions) {
1067             mMsgActions->setMessageView(nullptr);
1068         }
1069         if (mShowIntroductionAction) {
1070             mShowIntroductionAction->setEnabled(false);
1071         }
1072     }
1073     if (!KMailSettings::self()->enableFolderQuickSearch()) {
1074         mFolderTreeWidget->filterFolderLineEdit()->hide();
1075     }
1076 
1077     //
1078     // Create the favorite folder view
1079     //
1080     mAkonadiStandardActionManager = new Akonadi::StandardMailActionManager(mGUIClient->actionCollection(), this);
1081     connect(mAkonadiStandardActionManager, &Akonadi::StandardMailActionManager::actionStateUpdated, this, &KMMainWidget::slotAkonadiStandardActionUpdated);
1082 
1083     mAkonadiStandardActionManager->setCollectionSelectionModel(mFolderTreeWidget->folderTreeView()->selectionModel());
1084     mAkonadiStandardActionManager->setItemSelectionModel(mMessagePane->currentItemSelectionModel());
1085 
1086     if (mEnableFavoriteFolderView) {
1087         mFavoriteCollectionsView = new FavoriteCollectionWidget(KMKernel::self()->mailCommonSettings(), mGUIClient, this);
1088         refreshFavoriteFoldersViewProperties();
1089         connect(mFavoriteCollectionsView, qOverload<const Akonadi::Collection &>(&EntityListView::currentChanged), this, &KMMainWidget::slotFolderChanged);
1090         connect(mFavoriteCollectionsView, &FavoriteCollectionWidget::newTabRequested, this, &KMMainWidget::slotCreateNewTab);
1091         mFavoritesModel = new Akonadi::FavoriteCollectionsModel(mFolderTreeWidget->folderTreeWidgetProxyModel(),
1092                                                                 KMKernel::self()->config()->group(QStringLiteral("FavoriteCollections")),
1093                                                                 mFavoriteCollectionsView);
1094 
1095         auto orderProxy = new MailCommon::FavoriteCollectionOrderProxyModel(this);
1096         orderProxy->setOrderConfig(KMKernel::self()->config()->group(QStringLiteral("FavoriteCollectionsOrder")));
1097         orderProxy->setSourceModel(mFavoritesModel);
1098         orderProxy->sort(0, Qt::AscendingOrder);
1099 
1100         mFavoriteCollectionsView->setModel(orderProxy);
1101 
1102         mAkonadiStandardActionManager->setFavoriteCollectionsModel(mFavoritesModel);
1103         mAkonadiStandardActionManager->setFavoriteSelectionModel(mFavoriteCollectionsView->selectionModel());
1104     }
1105 
1106     // Don't use mMailActionManager->createAllActions() to save memory by not
1107     // creating actions that doesn't make sense.
1108     const auto standardActions = {
1109         StandardActionManager::CreateCollection,
1110         StandardActionManager::CopyCollections,
1111         StandardActionManager::DeleteCollections,
1112         StandardActionManager::SynchronizeCollections,
1113         StandardActionManager::CollectionProperties,
1114         StandardActionManager::CopyItems,
1115         StandardActionManager::Paste,
1116         StandardActionManager::DeleteItems,
1117         StandardActionManager::ManageLocalSubscriptions,
1118         StandardActionManager::CopyCollectionToMenu,
1119         StandardActionManager::CopyItemToMenu,
1120         StandardActionManager::MoveItemToMenu,
1121         StandardActionManager::MoveCollectionToMenu,
1122         StandardActionManager::CutItems,
1123         StandardActionManager::CutCollections,
1124         StandardActionManager::CreateResource,
1125         StandardActionManager::DeleteResources,
1126         StandardActionManager::ResourceProperties,
1127         StandardActionManager::SynchronizeResources,
1128         StandardActionManager::ToggleWorkOffline,
1129         StandardActionManager::SynchronizeCollectionsRecursive,
1130     };
1131 
1132     for (StandardActionManager::Type standardAction : standardActions) {
1133         mAkonadiStandardActionManager->createAction(standardAction);
1134     }
1135 
1136     if (mEnableFavoriteFolderView) {
1137         const auto favoriteActions = {
1138             StandardActionManager::AddToFavoriteCollections,
1139             StandardActionManager::RemoveFromFavoriteCollections,
1140             StandardActionManager::RenameFavoriteCollection,
1141             StandardActionManager::SynchronizeFavoriteCollections,
1142         };
1143         for (StandardActionManager::Type favoriteAction : favoriteActions) {
1144             mAkonadiStandardActionManager->createAction(favoriteAction);
1145         }
1146     }
1147 
1148     const auto mailActions = {StandardMailActionManager::MarkAllMailAsRead,
1149                               StandardMailActionManager::MoveToTrash,
1150                               StandardMailActionManager::MoveAllToTrash,
1151                               StandardMailActionManager::RemoveDuplicates,
1152                               StandardMailActionManager::EmptyAllTrash,
1153                               StandardMailActionManager::MarkMailAsRead,
1154                               StandardMailActionManager::MarkMailAsUnread,
1155                               StandardMailActionManager::MarkMailAsImportant,
1156                               StandardMailActionManager::MarkMailAsActionItem};
1157 
1158     for (StandardMailActionManager::Type mailAction : mailActions) {
1159         mAkonadiStandardActionManager->createAction(mailAction);
1160     }
1161 
1162     mAkonadiStandardActionManager->interceptAction(Akonadi::StandardActionManager::CollectionProperties);
1163     connect(mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CollectionProperties),
1164             &QAction::triggered,
1165             mManageShowCollectionProperties,
1166             &ManageShowCollectionProperties::slotCollectionProperties);
1167 
1168     //
1169     // Create all kinds of actions
1170     //
1171     mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::RemoveDuplicates)->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Asterisk));
1172     mAkonadiStandardActionManager->interceptAction(Akonadi::StandardMailActionManager::RemoveDuplicates);
1173     connect(mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::RemoveDuplicates),
1174             &QAction::triggered,
1175             this,
1176             &KMMainWidget::slotRemoveDuplicates);
1177 
1178     mCollectionProperties = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CollectionProperties);
1179     connect(kmkernel->folderCollectionMonitor(), &Monitor::collectionRemoved, this, &KMMainWidget::slotCollectionRemoved);
1180     connect(kmkernel->folderCollectionMonitor(), &Monitor::itemAdded, this, &KMMainWidget::slotItemAdded);
1181     connect(kmkernel->folderCollectionMonitor(), &Monitor::itemRemoved, this, &KMMainWidget::slotItemRemoved);
1182     connect(kmkernel->folderCollectionMonitor(), &Monitor::itemMoved, this, &KMMainWidget::slotItemMoved);
1183     connect(kmkernel->folderCollectionMonitor(),
1184             qOverload<const Akonadi::Collection &, const QSet<QByteArray> &>(&ChangeRecorder::collectionChanged),
1185             this,
1186             &KMMainWidget::slotCollectionChanged);
1187 
1188     connect(kmkernel->folderCollectionMonitor(), &Monitor::collectionStatisticsChanged, this, &KMMainWidget::slotCollectionStatisticsChanged);
1189 }
1190 
1191 void KMMainWidget::updateMoveAction(const Akonadi::CollectionStatistics &statistic)
1192 {
1193     const bool hasUnreadMails = (statistic.unreadCount() > 0);
1194     updateMoveAction(hasUnreadMails);
1195 }
1196 
1197 void KMMainWidget::updateMoveAction(bool hasUnreadMails)
1198 {
1199     const bool enable_goto_unread = hasUnreadMails || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllFolders)
1200         || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllMarkedFolders);
1201     actionCollection()->action(QStringLiteral("go_next_unread_message"))->setEnabled(enable_goto_unread);
1202     actionCollection()->action(QStringLiteral("go_prev_unread_message"))->setEnabled(enable_goto_unread);
1203     if (mAkonadiStandardActionManager && mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkAllMailAsRead)) {
1204         mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkAllMailAsRead)->setEnabled(hasUnreadMails);
1205     }
1206 }
1207 
1208 void KMMainWidget::updateAllToTrashAction(qint64 statistics)
1209 {
1210     if (mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)) {
1211         const bool folderWithContent = mCurrentFolderSettings && !mCurrentFolderSettings->isStructural();
1212         mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)
1213             ->setEnabled(folderWithContent && (statistics > 0) && mCurrentFolderSettings->canDeleteMessages());
1214     }
1215 }
1216 
1217 void KMMainWidget::slotCollectionStatisticsChanged(Akonadi::Collection::Id id, const Akonadi::CollectionStatistics &statistic)
1218 {
1219     if (id == CommonKernel->outboxCollectionFolder().id()) {
1220         const bool enableAction = (statistic.count() > 0);
1221         mSendQueued->setEnabled(enableAction);
1222         mSendActionMenu->setEnabled(enableAction);
1223     } else if (id == mCurrentCollection.id()) {
1224         updateMoveAction(statistic);
1225         updateAllToTrashAction(statistic.count());
1226         setCurrentCollection(CommonKernel->collectionFromId(mCurrentCollection.id()));
1227     }
1228 }
1229 
1230 void KMMainWidget::slotCreateNewTab(bool preferNewTab)
1231 {
1232     mMessagePane->setPreferEmptyTab(preferNewTab);
1233 }
1234 
1235 void KMMainWidget::slotCollectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &set)
1236 {
1237     if ((collection == mCurrentCollection) && (set.contains("MESSAGEFOLDER") || set.contains("expirationcollectionattribute"))) {
1238         if (set.contains("MESSAGEFOLDER")) {
1239             mMessagePane->resetModelStorage();
1240         } else {
1241             setCurrentCollection(collection);
1242         }
1243     } else if (set.contains("ENTITYDISPLAY") || set.contains("NAME")) {
1244         const QModelIndex idx = Akonadi::EntityTreeModel::modelIndexForCollection(KMKernel::self()->collectionModel(), collection);
1245         if (idx.isValid()) {
1246             const QString text = idx.data().toString();
1247             const auto icon = idx.data(Qt::DecorationRole).value<QIcon>();
1248             mMessagePane->updateTabIconText(collection, text, icon);
1249         }
1250     }
1251 }
1252 
1253 void KMMainWidget::slotItemAdded(const Akonadi::Item &msg, const Akonadi::Collection &col)
1254 {
1255     Q_UNUSED(msg)
1256     if (col.isValid()) {
1257         if (col == CommonKernel->outboxCollectionFolder()) {
1258             startUpdateMessageActionsTimer();
1259         }
1260     }
1261 }
1262 
1263 void KMMainWidget::slotItemRemoved(const Akonadi::Item &item)
1264 {
1265     if (item.isValid() && item.parentCollection().isValid()) {
1266         HistoryClosedReaderManager::self()->removeItem(item.id());
1267         if (item.parentCollection() == CommonKernel->outboxCollectionFolder()) {
1268             startUpdateMessageActionsTimer();
1269         }
1270     }
1271 }
1272 
1273 void KMMainWidget::slotItemMoved(const Akonadi::Item &item, const Akonadi::Collection &from, const Akonadi::Collection &to)
1274 {
1275     if (item.isValid() && ((from.id() == CommonKernel->outboxCollectionFolder().id()) || to.id() == CommonKernel->outboxCollectionFolder().id())) {
1276         startUpdateMessageActionsTimer();
1277     }
1278 }
1279 
1280 //-------------------------------------------------------------------------
1281 void KMMainWidget::slotFocusQuickSearch()
1282 {
1283     const QString text = mMsgView ? mMsgView->copyText() : QString();
1284     mMessagePane->focusQuickSearch(text);
1285 }
1286 
1287 //-------------------------------------------------------------------------
1288 bool KMMainWidget::showSearchDialog()
1289 {
1290     if (!mSearchWin) {
1291         mSearchWin = new SearchWindow(this, mCurrentCollection);
1292         mSearchWin->setModal(false);
1293         mSearchWin->setObjectName(QLatin1StringView("Search"));
1294     } else {
1295         mSearchWin->activateFolder(mCurrentCollection);
1296     }
1297 
1298     mSearchWin->show();
1299     KWindowSystem::activateWindow(mSearchWin->windowHandle());
1300     return true;
1301 }
1302 
1303 //-----------------------------------------------------------------------------
1304 void KMMainWidget::slotFilter()
1305 {
1306     FilterIf->openFilterDialog(true);
1307 }
1308 
1309 void KMMainWidget::slotManageSieveScripts()
1310 {
1311     if (!kmkernel->askToGoOnline()) {
1312         return;
1313     }
1314     if (mManageSieveDialog) {
1315         return;
1316     }
1317 
1318     mManageSieveDialog = new KSieveUi::ManageSieveScriptsDialog(mSievePasswordProvider);
1319     connect(mManageSieveDialog.data(), &KSieveUi::ManageSieveScriptsDialog::finished, this, &KMMainWidget::slotCheckVacation);
1320     mManageSieveDialog->show();
1321 }
1322 
1323 //-----------------------------------------------------------------------------
1324 void KMMainWidget::slotCheckMail()
1325 {
1326     kmkernel->checkMail();
1327 }
1328 
1329 //-----------------------------------------------------------------------------
1330 void KMMainWidget::slotCheckMailOnStartup()
1331 {
1332     kmkernel->checkMailOnStartup();
1333 }
1334 
1335 void KMMainWidget::slotCompose()
1336 {
1337     auto job = new ComposeNewMessageJob;
1338     job->setFolderSettings(mCurrentFolderSettings);
1339     job->setCurrentCollection(mCurrentCollection);
1340     job->start();
1341 }
1342 
1343 //-----------------------------------------------------------------------------
1344 // TODO: do we want the list sorted alphabetically?
1345 void KMMainWidget::slotShowNewFromTemplate()
1346 {
1347     if (mCurrentFolderSettings) {
1348         const KIdentityManagementCore::Identity &ident = kmkernel->identityManager()->identityForUoidOrDefault(mCurrentFolderSettings->identity());
1349         mTemplateFolder = CommonKernel->collectionFromId(ident.templates().toLongLong());
1350     }
1351 
1352     if (!mTemplateFolder.isValid()) {
1353         mTemplateFolder = CommonKernel->templatesCollectionFolder();
1354     }
1355     if (!mTemplateFolder.isValid()) {
1356         qCWarning(KMAIL_LOG) << "Template folder not found";
1357         return;
1358     }
1359 
1360     mTemplateMenu->menu()->clear();
1361 
1362     auto job = new Akonadi::ItemFetchJob(mTemplateFolder);
1363     job->fetchScope().setAncestorRetrieval(ItemFetchScope::Parent);
1364     job->fetchScope().fetchFullPayload();
1365     connect(job, &Akonadi::ItemFetchJob::result, this, &KMMainWidget::slotDelayedShowNewFromTemplate);
1366 }
1367 
1368 void KMMainWidget::slotDelayedShowNewFromTemplate(KJob *job)
1369 {
1370     auto fetchJob = qobject_cast<Akonadi::ItemFetchJob *>(job);
1371 
1372     const Akonadi::Item::List items = fetchJob->items();
1373     const int numberOfItems = items.count();
1374     for (int idx = 0; idx < numberOfItems; ++idx) {
1375         KMime::Message::Ptr msg = MessageComposer::Util::message(items.at(idx));
1376         if (msg) {
1377             QString subj;
1378             if (auto subject = msg->subject(false)) {
1379                 subj = subject->asUnicodeString();
1380             }
1381 
1382             if (subj.isEmpty()) {
1383                 subj = i18n("No Subject");
1384             }
1385 
1386             QAction *templateAction = mTemplateMenu->menu()->addAction(KStringHandler::rsqueeze(subj.replace(QLatin1Char('&'), QStringLiteral("&&"))));
1387             QVariant var;
1388             var.setValue(items.at(idx));
1389             templateAction->setData(var);
1390         }
1391     }
1392 
1393     // If there are no templates available, add a menu entry which informs
1394     // the user about this.
1395     if (mTemplateMenu->menu()->actions().isEmpty()) {
1396         QAction *noAction = mTemplateMenu->menu()->addAction(i18n("(no templates)"));
1397         noAction->setEnabled(false);
1398     }
1399 }
1400 
1401 //-----------------------------------------------------------------------------
1402 void KMMainWidget::slotNewFromTemplate(QAction *action)
1403 {
1404     if (!mTemplateFolder.isValid()) {
1405         return;
1406     }
1407     const auto item = action->data().value<Akonadi::Item>();
1408     newFromTemplate(item);
1409 }
1410 
1411 //-----------------------------------------------------------------------------
1412 void KMMainWidget::newFromTemplate(const Akonadi::Item &msg)
1413 {
1414     if (!msg.isValid()) {
1415         return;
1416     }
1417     auto command = new KMUseTemplateCommand(this, msg);
1418     command->start();
1419 }
1420 
1421 //-----------------------------------------------------------------------------
1422 void KMMainWidget::slotPostToML()
1423 {
1424     if (mCurrentFolderSettings && mCurrentFolderSettings->isMailingListEnabled()) {
1425         if (KMail::Util::mailingListPost(mCurrentFolderSettings, mCurrentCollection)) {
1426             return;
1427         }
1428     }
1429     slotCompose();
1430 }
1431 
1432 void KMMainWidget::slotExpireFolder()
1433 {
1434     if (!mCurrentFolderSettings) {
1435         return;
1436     }
1437     const MailCommon::ExpireCollectionAttribute *attr = mCurrentCollection.attribute<MailCommon::ExpireCollectionAttribute>();
1438     if (attr) {
1439         bool canBeExpired = true;
1440         if (!attr->isAutoExpire()) {
1441             canBeExpired = false;
1442         } else if (attr->unreadExpireUnits() == MailCommon::ExpireCollectionAttribute::ExpireNever
1443                    && attr->readExpireUnits() == MailCommon::ExpireCollectionAttribute::ExpireNever) {
1444             canBeExpired = false;
1445         }
1446 
1447         if (!canBeExpired) {
1448             const QString message = i18n("This folder does not have any expiry options set");
1449             KMessageBox::information(this, message);
1450             return;
1451         }
1452 
1453         if (KMailSettings::self()->warnBeforeExpire()) {
1454             const QString message = i18n("<qt>Are you sure you want to expire the folder <b>%1</b>?</qt>", mCurrentFolderSettings->name().toHtmlEscaped());
1455             if (KMessageBox::warningContinueCancel(this, message, i18n("Expire Folder"), KGuiItem(i18n("&Expire"))) != KMessageBox::Continue) {
1456                 return;
1457             }
1458         }
1459 
1460         MailCommon::Util::expireOldMessages(mCurrentCollection, true /*immediate*/);
1461     }
1462 }
1463 
1464 //-----------------------------------------------------------------------------
1465 void KMMainWidget::slotEmptyFolder()
1466 {
1467     if (!mCurrentCollection.isValid()) {
1468         return;
1469     }
1470     const bool isTrash = CommonKernel->folderIsTrash(mCurrentCollection);
1471     const QString title = (isTrash) ? i18n("Empty Trash") : i18n("Move to Trash");
1472     const QString text = (isTrash) ? i18n("Are you sure you want to empty the trash folder?")
1473                                    : i18n(
1474                                        "<qt>Are you sure you want to move all messages from "
1475                                        "folder <b>%1</b> to the trash?</qt>",
1476                                        mCurrentCollection.name().toHtmlEscaped());
1477     const QString icon = (isTrash) ? QStringLiteral("edit-delete-shred") : QStringLiteral("edit-delete");
1478 
1479     if (KMessageBox::warningContinueCancel(this, text, title, KGuiItem(title, icon)) != KMessageBox::Continue) {
1480         return;
1481     }
1482     KCursorSaver saver(Qt::WaitCursor);
1483     slotSelectAllMessages();
1484     if (isTrash) {
1485         /* Don't ask for confirmation again when deleting, the user has already
1486         confirmed. */
1487         deleteSelectedMessages(false);
1488     } else {
1489         slotTrashSelectedMessages();
1490     }
1491 
1492     if (mMsgView) {
1493         mMsgView->clearCache();
1494     }
1495 
1496     if (!isTrash) {
1497         const QString str = i18n("Moved all messages to the trash");
1498         showMessageActivities(str);
1499     }
1500 
1501     updateMessageActions();
1502 
1503     // Disable empty trash/move all to trash action - we've just deleted/moved
1504     // all folder contents.
1505     mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)->setEnabled(false);
1506 }
1507 
1508 //-----------------------------------------------------------------------------
1509 void KMMainWidget::slotArchiveFolder()
1510 {
1511     if (mCurrentCollection.isValid()) {
1512         KMail::ArchiveFolderDialog archiveDialog(this);
1513         archiveDialog.setFolder(mCurrentCollection);
1514         archiveDialog.exec();
1515     }
1516 }
1517 
1518 //-----------------------------------------------------------------------------
1519 void KMMainWidget::slotRemoveFolder()
1520 {
1521     if (!mCurrentFolderSettings) {
1522         return;
1523     }
1524     if (!mCurrentFolderSettings->isValid()) {
1525         return;
1526     }
1527     if (mCurrentFolderSettings->isSystemFolder()) {
1528         return;
1529     }
1530     if (mCurrentFolderSettings->isReadOnly()) {
1531         return;
1532     }
1533 
1534     auto job = new RemoveCollectionJob(this);
1535     connect(job, &RemoveCollectionJob::clearCurrentFolder, this, &KMMainWidget::slotClearCurrentFolder);
1536     job->setMainWidget(this);
1537     job->setCurrentFolder(mCurrentCollection);
1538     job->start();
1539 }
1540 
1541 void KMMainWidget::slotClearCurrentFolder()
1542 {
1543     clearCurrentFolder();
1544 }
1545 
1546 //-----------------------------------------------------------------------------
1547 void KMMainWidget::slotExpireAll()
1548 {
1549     if (KMailSettings::self()->warnBeforeExpire()) {
1550         const int ret = KMessageBox::warningContinueCancel(KMainWindow::memberList().constFirst(),
1551                                                            i18n("Are you sure you want to expire all old messages?"),
1552                                                            i18n("Expire Old Messages?"),
1553                                                            KGuiItem(i18n("Expire")));
1554         if (ret != KMessageBox::Continue) {
1555             return;
1556         }
1557     }
1558 
1559     kmkernel->expireAllFoldersNow();
1560 }
1561 
1562 void KMMainWidget::assignLoadExternalReference()
1563 {
1564     if (mFolderHtmlLoadExtPreference) {
1565         mMsgView->setHtmlLoadExtDefault(mFolderHtmlLoadExtPreference);
1566     } else {
1567         mMsgView->setHtmlLoadExtDefault(mHtmlLoadExtGlobalSetting);
1568     }
1569     mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference);
1570 }
1571 
1572 //-----------------------------------------------------------------------------
1573 void KMMainWidget::slotOverrideHtmlLoadExt()
1574 {
1575     if (mHtmlLoadExtGlobalSetting == mFolderHtmlLoadExtPreference) {
1576         int result = KMessageBox::warningContinueCancel(this,
1577                                                         // the warning text is taken from configuredialog.cpp:
1578                                                         i18n("Loading external references in html mail will make you more vulnerable to "
1579                                                              "\"spam\" and may increase the likelihood that your system will be "
1580                                                              "compromised by other present and anticipated security exploits."),
1581                                                         i18n("Security Warning"),
1582                                                         KGuiItem(i18n("Load External References")),
1583                                                         KStandardGuiItem::cancel(),
1584                                                         QStringLiteral("OverrideHtmlLoadExtWarning"),
1585                                                         KMessageBox::Option());
1586         if (result == KMessageBox::Cancel) {
1587             mPreferHtmlLoadExtAction->setChecked(false);
1588             return;
1589         }
1590     }
1591     mFolderHtmlLoadExtPreference = !mFolderHtmlLoadExtPreference;
1592 
1593     if (mMsgView) {
1594         assignLoadExternalReference();
1595         mMsgView->update(true);
1596     }
1597 }
1598 
1599 //-----------------------------------------------------------------------------
1600 void KMMainWidget::slotForwardInlineMsg()
1601 {
1602     if (!mCurrentFolderSettings) {
1603         return;
1604     }
1605 
1606     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
1607     if (selectedMessages.isEmpty()) {
1608         return;
1609     }
1610     const QString text = mMsgView ? mMsgView->copyText() : QString();
1611     auto command = new KMForwardCommand(this, selectedMessages, mCurrentFolderSettings->identity(), QString(), text);
1612 
1613     command->start();
1614 }
1615 
1616 //-----------------------------------------------------------------------------
1617 void KMMainWidget::slotForwardAttachedMessage()
1618 {
1619     if (!mCurrentFolderSettings) {
1620         return;
1621     }
1622 
1623     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
1624     if (selectedMessages.isEmpty()) {
1625         return;
1626     }
1627     auto command = new KMForwardAttachedCommand(this, selectedMessages, mCurrentFolderSettings->identity());
1628 
1629     command->start();
1630 }
1631 
1632 //-----------------------------------------------------------------------------
1633 void KMMainWidget::slotUseTemplate()
1634 {
1635     newFromTemplate(mMessagePane->currentItem());
1636 }
1637 
1638 //-----------------------------------------------------------------------------
1639 // Message moving and permanent deletion
1640 //
1641 
1642 void KMMainWidget::moveMessageSelected(MessageList::Core::MessageItemSetReference ref, const Akonadi::Collection &dest, bool confirmOnDeletion)
1643 {
1644     Akonadi::Item::List selectMsg = mMessagePane->itemListFromPersistentSet(ref);
1645     selectMsg = mPluginCheckBeforeDeletingManagerInterface->confirmBeforeDeleting(selectMsg);
1646     if (selectMsg.isEmpty()) {
1647         mMessagePane->deletePersistentSet(ref);
1648         return;
1649     }
1650     // If this is a deletion, ask for confirmation
1651     if (confirmOnDeletion) {
1652         const int selectedMessageCount = selectMsg.count();
1653         int ret = KMessageBox::warningContinueCancel(this,
1654                                                      i18np("<qt>Do you really want to delete the selected message?<br />"
1655                                                            "Once deleted, it cannot be restored.</qt>",
1656                                                            "<qt>Do you really want to delete the %1 selected messages?<br />"
1657                                                            "Once deleted, they cannot be restored.</qt>",
1658                                                            selectedMessageCount),
1659                                                      selectedMessageCount > 1 ? i18n("Delete Messages") : i18n("Delete Message"),
1660                                                      KStandardGuiItem::del(),
1661                                                      KStandardGuiItem::cancel());
1662         if (ret == KMessageBox::Cancel) {
1663             mMessagePane->deletePersistentSet(ref);
1664             return; // user canceled the action
1665         }
1666     }
1667     mMessagePane->markMessageItemsAsAboutToBeRemoved(ref, true);
1668     // And stuff them into a KMMoveCommand :)
1669     auto command = new KMMoveCommand(dest, selectMsg, ref);
1670     connect(command, &KMMoveCommand::moveDone, this, &KMMainWidget::slotMoveMessagesCompleted);
1671     command->start();
1672 
1673     if (dest.isValid()) {
1674         showMessageActivities(i18n("Moving messages..."));
1675     } else {
1676         showMessageActivities(i18n("Deleting messages..."));
1677     }
1678 }
1679 
1680 void KMMainWidget::slotMoveMessagesCompleted(KMMoveCommand *command)
1681 {
1682     Q_ASSERT(command);
1683     mMessagePane->markMessageItemsAsAboutToBeRemoved(command->refSet(), false);
1684     mMessagePane->deletePersistentSet(command->refSet());
1685     // Bleah :D
1686     const bool moveWasReallyADelete = !command->destFolder().isValid();
1687 
1688     QString str;
1689     if (command->result() == KMCommand::OK) {
1690         if (moveWasReallyADelete) {
1691             str = i18n("Messages deleted successfully.");
1692         } else {
1693             str = i18n("Messages moved successfully.");
1694         }
1695     } else {
1696         if (moveWasReallyADelete) {
1697             if (command->result() == KMCommand::Failed) {
1698                 str = i18n("Deleting messages failed.");
1699             } else {
1700                 str = i18n("Deleting messages canceled.");
1701             }
1702         } else {
1703             if (command->result() == KMCommand::Failed) {
1704                 str = i18n("Moving messages failed.");
1705             } else {
1706                 str = i18n("Moving messages canceled.");
1707             }
1708         }
1709     }
1710     showMessageActivities(str);
1711     // The command will autodelete itself and will also kill the set.
1712 }
1713 
1714 void KMMainWidget::slotDeleteMessages()
1715 {
1716     deleteSelectedMessages(true && !KMailSettings::self()->deleteMessageWithoutConfirmation());
1717 }
1718 
1719 Akonadi::Item::List KMMainWidget::currentSelection() const
1720 {
1721     Akonadi::Item::List selectMsg;
1722     MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet();
1723     if (ref != -1) {
1724         selectMsg = mMessagePane->itemListFromPersistentSet(ref);
1725     }
1726     return selectMsg;
1727 }
1728 
1729 void KMMainWidget::deleteSelectedMessages(bool confirmDelete)
1730 {
1731     // Create a persistent message set from the current selection
1732     MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet();
1733     if (ref != -1) {
1734         moveMessageSelected(ref, Akonadi::Collection(), confirmDelete);
1735     }
1736 }
1737 
1738 void KMMainWidget::slotDeleteThread(bool confirmDelete)
1739 {
1740     // Create a persistent set from the current thread.
1741     MessageList::Core::MessageItemSetReference ref = mMessagePane->currentThreadAsPersistentSet();
1742     if (ref != -1) {
1743         moveMessageSelected(ref, Akonadi::Collection(), confirmDelete);
1744     }
1745 }
1746 
1747 FolderSelectionDialog *KMMainWidget::moveOrCopyToDialog()
1748 {
1749     if (!mMoveOrCopyToDialog) {
1750         FolderSelectionDialog::SelectionFolderOption options = FolderSelectionDialog::HideVirtualFolder;
1751         mMoveOrCopyToDialog = new FolderSelectionDialog(this, options);
1752         mMoveOrCopyToDialog->setModal(true);
1753     }
1754     return mMoveOrCopyToDialog;
1755 }
1756 
1757 FolderSelectionDialog *KMMainWidget::selectFromAllFoldersDialog()
1758 {
1759     if (!mSelectFromAllFoldersDialog) {
1760         FolderSelectionDialog::SelectionFolderOptions options = FolderSelectionDialog::None;
1761         options |= FolderSelectionDialog::NotAllowToCreateNewFolder;
1762 
1763         mSelectFromAllFoldersDialog = new FolderSelectionDialog(this, options);
1764         mSelectFromAllFoldersDialog->setModal(true);
1765     }
1766     return mSelectFromAllFoldersDialog;
1767 }
1768 
1769 void KMMainWidget::slotMoveSelectedMessageToFolder()
1770 {
1771     QPointer<MailCommon::FolderSelectionDialog> dialog(moveOrCopyToDialog());
1772     dialog->setWindowTitle(i18nc("@title:window", "Move Messages to Folder"));
1773     if (dialog->exec() && dialog) {
1774         const Akonadi::Collection dest = dialog->selectedCollection();
1775         if (dest.isValid()) {
1776             moveSelectedMessagesToFolder(dest);
1777         }
1778     }
1779 }
1780 
1781 void KMMainWidget::moveSelectedMessagesToFolder(const Akonadi::Collection &dest)
1782 {
1783     MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet();
1784     if (ref != -1) {
1785         // Need to verify if dest == src ??? akonadi do it for us.
1786         moveMessageSelected(ref, dest, false);
1787     }
1788 }
1789 
1790 void KMMainWidget::copyMessageSelected(const Akonadi::Item::List &selectMsg, const Akonadi::Collection &dest)
1791 {
1792     if (selectMsg.isEmpty()) {
1793         return;
1794     }
1795     // And stuff them into a KMCopyCommand :)
1796     auto command = new KMCopyCommand(dest, selectMsg);
1797     connect(command, &KMCommand::completed, this, &KMMainWidget::slotCopyMessagesCompleted);
1798     command->start();
1799     showMessageActivities(i18n("Copying messages..."));
1800 }
1801 
1802 void KMMainWidget::slotCopyMessagesCompleted(KMCommand *command)
1803 {
1804     Q_ASSERT(command);
1805     QString str;
1806     if (command->result() == KMCommand::OK) {
1807         str = i18n("Messages copied successfully.");
1808     } else {
1809         if (command->result() == KMCommand::Failed) {
1810             str = i18n("Copying messages failed.");
1811         } else {
1812             str = i18n("Copying messages canceled.");
1813         }
1814     }
1815     showMessageActivities(str);
1816     // The command will autodelete itself and will also kill the set.
1817 }
1818 
1819 void KMMainWidget::slotCopySelectedMessagesToFolder()
1820 {
1821     QPointer<MailCommon::FolderSelectionDialog> dialog(moveOrCopyToDialog());
1822     dialog->setWindowTitle(i18nc("@title:window", "Copy Messages to Folder"));
1823 
1824     if (dialog->exec() && dialog) {
1825         const Akonadi::Collection dest = dialog->selectedCollection();
1826         if (dest.isValid()) {
1827             copySelectedMessagesToFolder(dest);
1828         }
1829     }
1830 }
1831 
1832 void KMMainWidget::copySelectedMessagesToFolder(const Akonadi::Collection &dest)
1833 {
1834     const Akonadi::Item::List lstMsg = mMessagePane->selectionAsMessageItemList();
1835     if (!lstMsg.isEmpty()) {
1836         copyMessageSelected(lstMsg, dest);
1837     }
1838 }
1839 
1840 //-----------------------------------------------------------------------------
1841 // Message trashing
1842 //
1843 void KMMainWidget::trashMessageSelected(MessageList::Core::MessageItemSetReference ref)
1844 {
1845     if (!mCurrentCollection.isValid()) {
1846         return;
1847     }
1848 
1849     Akonadi::Item::List select = mMessagePane->itemListFromPersistentSet(ref);
1850 
1851     select = mPluginCheckBeforeDeletingManagerInterface->confirmBeforeDeleting(select);
1852     if (select.isEmpty()) {
1853         mMessagePane->deletePersistentSet(ref);
1854         return;
1855     }
1856 
1857     mMessagePane->markMessageItemsAsAboutToBeRemoved(ref, true);
1858 
1859     // FIXME: Why we don't use KMMoveCommand( trashFolder(), selectedMessages ); ?
1860     // And stuff them into a KMTrashMsgCommand :)
1861     auto command = new KMTrashMsgCommand(mCurrentCollection, select, ref);
1862 
1863     connect(command, &KMTrashMsgCommand::moveDone, this, &KMMainWidget::slotTrashMessagesCompleted);
1864     command->start();
1865     switch (command->operation()) {
1866     case KMTrashMsgCommand::MoveToTrash:
1867         showMessageActivities(i18n("Moving messages to trash..."));
1868         break;
1869     case KMTrashMsgCommand::Delete:
1870         showMessageActivities(i18n("Deleting messages..."));
1871         break;
1872     case KMTrashMsgCommand::Both:
1873     case KMTrashMsgCommand::Unknown:
1874         showMessageActivities(i18n("Deleting and moving messages to trash..."));
1875         break;
1876     }
1877 }
1878 
1879 void KMMainWidget::slotTrashMessagesCompleted(KMTrashMsgCommand *command)
1880 {
1881     Q_ASSERT(command);
1882     mMessagePane->markMessageItemsAsAboutToBeRemoved(command->refSet(), false);
1883     mMessagePane->deletePersistentSet(command->refSet());
1884     if (command->result() == KMCommand::OK) {
1885         switch (command->operation()) {
1886         case KMTrashMsgCommand::MoveToTrash:
1887             showMessageActivities(i18n("Messages moved to trash successfully."));
1888             break;
1889         case KMTrashMsgCommand::Delete:
1890             showMessageActivities(i18n("Messages deleted successfully."));
1891             break;
1892         case KMTrashMsgCommand::Both:
1893         case KMTrashMsgCommand::Unknown:
1894             showMessageActivities(i18n("Messages moved to trash or deleted successfully"));
1895             break;
1896         }
1897     } else if (command->result() == KMCommand::Failed) {
1898         switch (command->operation()) {
1899         case KMTrashMsgCommand::MoveToTrash:
1900             showMessageActivities(i18n("Moving messages to trash failed."));
1901             break;
1902         case KMTrashMsgCommand::Delete:
1903             showMessageActivities(i18n("Deleting messages failed."));
1904             break;
1905         case KMTrashMsgCommand::Both:
1906         case KMTrashMsgCommand::Unknown:
1907             showMessageActivities(i18n("Deleting or moving messages to trash failed."));
1908             break;
1909         }
1910     } else {
1911         switch (command->operation()) {
1912         case KMTrashMsgCommand::MoveToTrash:
1913             showMessageActivities(i18n("Moving messages to trash canceled."));
1914             break;
1915         case KMTrashMsgCommand::Delete:
1916             showMessageActivities(i18n("Deleting messages canceled."));
1917             break;
1918         case KMTrashMsgCommand::Both:
1919         case KMTrashMsgCommand::Unknown:
1920             showMessageActivities(i18n("Deleting or moving messages to trash canceled."));
1921             break;
1922         }
1923     }
1924 
1925     // The command will autodelete itself and will also kill the set.
1926 }
1927 
1928 void KMMainWidget::slotTrashSelectedMessages()
1929 {
1930     MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet();
1931     if (ref != -1) {
1932         trashMessageSelected(ref);
1933     }
1934 }
1935 
1936 void KMMainWidget::slotTrashThread()
1937 {
1938     MessageList::Core::MessageItemSetReference ref = mMessagePane->currentThreadAsPersistentSet();
1939     if (ref != -1) {
1940         trashMessageSelected(ref);
1941     }
1942 }
1943 
1944 //-----------------------------------------------------------------------------
1945 // Message tag setting for messages
1946 //
1947 // FIXME: The "selection" version of these functions is in MessageActions.
1948 //        We should probably move everything there....
1949 void KMMainWidget::toggleMessageSetTag(const Akonadi::Item::List &select, const Akonadi::Tag &tag)
1950 {
1951     if (select.isEmpty()) {
1952         return;
1953     }
1954     auto command = new KMSetTagCommand(Akonadi::Tag::List() << tag, select, KMSetTagCommand::Toggle);
1955     command->start();
1956 }
1957 
1958 void KMMainWidget::slotSelectMoreMessageTagList()
1959 {
1960     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
1961     if (selectedMessages.isEmpty()) {
1962         return;
1963     }
1964 
1965     QPointer<TagSelectDialog> dlg = new TagSelectDialog(this, selectedMessages.count(), selectedMessages.first());
1966     dlg->setActionCollection(QList<KActionCollection *>{actionCollection()});
1967     if (dlg->exec()) {
1968         const Akonadi::Tag::List lst = dlg->selectedTag();
1969         auto command = new KMSetTagCommand(lst, selectedMessages, KMSetTagCommand::CleanExistingAndAddNew);
1970         command->start();
1971     }
1972     delete dlg;
1973 }
1974 
1975 void KMMainWidget::slotUpdateMessageTagList(const Akonadi::Tag &tag)
1976 {
1977     // Create a persistent set from the current thread.
1978     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
1979     if (selectedMessages.isEmpty()) {
1980         return;
1981     }
1982     toggleMessageSetTag(selectedMessages, tag);
1983 }
1984 
1985 void KMMainWidget::refreshMessageListSelection()
1986 {
1987     setCurrentCollection(mMessagePane->currentFolder());
1988     mAkonadiStandardActionManager->setItemSelectionModel(mMessagePane->currentItemSelectionModel());
1989     slotMessageSelected(mMessagePane->currentItem());
1990     Q_EMIT captionChangeRequest(MailCommon::Util::fullCollectionPath(mMessagePane->currentFolder()));
1991 }
1992 
1993 //-----------------------------------------------------------------------------
1994 // Status setting for threads
1995 //
1996 // FIXME: The "selection" version of these functions is in MessageActions.
1997 //        We should probably move everything there....
1998 void KMMainWidget::setMessageSetStatus(const Akonadi::Item::List &select, Akonadi::MessageStatus status, bool toggle)
1999 {
2000     auto command = new KMSetStatusCommand(status, select, toggle);
2001     command->start();
2002 }
2003 
2004 void KMMainWidget::setCurrentThreadStatus(Akonadi::MessageStatus status, bool toggle)
2005 {
2006     const Akonadi::Item::List select = mMessagePane->currentThreadAsMessageList();
2007     if (select.isEmpty()) {
2008         return;
2009     }
2010     setMessageSetStatus(select, status, toggle);
2011 }
2012 
2013 void KMMainWidget::slotSetThreadStatusUnread()
2014 {
2015     setCurrentThreadStatus(MessageStatus::statusRead(), true);
2016 }
2017 
2018 void KMMainWidget::slotSetThreadStatusImportant()
2019 {
2020     setCurrentThreadStatus(MessageStatus::statusImportant(), true);
2021 }
2022 
2023 void KMMainWidget::slotSetThreadStatusRead()
2024 {
2025     setCurrentThreadStatus(MessageStatus::statusRead(), false);
2026 }
2027 
2028 void KMMainWidget::slotSetThreadStatusToAct()
2029 {
2030     setCurrentThreadStatus(MessageStatus::statusToAct(), true);
2031 }
2032 
2033 void KMMainWidget::slotSetThreadStatusWatched()
2034 {
2035     setCurrentThreadStatus(MessageStatus::statusWatched(), true);
2036     if (mWatchThreadAction->isChecked()) {
2037         mIgnoreThreadAction->setChecked(false);
2038     }
2039 }
2040 
2041 void KMMainWidget::slotSetThreadStatusIgnored()
2042 {
2043     setCurrentThreadStatus(MessageStatus::statusIgnored(), true);
2044     if (mIgnoreThreadAction->isChecked()) {
2045         mWatchThreadAction->setChecked(false);
2046     }
2047 }
2048 
2049 //-----------------------------------------------------------------------------
2050 void KMMainWidget::slotRedirectMessage()
2051 {
2052     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2053     if (selectedMessages.isEmpty()) {
2054         return;
2055     }
2056 
2057     auto command = new KMRedirectCommand(this, selectedMessages);
2058     command->start();
2059 }
2060 
2061 void KMMainWidget::slotNewMessageToRecipients()
2062 {
2063     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2064     if (selectedMessages.count() != 1) {
2065         return;
2066     }
2067 
2068     auto job = new ComposeNewMessageJob;
2069     job->setFolderSettings(mCurrentFolderSettings);
2070     job->setCurrentCollection(mCurrentCollection);
2071     job->setRecipientsFromMessage(selectedMessages.constFirst());
2072     job->start();
2073 }
2074 
2075 //-----------------------------------------------------------------------------
2076 void KMMainWidget::slotCustomReplyToMsg(const QString &tmpl)
2077 {
2078     const Akonadi::Item msg = mMessagePane->currentItem();
2079     if (!msg.isValid()) {
2080         return;
2081     }
2082 
2083     const QString text = mMsgView ? mMsgView->copyText() : QString();
2084 
2085     qCDebug(KMAIL_LOG) << "Reply with template:" << tmpl;
2086 
2087     auto command = new KMReplyCommand(this, msg, MessageComposer::ReplySmart, text, false, tmpl);
2088     command->start();
2089 }
2090 
2091 //-----------------------------------------------------------------------------
2092 void KMMainWidget::slotCustomReplyAllToMsg(const QString &tmpl)
2093 {
2094     const Akonadi::Item msg = mMessagePane->currentItem();
2095     if (!msg.isValid()) {
2096         return;
2097     }
2098 
2099     const QString text = mMsgView ? mMsgView->copyText() : QString();
2100 
2101     qCDebug(KMAIL_LOG) << "Reply to All with template:" << tmpl;
2102 
2103     auto command = new KMReplyCommand(this, msg, MessageComposer::ReplyAll, text, false, tmpl);
2104     command->setReplyAsHtml(messageView() ? messageView()->htmlMail() : false);
2105     command->start();
2106 }
2107 
2108 //-----------------------------------------------------------------------------
2109 void KMMainWidget::slotCustomForwardMsg(const QString &tmpl)
2110 {
2111     if (!mCurrentFolderSettings) {
2112         return;
2113     }
2114 
2115     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2116     if (selectedMessages.isEmpty()) {
2117         return;
2118     }
2119     const QString text = mMsgView ? mMsgView->copyText() : QString();
2120     qCDebug(KMAIL_LOG) << "Forward with template:" << tmpl;
2121     auto command = new KMForwardCommand(this, selectedMessages, mCurrentFolderSettings->identity(), tmpl, text);
2122 
2123     command->start();
2124 }
2125 
2126 void KMMainWidget::openFilterDialog(const QByteArray &field, const QString &value)
2127 {
2128     FilterIf->openFilterDialog(false);
2129     FilterIf->createFilter(field, value);
2130 }
2131 
2132 //-----------------------------------------------------------------------------
2133 void KMMainWidget::slotSubjectFilter()
2134 {
2135     const KMime::Message::Ptr msg = mMessagePane->currentMessage();
2136     if (!msg) {
2137         return;
2138     }
2139     QString str;
2140     if (auto subject = msg->subject(false)) {
2141         str = subject->asUnicodeString();
2142     }
2143     openFilterDialog("Subject", str);
2144 }
2145 
2146 //-----------------------------------------------------------------------------
2147 void KMMainWidget::slotFromFilter()
2148 {
2149     KMime::Message::Ptr msg = mMessagePane->currentMessage();
2150     if (!msg) {
2151         return;
2152     }
2153 
2154     AddrSpecList al = MessageHelper::extractAddrSpecs(msg, "From");
2155     if (al.empty()) {
2156         openFilterDialog("From", msg->from()->asUnicodeString());
2157     } else {
2158         openFilterDialog("From", al.front().asString());
2159     }
2160 }
2161 
2162 //-----------------------------------------------------------------------------
2163 void KMMainWidget::slotToFilter()
2164 {
2165     KMime::Message::Ptr msg = mMessagePane->currentMessage();
2166     if (!msg) {
2167         return;
2168     }
2169     openFilterDialog("To", msg->to()->asUnicodeString());
2170 }
2171 
2172 void KMMainWidget::slotCcFilter()
2173 {
2174     KMime::Message::Ptr msg = mMessagePane->currentMessage();
2175     if (!msg) {
2176         return;
2177     }
2178     openFilterDialog("Cc", msg->cc()->asUnicodeString());
2179 }
2180 
2181 //-----------------------------------------------------------------------------
2182 void KMMainWidget::slotUndo()
2183 {
2184     kmkernel->undoStack()->undo();
2185     updateMessageActions();
2186     updateFolderMenu();
2187 }
2188 
2189 //-----------------------------------------------------------------------------
2190 void KMMainWidget::slotJumpToFolder()
2191 {
2192     QPointer<MailCommon::FolderSelectionDialog> dialog(selectFromAllFoldersDialog());
2193     dialog->setWindowTitle(i18nc("@title:window", "Jump to Folder"));
2194     if (dialog->exec() && dialog) {
2195         Akonadi::Collection collection = dialog->selectedCollection();
2196         if (collection.isValid()) {
2197             slotSelectCollectionFolder(collection);
2198         }
2199     }
2200 }
2201 
2202 void KMMainWidget::slotSelectCollectionFolder(const Akonadi::Collection &col)
2203 {
2204     if (mFolderTreeWidget) {
2205         mFolderTreeWidget->selectCollectionFolder(col);
2206         slotFolderChanged(col); // call it explicitly in case the collection is filtered out in the foldertreeview
2207     }
2208 }
2209 
2210 void KMMainWidget::slotApplyFilters()
2211 {
2212     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2213     if (selectedMessages.isEmpty()) {
2214         return;
2215     }
2216     applyFilters(selectedMessages);
2217 }
2218 
2219 Akonadi::Collection::List KMMainWidget::applyFilterOnCollection(bool recursive)
2220 {
2221     Akonadi::Collection::List cols;
2222     if (recursive) {
2223         cols = KMKernel::self()->subfolders(mCurrentCollection);
2224     } else {
2225         cols << mCurrentCollection;
2226     }
2227     return cols;
2228 }
2229 
2230 void KMMainWidget::slotApplyFiltersOnFolder(bool recursive)
2231 {
2232     if (mCurrentCollection.isValid()) {
2233         const Akonadi::Collection::List cols = applyFilterOnCollection(recursive);
2234         applyFilters(cols);
2235     }
2236 }
2237 
2238 void KMMainWidget::slotApplyFilterOnFolder(bool recursive)
2239 {
2240     if (mCurrentCollection.isValid()) {
2241         const Akonadi::Collection::List cols = applyFilterOnCollection(recursive);
2242         auto action = qobject_cast<QAction *>(sender());
2243         applyFilter(cols, action->property("filter_id").toString());
2244     }
2245 }
2246 
2247 void KMMainWidget::applyFilters(const Akonadi::Item::List &selectedMessages)
2248 {
2249     KCursorSaver saver(Qt::WaitCursor);
2250 
2251     MailCommon::FilterManager::instance()->filter(selectedMessages);
2252 }
2253 
2254 void KMMainWidget::applyFilters(const Akonadi::Collection::List &selectedCols)
2255 {
2256     KCursorSaver saver(Qt::WaitCursor);
2257     MailCommon::FilterManager::instance()->filter(selectedCols);
2258 }
2259 
2260 void KMMainWidget::applyFilter(const Akonadi::Collection::List &selectedCols, const QString &filter)
2261 {
2262     KCursorSaver saver(Qt::WaitCursor);
2263     MailCommon::FilterManager::instance()->filter(selectedCols, {filter});
2264 }
2265 
2266 //-----------------------------------------------------------------------------
2267 void KMMainWidget::slotCheckVacation()
2268 {
2269     updateVacationScriptStatus(false);
2270     if (!kmkernel->askToGoOnline()) {
2271         return;
2272     }
2273 
2274     mVacationManager->checkVacation();
2275 }
2276 
2277 void KMMainWidget::slotEditCurrentVacation()
2278 {
2279     slotEditVacation(QString());
2280 }
2281 
2282 void KMMainWidget::slotEditVacation(const QString &serverName)
2283 {
2284     if (!kmkernel->askToGoOnline()) {
2285         return;
2286     }
2287 
2288     mVacationManager->slotEditVacation(serverName);
2289 }
2290 
2291 //-----------------------------------------------------------------------------
2292 void KMMainWidget::slotDebugSieve()
2293 {
2294     if (kmkernel->allowToDebug()) {
2295         QPointer<KSieveUi::SieveDebugDialog> mSieveDebugDialog = new KSieveUi::SieveDebugDialog(mSievePasswordProvider, this);
2296         mSieveDebugDialog->exec();
2297         delete mSieveDebugDialog;
2298     }
2299 }
2300 
2301 void KMMainWidget::slotConfigChanged()
2302 {
2303     readConfig();
2304     mMsgActions->setupForwardActions(actionCollection());
2305     mMsgActions->setupForwardingActionsList(mGUIClient);
2306 }
2307 
2308 //-----------------------------------------------------------------------------
2309 void KMMainWidget::slotSaveMsg()
2310 {
2311     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2312     if (selectedMessages.isEmpty()) {
2313         return;
2314     }
2315     auto saveCommand = new KMSaveMsgCommand(this, selectedMessages);
2316     saveCommand->start();
2317 }
2318 
2319 //-----------------------------------------------------------------------------
2320 void KMMainWidget::slotOpenMsg()
2321 {
2322     auto openCommand = new KMOpenMsgCommand(this, QUrl(), overrideEncoding(), this);
2323 
2324     openCommand->start();
2325 }
2326 
2327 //-----------------------------------------------------------------------------
2328 void KMMainWidget::slotSaveAttachments()
2329 {
2330     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2331     if (selectedMessages.isEmpty()) {
2332         return;
2333     }
2334     if (!mMsgView) {
2335         return;
2336     }
2337     // Avoid re-downloading in the common case that only one message is selected, and the message
2338     // is also displayed in the viewer. For this, create a dummy item without a parent collection / item id,
2339     // so that KMCommand doesn't download it.
2340     KMSaveAttachmentsCommand *saveCommand = nullptr;
2341     if (mMsgView && selectedMessages.size() == 1 && mMsgView->messageItem().hasPayload<KMime::Message::Ptr>()
2342         && selectedMessages.first().id() == mMsgView->messageItem().id()) {
2343         Akonadi::Item dummyItem;
2344         dummyItem.setPayload<KMime::Message::Ptr>(mMsgView->messageItem().payload<KMime::Message::Ptr>());
2345         saveCommand = new KMSaveAttachmentsCommand(this, dummyItem, mMsgView->viewer());
2346     } else {
2347         saveCommand = new KMSaveAttachmentsCommand(this, selectedMessages, mMsgView->viewer());
2348     }
2349 
2350     saveCommand->start();
2351 }
2352 
2353 //-----------------------------------------------------------------------------
2354 
2355 static std::pair<int, int> countMessages(const Akonadi::Item::List &items)
2356 {
2357     int signedMsgs = 0;
2358     int encryptedMsgs = 0;
2359     for (const auto &item : items) {
2360         if (item.flags().contains(Akonadi::MessageFlags::Signed)) {
2361             ++signedMsgs;
2362         }
2363         if (item.flags().contains(Akonadi::MessageFlags::Encrypted)) {
2364             ++encryptedMsgs;
2365         }
2366     }
2367 
2368     return {signedMsgs, encryptedMsgs};
2369 }
2370 
2371 void KMMainWidget::slotDeleteAttachments()
2372 {
2373     const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2374     if (selectedMessages.isEmpty()) {
2375         return;
2376     }
2377     if (!mMsgView) {
2378         return;
2379     }
2380 
2381     const auto [signedMessages, encryptedMessages] = countMessages(selectedMessages);
2382     if (encryptedMessages == selectedMessages.size()) {
2383         KMessageBox::information(
2384             this,
2385             i18np("The selected message is encrypted. It's currently not possible to delete attachments from encrypted messages.",
2386                   "The selection contains only encrypted messages, from which it's currently not possible to delete attachments.",
2387                   encryptedMessages),
2388             i18ncp("@title:window", "Delete Attachments from Encrypted Message", "Delete Attachments from Encrypted Messages", encryptedMessages));
2389         return;
2390     }
2391 
2392     QString message = i18np(
2393         "<html><p>Do you really want to delete all attachments from the selected message?<br />"
2394         "Once deleted, they cannot be restored.</p></html>",
2395         "<html>Do you really want to delete all attachments from the %1 selected messages?<br />"
2396         "Once deleted, they cannot be restored.</html>",
2397         selectedMessages.size());
2398     if (signedMessages > 0) {
2399         message += i18n(
2400             "<html><p>Note that deleting attachments from a cryptographically signed message "
2401             "will invalidate its signature.</p></html>");
2402     }
2403     if (encryptedMessages > 0) {
2404         message += i18n(
2405             "<html><p>It's not currently possible to delete attachments from encrypted messages.<br />"
2406             "Encrypted messages will be ignored.</p></html>");
2407     }
2408 
2409     const int ret =
2410         KMessageBox::warningContinueCancel(this, message, i18nc("@title:window", "Delete Attachments"), KStandardGuiItem::del(), KStandardGuiItem::cancel());
2411     if (ret == KMessageBox::Cancel) {
2412         return; // user canceled the action
2413     }
2414 
2415     auto deleteCommand = new KMDeleteAttachmentsCommand(this, selectedMessages);
2416     deleteCommand->start();
2417 }
2418 
2419 void KMMainWidget::slotOnlineStatus()
2420 {
2421     // KMKernel will emit a signal when we toggle the network state that is caught by
2422     // KMMainWidget::slotUpdateOnlineStatus to update our GUI
2423     if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Online) {
2424         // if online; then toggle and set it offline.
2425         kmkernel->stopNetworkJobs();
2426     } else {
2427         kmkernel->resumeNetworkJobs();
2428         slotCheckVacation();
2429     }
2430 }
2431 
2432 void KMMainWidget::slotUpdateOnlineStatus(KMailSettings::EnumNetworkState::type)
2433 {
2434     if (!mAkonadiStandardActionManager) {
2435         return;
2436     }
2437     QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::ToggleWorkOffline);
2438     if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Online) {
2439         action->setText(i18n("Work Offline"));
2440         action->setIcon(QIcon::fromTheme(QStringLiteral("user-offline")));
2441     } else {
2442         action->setText(i18n("Work Online"));
2443         action->setIcon(QIcon::fromTheme(QStringLiteral("user-online")));
2444     }
2445 }
2446 
2447 //-----------------------------------------------------------------------------
2448 void KMMainWidget::slotSendQueued()
2449 {
2450     if (kmkernel->msgSender()) {
2451         if (!kmkernel->msgSender()->sendQueued()) {
2452             KNotification::event(QStringLiteral("sent-mail-error"),
2453                                  i18n("Send Email"),
2454                                  i18n("Impossible to send email"),
2455                                  QStringLiteral("kmail"),
2456                                  KNotification::CloseOnTimeout);
2457         }
2458     }
2459 }
2460 
2461 //-----------------------------------------------------------------------------
2462 void KMMainWidget::slotSendQueuedVia(MailTransport::Transport *transport)
2463 {
2464     if (transport) {
2465         if (kmkernel->msgSender()) {
2466             if (!kmkernel->msgSender()->sendQueued(transport->id())) {
2467                 KNotification::event(QStringLiteral("sent-mail-error"),
2468                                      i18n("Send Email"),
2469                                      i18n("Impossible to send email"),
2470                                      QStringLiteral("kmail"),
2471                                      KNotification::CloseOnTimeout);
2472             }
2473         }
2474     }
2475 }
2476 
2477 //-----------------------------------------------------------------------------
2478 void KMMainWidget::slotShowBusySplash()
2479 {
2480     if (mReaderWindowActive) {
2481         mMsgView->displayBusyPage();
2482     }
2483 }
2484 
2485 void KMMainWidget::showOfflinePage()
2486 {
2487     if (!mReaderWindowActive) {
2488         return;
2489     }
2490 
2491     mMsgView->displayOfflinePage();
2492 }
2493 
2494 void KMMainWidget::showResourceOfflinePage()
2495 {
2496     if (!mReaderWindowActive) {
2497         return;
2498     }
2499 
2500     mMsgView->displayResourceOfflinePage();
2501 }
2502 
2503 void KMMainWidget::slotFocusOnNextMessage()
2504 {
2505     mMessagePane->focusNextMessageItem(MessageList::Core::MessageTypeAny, true, false);
2506 }
2507 
2508 void KMMainWidget::slotFocusOnPrevMessage()
2509 {
2510     mMessagePane->focusPreviousMessageItem(MessageList::Core::MessageTypeAny, true, false);
2511 }
2512 
2513 void KMMainWidget::slotSelectFirstMessage()
2514 {
2515     mMessagePane->selectFirstMessageItem(MessageList::Core::MessageTypeAny, true);
2516 }
2517 
2518 void KMMainWidget::slotSelectLastMessage()
2519 {
2520     mMessagePane->selectLastMessageItem(MessageList::Core::MessageTypeAny, true);
2521 }
2522 
2523 void KMMainWidget::slotSelectFocusedMessage()
2524 {
2525     mMessagePane->selectFocusedMessageItem(true);
2526 }
2527 
2528 void KMMainWidget::slotSelectNextMessage()
2529 {
2530     mMessagePane->selectNextMessageItem(MessageList::Core::MessageTypeAny, MessageList::Core::ClearExistingSelection, true, false);
2531 }
2532 
2533 void KMMainWidget::slotExtendSelectionToNextMessage()
2534 {
2535     mMessagePane->selectNextMessageItem(MessageList::Core::MessageTypeAny,
2536                                         MessageList::Core::GrowOrShrinkExistingSelection,
2537                                         true, // center item
2538                                         false // don't loop in folder
2539     );
2540 }
2541 
2542 void KMMainWidget::slotSelectNextUnreadMessage()
2543 {
2544     // The looping logic is: "Don't loop" just never loops, "Loop in current folder"
2545     // loops just in current folder, "Loop in all folders" loops in the current folder
2546     // first and then after confirmation jumps to the next folder.
2547     // A bad point here is that if you answer "No, and don't ask me again" to the confirmation
2548     // dialog then you have "Loop in current folder" and "Loop in all folders" that do
2549     // the same thing and no way to get the old behaviour. However, after a consultation on #kontact,
2550     // for bug-to-bug backward compatibility, the masters decided to keep it b0rken :D
2551     // If nobody complains, it stays like it is: if you complain enough maybe the masters will
2552     // decide to reconsider :)
2553     if (!mMessagePane->selectNextMessageItem(MessageList::Core::MessageTypeUnreadOnly,
2554                                              MessageList::Core::ClearExistingSelection,
2555                                              true, // center item
2556                                              KMailSettings::self()->loopOnGotoUnread() != KMailSettings::EnumLoopOnGotoUnread::DontLoop)) {
2557         // no next unread message was found in the current folder
2558         if ((KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllFolders)
2559             || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllMarkedFolders)) {
2560             mGoToFirstUnreadMessageInSelectedFolder = true;
2561             mFolderTreeWidget->folderTreeView()->selectNextUnreadFolder(true);
2562             mGoToFirstUnreadMessageInSelectedFolder = false;
2563         }
2564     }
2565 }
2566 
2567 void KMMainWidget::slotSelectPreviousMessage()
2568 {
2569     mMessagePane->selectPreviousMessageItem(MessageList::Core::MessageTypeAny, MessageList::Core::ClearExistingSelection, true, false);
2570 }
2571 
2572 void KMMainWidget::slotExtendSelectionToPreviousMessage()
2573 {
2574     mMessagePane->selectPreviousMessageItem(MessageList::Core::MessageTypeAny,
2575                                             MessageList::Core::GrowOrShrinkExistingSelection,
2576                                             true, // center item
2577                                             false // don't loop in folder
2578     );
2579 }
2580 
2581 void KMMainWidget::slotSelectPreviousUnreadMessage()
2582 {
2583     if (!mMessagePane->selectPreviousMessageItem(MessageList::Core::MessageTypeUnreadOnly,
2584                                                  MessageList::Core::ClearExistingSelection,
2585                                                  true, // center item
2586                                                  KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInCurrentFolder)) {
2587         // no next unread message was found in the current folder
2588         if ((KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllFolders)
2589             || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllMarkedFolders)) {
2590             mGoToFirstUnreadMessageInSelectedFolder = true;
2591             mFolderTreeWidget->folderTreeView()->selectPrevUnreadFolder();
2592             mGoToFirstUnreadMessageInSelectedFolder = false;
2593         }
2594     }
2595 }
2596 
2597 void KMMainWidget::slotDisplayCurrentMessage()
2598 {
2599     if (mMessagePane->currentItem().isValid() && !mMessagePane->searchEditHasFocus()) {
2600         slotMessageActivated(mMessagePane->currentItem());
2601     }
2602 }
2603 
2604 // Called by double-clicked or 'Enter' in the messagelist -> pop up reader window
2605 void KMMainWidget::slotMessageActivated(const Akonadi::Item &msg)
2606 {
2607     if (!mCurrentCollection.isValid() || !msg.isValid()) {
2608         return;
2609     }
2610 
2611     if (CommonKernel->folderIsDraftOrOutbox(mCurrentCollection)) {
2612         mMsgActions->setCurrentMessage(msg);
2613         mMsgActions->editCurrentMessage();
2614         return;
2615     }
2616 
2617     if (CommonKernel->folderIsTemplates(mCurrentCollection)) {
2618         slotUseTemplate();
2619         return;
2620     }
2621 
2622     KMReaderMainWin *win = nullptr;
2623     if (!mMsgView) {
2624         win = new KMReaderMainWin(mFolderDisplayFormatPreference, mFolderHtmlLoadExtPreference);
2625     }
2626     // Try to fetch the mail, even in offline mode, it might be cached
2627     auto cmd = new KMFetchMessageCommand(this, msg, win ? win->viewer() : mMsgView->viewer(), win);
2628     connect(cmd, &KMCommand::completed, this, &KMMainWidget::slotItemsFetchedForActivation);
2629     cmd->start();
2630 }
2631 
2632 void KMMainWidget::slotItemsFetchedForActivation(KMCommand *command)
2633 {
2634     KMCommand::Result result = command->result();
2635     if (result != KMCommand::OK) {
2636         qCDebug(KMAIL_LOG) << "slotItemsFetchedForActivation result:" << result;
2637         return;
2638     }
2639 
2640     auto fetchCmd = qobject_cast<KMFetchMessageCommand *>(command);
2641     const Item msg = fetchCmd->item();
2642     KMReaderMainWin *win = fetchCmd->readerMainWin();
2643     if (!win) {
2644         win = new KMReaderMainWin(mFolderDisplayFormatPreference, mFolderHtmlLoadExtPreference);
2645     }
2646     win->showMessage(overrideEncoding(), msg, CommonKernel->collectionFromId(msg.parentCollection().id()));
2647     const bool useFixedFont = mMsgView ? mMsgView->isFixedFont() : MessageViewer::MessageViewerSettings::self()->useFixedFont();
2648     win->setUseFixedFont(useFixedFont);
2649     if (mMsgView) {
2650         auto viewer = mMsgView->viewer();
2651         if (viewer) {
2652             win->viewer()->setWebViewZoomFactor(viewer->webViewZoomFactor());
2653             win->viewer()->setHtmlLoadExtOverride(viewer->htmlLoadExtOverride());
2654             win->viewer()->setDisplayFormatMessageOverwrite(viewer->displayFormatMessageOverwrite());
2655         }
2656     }
2657     win->show();
2658 }
2659 
2660 void KMMainWidget::slotMessageStatusChangeRequest(const Akonadi::Item &item, const Akonadi::MessageStatus &set, const Akonadi::MessageStatus &clear)
2661 {
2662     if (!item.isValid()) {
2663         return;
2664     }
2665 
2666     if (clear.toQInt32() != Akonadi::MessageStatus().toQInt32()) {
2667         auto command = new KMSetStatusCommand(clear, Akonadi::Item::List() << item, true);
2668         command->start();
2669     }
2670 
2671     if (set.toQInt32() != Akonadi::MessageStatus().toQInt32()) {
2672         auto command = new KMSetStatusCommand(set, Akonadi::Item::List() << item, false);
2673         command->start();
2674     }
2675 }
2676 
2677 //-----------------------------------------------------------------------------
2678 void KMMainWidget::slotSelectAllMessages()
2679 {
2680     mMessagePane->selectAll();
2681     updateMessageActions();
2682 }
2683 
2684 void KMMainWidget::slotMessagePopup(const Akonadi::Item &msg, const WebEngineViewer::WebHitTestResult &result, QPoint aPoint)
2685 {
2686     QUrl aUrl = result.linkUrl();
2687     QUrl imageUrl = result.imageUrl();
2688     updateMessageMenu();
2689 
2690     const QString email = KEmailAddress::firstEmailAddress(aUrl.path()).toLower();
2691     if (aUrl.scheme() == QLatin1StringView("mailto") && !email.isEmpty()) {
2692         auto job = new Akonadi::ContactSearchJob(this);
2693         job->setLimit(1);
2694         job->setQuery(Akonadi::ContactSearchJob::Email, email, Akonadi::ContactSearchJob::ExactMatch);
2695         job->setProperty("msg", QVariant::fromValue(msg));
2696         job->setProperty("point", aPoint);
2697         job->setProperty("imageUrl", imageUrl);
2698         job->setProperty("url", aUrl);
2699         job->setProperty("webhitresult", QVariant::fromValue(result));
2700         connect(job, &Akonadi::ContactSearchJob::result, this, &KMMainWidget::slotContactSearchJobForMessagePopupDone);
2701     } else {
2702         showMessagePopup(msg, aUrl, imageUrl, aPoint, false, false, result);
2703     }
2704 }
2705 
2706 void KMMainWidget::slotContactSearchJobForMessagePopupDone(KJob *job)
2707 {
2708     const Akonadi::ContactSearchJob *searchJob = qobject_cast<Akonadi::ContactSearchJob *>(job);
2709     const bool contactAlreadyExists = !searchJob->contacts().isEmpty();
2710 
2711     const Akonadi::Item::List listContact = searchJob->items();
2712     const bool uniqueContactFound = (listContact.count() == 1);
2713     if (uniqueContactFound) {
2714         mMsgView->setContactItem(listContact.first(), searchJob->contacts().at(0));
2715     } else {
2716         mMsgView->clearContactItem();
2717     }
2718     const auto msg = job->property("msg").value<Akonadi::Item>();
2719     const QPoint aPoint = job->property("point").toPoint();
2720     const QUrl imageUrl = job->property("imageUrl").toUrl();
2721     const QUrl url = job->property("url").toUrl();
2722     const auto result = job->property("webhitresult").value<WebEngineViewer::WebHitTestResult>();
2723 
2724     showMessagePopup(msg, url, imageUrl, aPoint, contactAlreadyExists, uniqueContactFound, result);
2725 }
2726 
2727 void KMMainWidget::showMessagePopup(const Akonadi::Item &msg,
2728                                     const QUrl &url,
2729                                     const QUrl &imageUrl,
2730                                     const QPoint &aPoint,
2731                                     bool contactAlreadyExists,
2732                                     bool uniqueContactFound,
2733                                     const WebEngineViewer::WebHitTestResult &result)
2734 {
2735     QMenu menu(this);
2736     bool urlMenuAdded = false;
2737 
2738     if (!url.isEmpty()) {
2739         if (url.scheme() == QLatin1StringView("mailto")) {
2740             // popup on a mailto URL
2741             menu.addAction(mMsgView->mailToComposeAction());
2742             menu.addAction(mMsgView->mailToReplyAction());
2743             menu.addAction(mMsgView->mailToForwardAction());
2744 
2745             menu.addSeparator();
2746 
2747             if (contactAlreadyExists) {
2748                 if (uniqueContactFound) {
2749                     menu.addAction(mMsgView->editContactAction());
2750                 } else {
2751                     menu.addAction(mMsgView->openAddrBookAction());
2752                 }
2753             } else {
2754                 menu.addAction(mMsgView->addAddrBookAction());
2755                 menu.addAction(mMsgView->addToExistingContactAction());
2756             }
2757             menu.addSeparator();
2758             menu.addMenu(mMsgView->viewHtmlOption());
2759             menu.addSeparator();
2760             menu.addAction(mMsgView->copyURLAction());
2761             urlMenuAdded = true;
2762         } else if (url.scheme() != QLatin1StringView("attachment")) {
2763             // popup on a not-mailto URL
2764             menu.addAction(mMsgView->urlOpenAction());
2765             menu.addAction(mMsgView->addUrlToBookmarkAction());
2766             menu.addAction(mMsgView->urlSaveAsAction());
2767             menu.addAction(mMsgView->copyURLAction());
2768             menu.addSeparator();
2769             menu.addAction(mMsgView->shareServiceUrlMenu());
2770             menu.addActions(mMsgView->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedUrl));
2771             if (!imageUrl.isEmpty()) {
2772                 mMsgView->addImageMenuActions(&menu);
2773             }
2774             urlMenuAdded = true;
2775         }
2776         qCDebug(KMAIL_LOG) << "URL is:" << url;
2777     }
2778     const QString selectedText = mMsgView ? mMsgView->copyText() : QString();
2779     if (mMsgView && !selectedText.isEmpty()) {
2780         if (urlMenuAdded) {
2781             menu.addSeparator();
2782         }
2783         menu.addAction(mMsgActions->replyMenu());
2784         menu.addSeparator();
2785 
2786         menu.addAction(mMsgView->copyAction());
2787         menu.addAction(mMsgView->selectAllAction());
2788         menu.addSeparator();
2789         mMsgActions->addWebShortcutsMenu(&menu, selectedText);
2790         menu.addSeparator();
2791         menu.addActions(mMsgView->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedSelection));
2792 #ifdef HAVE_TEXT_TO_SPEECH_SUPPORT
2793         menu.addSeparator();
2794         menu.addAction(mMsgView->speakTextAction());
2795 #endif
2796         menu.addSeparator();
2797         menu.addAction(mMsgView->shareTextAction());
2798     } else if (!urlMenuAdded) {
2799         // popup somewhere else (i.e., not a URL) on the message
2800         if (!mMessagePane->currentMessage()) {
2801             // no messages
2802             return;
2803         }
2804         Akonadi::Collection parentCol = msg.parentCollection();
2805         if (parentCol.isValid() && CommonKernel->folderIsTemplates(parentCol)) {
2806             menu.addAction(mMsgActions->newMessageFromTemplateAction());
2807         } else {
2808             menu.addAction(mMsgActions->replyMenu());
2809             menu.addAction(mMsgActions->forwardMenu());
2810         }
2811         if (parentCol.isValid() && CommonKernel->folderIsSentMailFolder(parentCol)) {
2812             menu.addAction(mMsgActions->sendAgainAction());
2813         } else {
2814             menu.addAction(mMsgActions->editAsNewAction());
2815         }
2816         menu.addAction(mailingListActionMenu());
2817         menu.addSeparator();
2818 
2819         menu.addAction(mCopyActionMenu);
2820         menu.addAction(mMoveActionMenu);
2821 
2822         menu.addSeparator();
2823 
2824         menu.addAction(mMsgActions->messageStatusMenu());
2825         menu.addSeparator();
2826         if (mMsgView) {
2827             if (!imageUrl.isEmpty()) {
2828                 mMsgView->addImageMenuActions(&menu);
2829                 menu.addSeparator();
2830             }
2831             menu.addSeparator();
2832             menu.addAction(mMsgActions->printPreviewAction());
2833             menu.addAction(mMsgActions->printAction());
2834             menu.addSeparator();
2835         }
2836         menu.addAction(mSaveAsAction);
2837         menu.addSeparator();
2838         menu.addAction(mSaveAttachmentsAction);
2839         menu.addAction(mDeleteAttachmentsAction);
2840         menu.addSeparator();
2841         menu.addAction(mMsgActions->exportToPdfAction());
2842         menu.addSeparator();
2843         if (parentCol.isValid() && CommonKernel->folderIsTrash(parentCol)) {
2844             menu.addAction(mDeleteAction);
2845         } else {
2846             menu.addAction(akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash));
2847         }
2848         menu.addSeparator();
2849 
2850         if (mMsgView) {
2851             if (mMsgView->dkimViewerMenu()) {
2852                 menu.addMenu(mMsgView->dkimViewerMenu()->menu());
2853                 menu.addSeparator();
2854             }
2855             if (mMsgView->remoteContentMenu()) {
2856                 menu.addMenu(mMsgView->remoteContentMenu());
2857                 menu.addSeparator();
2858             }
2859             menu.addActions(mPluginCheckBeforeDeletingManagerInterface->actions());
2860             menu.addSeparator();
2861 
2862             menu.addActions(mMsgView->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedMessage));
2863             menu.addSeparator();
2864         }
2865         menu.addAction(mMsgActions->addFollowupReminderAction());
2866         if (kmkernel->allowToDebug()) {
2867             menu.addSeparator();
2868             menu.addAction(mMsgActions->debugAkonadiSearchAction());
2869             menu.addSeparator();
2870             menu.addAction(mMsgView->developmentToolsAction());
2871         }
2872     }
2873     if (mMsgView) {
2874         const QList<QAction *> interceptorUrlActions = mMsgView->interceptorUrlActions(result);
2875         if (!interceptorUrlActions.isEmpty()) {
2876             menu.addSeparator();
2877             menu.addActions(interceptorUrlActions);
2878         }
2879     }
2880 
2881     KAcceleratorManager::manage(&menu);
2882     menu.exec(aPoint, nullptr);
2883 }
2884 
2885 void KMMainWidget::setZoomChanged(qreal zoomFactor)
2886 {
2887     if (mZoomLabelIndicator) {
2888         mZoomLabelIndicator->setZoom(zoomFactor);
2889     }
2890 }
2891 
2892 void KMMainWidget::setupActions()
2893 {
2894     KMailPluginInterface::self()->setParentWidget(this);
2895     KMailPluginInterface::self()->createPluginInterface();
2896     mMsgActions = new KMail::MessageActions(actionCollection(), this);
2897     mMsgActions->fillAkonadiStandardAction(mAkonadiStandardActionManager);
2898     mMsgActions->setMessageView(mMsgView);
2899 
2900     //----- File Menu
2901     mSaveAsAction = new QAction(QIcon::fromTheme(QStringLiteral("document-save")), i18n("Save &As..."), this);
2902     actionCollection()->addAction(QStringLiteral("file_save_as"), mSaveAsAction);
2903     connect(mSaveAsAction, &QAction::triggered, this, &KMMainWidget::slotSaveMsg);
2904     actionCollection()->setDefaultShortcut(mSaveAsAction, KStandardShortcut::save().first());
2905 
2906     mOpenAction = KStandardAction::open(this, &KMMainWidget::slotOpenMsg, actionCollection());
2907 
2908     mOpenRecentMenu = new KRecentFilesMenu(this);
2909     actionCollection()->addAction(QStringLiteral("kmail_file_open_recent"), mOpenRecentMenu->menuAction());
2910     connect(mOpenRecentMenu, &KRecentFilesMenu::urlTriggered, this, &KMMainWidget::slotOpenRecentMessage);
2911     {
2912         auto action = new QAction(i18n("&Expire All Folders"), this);
2913         actionCollection()->addAction(QStringLiteral("expire_all_folders"), action);
2914         connect(action, &QAction::triggered, this, &KMMainWidget::slotExpireAll);
2915     }
2916     {
2917         auto action = new QAction(QIcon::fromTheme(QStringLiteral("mail-receive")), i18n("Check &Mail"), this);
2918         actionCollection()->addAction(QStringLiteral("check_mail"), action);
2919         connect(action, &QAction::triggered, this, &KMMainWidget::slotCheckMail);
2920         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_L));
2921     }
2922 
2923     mAccountActionMenu = new KActionMenuAccount(this);
2924     mAccountActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("mail-receive")));
2925     mAccountActionMenu->setText(i18n("Check Mail In"));
2926 
2927     mAccountActionMenu->setIconText(i18n("Check Mail"));
2928     mAccountActionMenu->setToolTip(i18n("Check Mail"));
2929     actionCollection()->addAction(QStringLiteral("check_mail_in"), mAccountActionMenu);
2930     connect(mAccountActionMenu, &KActionMenu::triggered, this, &KMMainWidget::slotCheckMail);
2931 
2932     mSendQueued = new QAction(QIcon::fromTheme(QStringLiteral("mail-send")), i18n("&Send Queued Messages"), this);
2933     actionCollection()->addAction(QStringLiteral("send_queued"), mSendQueued);
2934     connect(mSendQueued, &QAction::triggered, this, &KMMainWidget::slotSendQueued);
2935     {
2936         QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::ToggleWorkOffline);
2937         mAkonadiStandardActionManager->interceptAction(Akonadi::StandardActionManager::ToggleWorkOffline);
2938         action->setCheckable(false);
2939         connect(action, &QAction::triggered, this, &KMMainWidget::slotOnlineStatus);
2940         action->setText(i18n("Online status (unknown)"));
2941     }
2942 
2943     mSendActionMenu = new KActionMenuTransport(this);
2944     mSendActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("mail-send")));
2945     mSendActionMenu->setText(i18n("Send Queued Messages Via"));
2946     actionCollection()->addAction(QStringLiteral("send_queued_via"), mSendActionMenu);
2947 
2948     connect(mSendActionMenu, &KActionMenuTransport::transportSelected, this, &KMMainWidget::slotSendQueuedVia);
2949 
2950     //----- Tools menu
2951     if (parent()->inherits("KMMainWin")) {
2952         auto action = new QAction(QIcon::fromTheme(QStringLiteral("x-office-address-book")), i18n("&Address Book"), this);
2953         actionCollection()->addAction(QStringLiteral("addressbook"), action);
2954         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotRunAddressBook);
2955         if (QStandardPaths::findExecutable(QStringLiteral("kaddressbook")).isEmpty()) {
2956             action->setEnabled(false);
2957         }
2958     }
2959 
2960     {
2961         auto action = new QAction(QIcon::fromTheme(QStringLiteral("pgp-keys")), i18n("Certificate Manager"), this);
2962         actionCollection()->addAction(QStringLiteral("tools_start_certman"), action);
2963         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotStartCertManager);
2964         // disable action if no certman binary is around
2965         if (QStandardPaths::findExecutable(QStringLiteral("kleopatra")).isEmpty()) {
2966             action->setEnabled(false);
2967         }
2968     }
2969 
2970     {
2971         auto action = new QAction(QIcon::fromTheme(QStringLiteral("document-import")), i18n("&Import Messages..."), this);
2972         actionCollection()->addAction(QStringLiteral("import"), action);
2973         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotImport);
2974         if (QStandardPaths::findExecutable(QStringLiteral("akonadiimportwizard")).isEmpty()) {
2975             action->setEnabled(false);
2976         }
2977     }
2978 
2979     {
2980         if (kmkernel->allowToDebug()) {
2981             auto action = new QAction(i18n("&Debug Sieve..."), this);
2982             actionCollection()->addAction(QStringLiteral("tools_debug_sieve"), action);
2983             connect(action, &QAction::triggered, this, &KMMainWidget::slotDebugSieve);
2984         }
2985     }
2986 
2987     {
2988         auto action = new QAction(i18n("Filter &Log Viewer..."), this);
2989         actionCollection()->addAction(QStringLiteral("filter_log_viewer"), action);
2990         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotFilterLogViewer);
2991     }
2992     {
2993         auto action = new QAction(i18n("Notification History..."), this);
2994         actionCollection()->addAction(QStringLiteral("notification_history"), action);
2995         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotShowNotificationHistory);
2996     }
2997     {
2998         auto action = new QAction(i18n("&Import from another Email Client..."), this);
2999         actionCollection()->addAction(QStringLiteral("importWizard"), action);
3000         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotImportWizard);
3001     }
3002     if (KSieveCore::Util::allowOutOfOfficeSettings()) {
3003         auto action = new QAction(i18n("Edit \"Out of Office\" Replies..."), this);
3004         actionCollection()->addAction(QStringLiteral("tools_edit_vacation"), action);
3005         connect(action, &QAction::triggered, this, &KMMainWidget::slotEditCurrentVacation);
3006     }
3007 
3008     {
3009         auto action = new QAction(i18n("&Configure Automatic Archiving..."), this);
3010         actionCollection()->addAction(QStringLiteral("tools_automatic_archiving"), action);
3011         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotConfigureAutomaticArchiving);
3012     }
3013 
3014     {
3015         auto action = new QAction(i18n("Delayed Messages..."), this);
3016         actionCollection()->addAction(QStringLiteral("message_delayed"), action);
3017         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotConfigureSendLater);
3018     }
3019 
3020     {
3021         auto action = new QAction(i18n("Followup Reminder Messages..."), this);
3022         actionCollection()->addAction(QStringLiteral("followup_reminder_messages"), action);
3023         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotConfigureFollowupReminder);
3024     }
3025 
3026     // Disable the standard action delete key sortcut.
3027     QAction *const standardDelAction = akonadiStandardAction(Akonadi::StandardActionManager::DeleteItems);
3028     standardDelAction->setShortcut(QKeySequence());
3029 
3030     //----- Edit Menu
3031 
3032     mDeleteAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action Hard delete, bypassing trash", "&Delete"), this);
3033     actionCollection()->addAction(QStringLiteral("delete"), mDeleteAction);
3034     connect(mDeleteAction, &QAction::triggered, this, &KMMainWidget::slotDeleteMessages);
3035     actionCollection()->setDefaultShortcut(mDeleteAction, QKeySequence(Qt::SHIFT | Qt::Key_Delete));
3036 
3037     mTrashThreadAction = new QAction(i18n("M&ove Thread to Trash"), this);
3038     actionCollection()->addAction(QStringLiteral("move_thread_to_trash"), mTrashThreadAction);
3039     actionCollection()->setDefaultShortcut(mTrashThreadAction, QKeySequence(Qt::CTRL | Qt::Key_Delete));
3040     mTrashThreadAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
3041     KMail::Util::addQActionHelpText(mTrashThreadAction, i18n("Move thread to trashcan"));
3042     connect(mTrashThreadAction, &QAction::triggered, this, &KMMainWidget::slotTrashThread);
3043 
3044     mDeleteThreadAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete-shred")), i18n("Delete T&hread"), this);
3045     actionCollection()->addAction(QStringLiteral("delete_thread"), mDeleteThreadAction);
3046     // Don't use new connect api.
3047     connect(mDeleteThreadAction, &QAction::triggered, this, &KMMainWidget::slotDeleteThread);
3048     actionCollection()->setDefaultShortcut(mDeleteThreadAction, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Delete));
3049 
3050     mSearchMessages = new QAction(QIcon::fromTheme(QStringLiteral("edit-find-mail")), i18n("&Find Messages..."), this);
3051     actionCollection()->addAction(QStringLiteral("search_messages"), mSearchMessages);
3052     connect(mSearchMessages, &QAction::triggered, this, &KMMainWidget::slotRequestFullSearchFromQuickSearch);
3053     actionCollection()->setDefaultShortcut(mSearchMessages, QKeySequence(Qt::Key_S));
3054 
3055     mSelectAllMessages = new QAction(i18n("Select &All Messages"), this);
3056     actionCollection()->addAction(QStringLiteral("mark_all_messages"), mSelectAllMessages);
3057     connect(mSelectAllMessages, &QAction::triggered, this, &KMMainWidget::slotSelectAllMessages);
3058     actionCollection()->setDefaultShortcut(mSelectAllMessages, QKeySequence(Qt::CTRL | Qt::Key_A));
3059 
3060     //----- Folder Menu
3061 
3062     mFolderMailingListPropertiesAction = new QAction(i18n("&Mailing List Management..."), this);
3063     actionCollection()->addAction(QStringLiteral("folder_mailinglist_properties"), mFolderMailingListPropertiesAction);
3064     connect(mFolderMailingListPropertiesAction,
3065             &QAction::triggered,
3066             mManageShowCollectionProperties,
3067             &ManageShowCollectionProperties::slotFolderMailingListProperties);
3068     // mFolderMailingListPropertiesAction->setIcon(QIcon::fromTheme("document-properties-mailing-list"));
3069 
3070     mClearFolderCacheAction = new QAction(i18n("&Clear Akonadi Cache..."), this);
3071     actionCollection()->addAction(QStringLiteral("folder_clear_akonadi_cache"), mClearFolderCacheAction);
3072     connect(mClearFolderCacheAction, &QAction::triggered, this, &KMMainWidget::slotClearFolder);
3073 
3074     {
3075         auto action = new QAction(i18n("&Clear Akonadi Cache in This Folder and All its Subfolders"), this);
3076         actionCollection()->addAction(QStringLiteral("folder_clear_akonadi_cache_and_subfolders"), action);
3077         connect(action, &QAction::triggered, this, &KMMainWidget::slotClearFolderAndSubFolders);
3078     }
3079 
3080     mShowFolderShortcutDialogAction = new QAction(QIcon::fromTheme(QStringLiteral("configure-shortcuts")), i18n("&Assign Shortcut..."), this);
3081     actionCollection()->addAction(QStringLiteral("folder_shortcut_command"), mShowFolderShortcutDialogAction);
3082     connect(mShowFolderShortcutDialogAction,
3083             &QAction::triggered,
3084             mManageShowCollectionProperties,
3085             &ManageShowCollectionProperties::slotShowFolderShortcutDialog);
3086     // FIXME: this action is not currently enabled in the rc file, but even if
3087     // it were there is inconsistency between the action name and action.
3088     // "Expiration Settings" implies that this will lead to a settings dialog
3089     // and it should be followed by a "...", but slotExpireFolder() performs
3090     // an immediate expiry.
3091     //
3092     // TODO: expire action should be disabled if there is no content or if
3093     // the folder can't delete messages.
3094     //
3095     // Leaving the action here for the moment, it and the "Expire" option in the
3096     // folder popup menu should be combined or at least made consistent.  Same for
3097     // slotExpireFolder() and FolderViewItem::slotShowExpiryProperties().
3098     mExpireFolderAction = new QAction(i18n("&Expiration Settings"), this);
3099     actionCollection()->addAction(QStringLiteral("expire"), mExpireFolderAction);
3100     connect(mExpireFolderAction, &QAction::triggered, this, &KMMainWidget::slotExpireFolder);
3101 
3102     mAkonadiStandardActionManager->interceptAction(Akonadi::StandardMailActionManager::MoveToTrash);
3103     connect(mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveToTrash),
3104             &QAction::triggered,
3105             this,
3106             &KMMainWidget::slotTrashSelectedMessages);
3107 
3108     mAkonadiStandardActionManager->interceptAction(Akonadi::StandardMailActionManager::MoveAllToTrash);
3109     connect(mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash),
3110             &QAction::triggered,
3111             this,
3112             &KMMainWidget::slotEmptyFolder);
3113 
3114     mAkonadiStandardActionManager->interceptAction(Akonadi::StandardActionManager::DeleteCollections);
3115     connect(mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections),
3116             &QAction::triggered,
3117             this,
3118             &KMMainWidget::slotRemoveFolder);
3119 
3120     // ### PORT ME: Add this to the context menu. Not possible right now because
3121     //              the context menu uses XMLGUI, and that would add the entry to
3122     //              all collection context menus
3123     mArchiveFolderAction = new QAction(i18n("&Archive Folder..."), this);
3124     actionCollection()->addAction(QStringLiteral("archive_folder"), mArchiveFolderAction);
3125     connect(mArchiveFolderAction, &QAction::triggered, this, &KMMainWidget::slotArchiveFolder);
3126 
3127     mDisplayMessageFormatMenu = new DisplayMessageFormatActionMenu(this);
3128     connect(mDisplayMessageFormatMenu, &DisplayMessageFormatActionMenu::changeDisplayMessageFormat, this, &KMMainWidget::slotChangeDisplayMessageFormat);
3129     actionCollection()->addAction(QStringLiteral("display_format_message"), mDisplayMessageFormatMenu);
3130 
3131     mPreferHtmlLoadExtAction = new KToggleAction(i18n("Load E&xternal References"), this);
3132     actionCollection()->addAction(QStringLiteral("prefer_html_external_refs"), mPreferHtmlLoadExtAction);
3133     connect(mPreferHtmlLoadExtAction, &KToggleAction::triggered, this, &KMMainWidget::slotOverrideHtmlLoadExt);
3134 
3135     {
3136         QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyCollections);
3137         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_C));
3138     }
3139     {
3140         QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::Paste);
3141         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_V));
3142     }
3143     {
3144         QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyItems);
3145         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::CTRL | Qt::Key_C));
3146     }
3147     {
3148         QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CutItems);
3149         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::CTRL | Qt::Key_X));
3150     }
3151 
3152     {
3153         QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyItemToMenu);
3154         action->setText(i18n("Copy Message To..."));
3155         action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::MoveItemToMenu);
3156         action->setText(i18n("Move Message To..."));
3157     }
3158 
3159     //----- Message Menu
3160     {
3161         auto action = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new")), i18n("&New Message..."), this);
3162         actionCollection()->addAction(QStringLiteral("new_message"), action);
3163         action->setIconText(i18nc("@action:intoolbar New Empty Message", "New"));
3164         connect(action, &QAction::triggered, this, &KMMainWidget::slotCompose);
3165         // do not set a New shortcut if kmail is a component
3166         if (kmkernel->xmlGuiInstanceName().isEmpty()) {
3167             actionCollection()->setDefaultShortcut(action, KStandardShortcut::openNew().first());
3168         }
3169     }
3170 
3171     mTemplateMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Message From &Template"), actionCollection());
3172     mTemplateMenu->setPopupMode(QToolButton::DelayedPopup);
3173     actionCollection()->addAction(QStringLiteral("new_from_template"), mTemplateMenu);
3174     connect(mTemplateMenu->menu(), &QMenu::aboutToShow, this, &KMMainWidget::slotShowNewFromTemplate);
3175     connect(mTemplateMenu->menu(), &QMenu::triggered, this, &KMMainWidget::slotNewFromTemplate);
3176 
3177     mMessageNewList = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new-list")), i18n("New Message t&o Mailing-List..."), this);
3178     actionCollection()->addAction(QStringLiteral("post_message"), mMessageNewList);
3179     connect(mMessageNewList, &QAction::triggered, this, &KMMainWidget::slotPostToML);
3180     actionCollection()->setDefaultShortcut(mMessageNewList, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_N));
3181 
3182     //----- Create filter actions
3183     mFilterMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("&Create Filter"), this);
3184     actionCollection()->addAction(QStringLiteral("create_filter"), mFilterMenu);
3185     connect(mFilterMenu, &QAction::triggered, this, &KMMainWidget::slotFilter);
3186     {
3187         auto action = new QAction(i18n("Filter on &Subject..."), this);
3188         actionCollection()->addAction(QStringLiteral("subject_filter"), action);
3189         connect(action, &QAction::triggered, this, &KMMainWidget::slotSubjectFilter);
3190         mFilterMenu->addAction(action);
3191     }
3192 
3193     {
3194         auto action = new QAction(i18n("Filter on &From..."), this);
3195         actionCollection()->addAction(QStringLiteral("from_filter"), action);
3196         connect(action, &QAction::triggered, this, &KMMainWidget::slotFromFilter);
3197         mFilterMenu->addAction(action);
3198     }
3199     {
3200         auto action = new QAction(i18n("Filter on &To..."), this);
3201         actionCollection()->addAction(QStringLiteral("to_filter"), action);
3202         connect(action, &QAction::triggered, this, &KMMainWidget::slotToFilter);
3203         mFilterMenu->addAction(action);
3204     }
3205     {
3206         auto action = new QAction(i18n("Filter on &Cc..."), this);
3207         actionCollection()->addAction(QStringLiteral("cc_filter"), action);
3208         connect(action, &QAction::triggered, this, &KMMainWidget::slotCcFilter);
3209         mFilterMenu->addAction(action);
3210     }
3211     mFilterMenu->addAction(mMsgActions->listFilterAction());
3212 
3213     mRestoreClosedMessageMenu = new HistoryClosedReaderMenu(this);
3214     actionCollection()->addAction(QStringLiteral("restore_closed_messageviewer"), mRestoreClosedMessageMenu);
3215     connect(mRestoreClosedMessageMenu, &HistoryClosedReaderMenu::openMessage, this, &KMMainWidget::slotRestoreClosedMessage);
3216     mRestoreClosedMessageMenu->createReOpenClosedAction();
3217     actionCollection()->addAction(QStringLiteral("restore_reopen_closed_messageviewer"), mRestoreClosedMessageMenu->reopenAction());
3218 
3219     //----- "Mark Thread" submenu
3220     mThreadStatusMenu = new KActionMenu(i18n("Mark &Thread"), this);
3221     actionCollection()->addAction(QStringLiteral("thread_status"), mThreadStatusMenu);
3222 
3223     mMarkThreadAsReadAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-mark-read")), i18n("Mark Thread as &Read"), this);
3224     actionCollection()->addAction(QStringLiteral("thread_read"), mMarkThreadAsReadAction);
3225     connect(mMarkThreadAsReadAction, &QAction::triggered, this, &KMMainWidget::slotSetThreadStatusRead);
3226     KMail::Util::addQActionHelpText(mMarkThreadAsReadAction, i18n("Mark all messages in the selected thread as read"));
3227     mThreadStatusMenu->addAction(mMarkThreadAsReadAction);
3228 
3229     mMarkThreadAsUnreadAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-mark-unread")), i18n("Mark Thread as &Unread"), this);
3230     actionCollection()->addAction(QStringLiteral("thread_unread"), mMarkThreadAsUnreadAction);
3231     connect(mMarkThreadAsUnreadAction, &QAction::triggered, this, &KMMainWidget::slotSetThreadStatusUnread);
3232     KMail::Util::addQActionHelpText(mMarkThreadAsUnreadAction, i18n("Mark all messages in the selected thread as unread"));
3233     mThreadStatusMenu->addAction(mMarkThreadAsUnreadAction);
3234 
3235     mThreadStatusMenu->addSeparator();
3236 
3237     //----- "Mark Thread" toggle actions
3238     mToggleThreadImportantAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-mark-important")), i18n("Mark Thread as &Important"), this);
3239     actionCollection()->addAction(QStringLiteral("thread_flag"), mToggleThreadImportantAction);
3240     connect(mToggleThreadImportantAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusImportant);
3241     mToggleThreadImportantAction->setCheckedState(KGuiItem(i18n("Remove &Important Thread Mark")));
3242     mThreadStatusMenu->addAction(mToggleThreadImportantAction);
3243 
3244     mToggleThreadToActAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-mark-task")), i18n("Mark Thread as &Action Item"), this);
3245     actionCollection()->addAction(QStringLiteral("thread_toact"), mToggleThreadToActAction);
3246     connect(mToggleThreadToActAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusToAct);
3247     mToggleThreadToActAction->setCheckedState(KGuiItem(i18n("Remove &Action Item Thread Mark")));
3248     mThreadStatusMenu->addAction(mToggleThreadToActAction);
3249 
3250     //------- "Watch and ignore thread" actions
3251     mWatchThreadAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-thread-watch")), i18n("&Watch Thread"), this);
3252     actionCollection()->addAction(QStringLiteral("thread_watched"), mWatchThreadAction);
3253     connect(mWatchThreadAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusWatched);
3254 
3255     mIgnoreThreadAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-thread-ignored")), i18n("&Ignore Thread"), this);
3256     actionCollection()->addAction(QStringLiteral("thread_ignored"), mIgnoreThreadAction);
3257     connect(mIgnoreThreadAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusIgnored);
3258 
3259     mThreadStatusMenu->addSeparator();
3260     mThreadStatusMenu->addAction(mWatchThreadAction);
3261     mThreadStatusMenu->addAction(mIgnoreThreadAction);
3262 
3263     mSaveAttachmentsAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("Save A&ttachments..."), this);
3264     actionCollection()->addAction(QStringLiteral("file_save_attachments"), mSaveAttachmentsAction);
3265     connect(mSaveAttachmentsAction, &QAction::triggered, this, &KMMainWidget::slotSaveAttachments);
3266 
3267     mDeleteAttachmentsAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:inmenu", "Delete All Attachments"), this);
3268     actionCollection()->addAction(QStringLiteral("delete_all_attachments"), mDeleteAttachmentsAction);
3269     connect(mDeleteAttachmentsAction, &QAction::triggered, this, &KMMainWidget::slotDeleteAttachments);
3270 
3271     mMoveActionMenu = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::MoveItemToMenu);
3272 
3273     mCopyActionMenu = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyItemToMenu);
3274 
3275     mCopyDecryptedActionMenu = new KActionMenu(i18n("Copy Decrypted To..."), this);
3276     actionCollection()->addAction(QStringLiteral("copy_decrypted_to_menu"), mCopyDecryptedActionMenu);
3277     connect(mCopyDecryptedActionMenu->menu(), &QMenu::triggered, this, &KMMainWidget::slotCopyDecryptedTo);
3278 
3279     mApplyAllFiltersAction = new QAction(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("Appl&y All Filters"), this);
3280     actionCollection()->addAction(QStringLiteral("apply_filters"), mApplyAllFiltersAction);
3281     connect(mApplyAllFiltersAction, &QAction::triggered, this, &KMMainWidget::slotApplyFilters);
3282     actionCollection()->setDefaultShortcut(mApplyAllFiltersAction, QKeySequence(Qt::CTRL | Qt::Key_J));
3283 
3284     mApplyFilterActionsMenu = new KActionMenu(i18n("A&pply Filter"), this);
3285     actionCollection()->addAction(QStringLiteral("apply_filter_actions"), mApplyFilterActionsMenu);
3286 
3287     {
3288         auto action = new QAction(i18nc("View->", "&Expand Thread / Group"), this);
3289         actionCollection()->addAction(QStringLiteral("expand_thread"), action);
3290         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Period));
3291         KMail::Util::addQActionHelpText(action, i18n("Expand the current thread or group"));
3292         connect(action, &QAction::triggered, this, &KMMainWidget::slotExpandThread);
3293     }
3294     {
3295         auto action = new QAction(i18nc("View->", "&Collapse Thread / Group"), this);
3296         actionCollection()->addAction(QStringLiteral("collapse_thread"), action);
3297         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Comma));
3298         KMail::Util::addQActionHelpText(action, i18n("Collapse the current thread or group"));
3299         connect(action, &QAction::triggered, this, &KMMainWidget::slotCollapseThread);
3300     }
3301     {
3302         auto action = new QAction(i18nc("View->", "Ex&pand All Threads"), this);
3303         actionCollection()->addAction(QStringLiteral("expand_all_threads"), action);
3304         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_Period));
3305         KMail::Util::addQActionHelpText(action, i18n("Expand all threads in the current folder"));
3306         connect(action, &QAction::triggered, this, &KMMainWidget::slotExpandAllThreads);
3307     }
3308     {
3309         auto action = new QAction(i18nc("View->", "C&ollapse All Threads"), this);
3310         actionCollection()->addAction(QStringLiteral("collapse_all_threads"), action);
3311         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_Comma));
3312         KMail::Util::addQActionHelpText(action, i18n("Collapse all threads in the current folder"));
3313         connect(action, &QAction::triggered, this, &KMMainWidget::slotCollapseAllThreads);
3314     }
3315 
3316     auto dukeOfMonmoth = new QAction(i18n("&Display Message"), this);
3317     actionCollection()->addAction(QStringLiteral("display_message"), dukeOfMonmoth);
3318     connect(dukeOfMonmoth, &QAction::triggered, this, &KMMainWidget::slotDisplayCurrentMessage);
3319     actionCollection()->setDefaultShortcuts(dukeOfMonmoth, QList<QKeySequence>{QKeySequence(Qt::Key_Enter), QKeySequence(Qt::Key_Return)});
3320 
3321     //----- Go Menu
3322     {
3323         auto action = new QAction(i18n("&Next Message"), this);
3324         actionCollection()->addAction(QStringLiteral("go_next_message"), action);
3325         actionCollection()->setDefaultShortcuts(action, QList<QKeySequence>{QKeySequence(Qt::Key_N), QKeySequence(Qt::Key_Right)});
3326         KMail::Util::addQActionHelpText(action, i18n("Go to the next message"));
3327         connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectNextMessage);
3328     }
3329     {
3330         auto action = new QAction(i18n("Next &Unread Message"), this);
3331         actionCollection()->addAction(QStringLiteral("go_next_unread_message"), action);
3332         actionCollection()->setDefaultShortcuts(action, QList<QKeySequence>{QKeySequence(Qt::Key_Plus), QKeySequence(Qt::Key_Plus | Qt::KeypadModifier)});
3333         if (QApplication::isRightToLeft()) {
3334             action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
3335         } else {
3336             action->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
3337         }
3338         action->setIconText(i18nc("@action:inmenu Goto next unread message", "Next"));
3339         KMail::Util::addQActionHelpText(action, i18n("Go to the next unread message"));
3340         connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectNextUnreadMessage);
3341     }
3342     {
3343         auto action = new QAction(i18n("&Previous Message"), this);
3344         actionCollection()->addAction(QStringLiteral("go_prev_message"), action);
3345         KMail::Util::addQActionHelpText(action, i18n("Go to the previous message"));
3346         actionCollection()->setDefaultShortcuts(action, QList<QKeySequence>{QKeySequence(Qt::Key_P), QKeySequence(Qt::Key_Left)});
3347         connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectPreviousMessage);
3348     }
3349     {
3350         auto action = new QAction(i18n("Previous Unread &Message"), this);
3351         actionCollection()->addAction(QStringLiteral("go_prev_unread_message"), action);
3352         actionCollection()->setDefaultShortcuts(action, QList<QKeySequence>{QKeySequence(Qt::Key_Minus), QKeySequence(Qt::Key_Minus | Qt::KeypadModifier)});
3353         if (QApplication::isRightToLeft()) {
3354             action->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
3355         } else {
3356             action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
3357         }
3358         action->setIconText(i18nc("@action:inmenu Goto previous unread message.", "Previous"));
3359         KMail::Util::addQActionHelpText(action, i18n("Go to the previous unread message"));
3360         connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectPreviousUnreadMessage);
3361     }
3362     {
3363         auto action = new QAction(i18n("Next Unread &Folder"), this);
3364         actionCollection()->addAction(QStringLiteral("go_next_unread_folder"), action);
3365         connect(action, &QAction::triggered, this, &KMMainWidget::slotNextUnreadFolder);
3366         actionCollection()->setDefaultShortcuts(action,
3367                                                 QList<QKeySequence>{QKeySequence(Qt::ALT | Qt::Key_Plus),
3368                                                                     QKeySequence(QKeyCombination(Qt::ALT, Qt::Key_Plus), QKeyCombination(Qt::KeypadModifier))});
3369         KMail::Util::addQActionHelpText(action, i18n("Go to the next folder with unread messages"));
3370     }
3371     {
3372         auto action = new QAction(i18n("Previous Unread F&older"), this);
3373         actionCollection()->addAction(QStringLiteral("go_prev_unread_folder"), action);
3374         actionCollection()->setDefaultShortcuts(
3375             action,
3376             QList<QKeySequence>{QKeySequence(Qt::ALT | Qt::Key_Minus),
3377                                 QKeySequence(QKeyCombination(Qt::ALT, Qt::Key_Minus), QKeyCombination(Qt::KeypadModifier))});
3378         KMail::Util::addQActionHelpText(action, i18n("Go to the previous folder with unread messages"));
3379         connect(action, &QAction::triggered, this, &KMMainWidget::slotPrevUnreadFolder);
3380     }
3381     {
3382         auto action = new QAction(i18nc("Go->", "Next Unread &Text"), this);
3383         actionCollection()->addAction(QStringLiteral("go_next_unread_text"), action);
3384         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Space));
3385         KMail::Util::addQActionHelpText(action, i18n("Go to the next unread text"));
3386         action->setWhatsThis(
3387             i18n("Scroll down current message. "
3388                  "If at end of current message, "
3389                  "go to next unread message."));
3390         connect(action, &QAction::triggered, this, &KMMainWidget::slotReadOn);
3391     }
3392 
3393     //----- Settings Menu
3394     {
3395         auto action = new QAction(QIcon::fromTheme(QStringLiteral("dialog-filters")), i18n("Configure &Filters..."), this);
3396         action->setMenuRole(QAction::NoRole); // do not move to application menu on OS X
3397         actionCollection()->addAction(QStringLiteral("filter"), action);
3398         connect(action, &QAction::triggered, this, &KMMainWidget::slotFilter);
3399     }
3400     {
3401         auto action = new QAction(i18n("Manage &Sieve Scripts..."), this);
3402         actionCollection()->addAction(QStringLiteral("sieveFilters"), action);
3403         connect(action, &QAction::triggered, this, &KMMainWidget::slotManageSieveScripts);
3404     }
3405     {
3406         auto action = new QAction(QIcon::fromTheme(QStringLiteral("list-resource-add")), i18n("&Add Account..."), this);
3407         actionCollection()->addAction(QStringLiteral("accountWizard"), action);
3408         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotAccountWizard);
3409     }
3410     {
3411         mShowIntroductionAction = new QAction(QIcon::fromTheme(QStringLiteral("kmail")), i18n("KMail &Introduction"), this);
3412         actionCollection()->addAction(QStringLiteral("help_kmail_welcomepage"), mShowIntroductionAction);
3413         KMail::Util::addQActionHelpText(mShowIntroductionAction, i18n("Display KMail's Welcome Page"));
3414         connect(mShowIntroductionAction, &QAction::triggered, this, &KMMainWidget::slotIntro);
3415         mShowIntroductionAction->setEnabled(mMsgView != nullptr);
3416     }
3417 
3418     // ----- Standard Actions
3419     {
3420         auto action = new QAction(QIcon::fromTheme(QStringLiteral("preferences-desktop-notification")), i18n("Configure &Notifications..."), this);
3421         action->setMenuRole(QAction::NoRole); // do not move to application menu on OS X
3422         actionCollection()->addAction(QStringLiteral("kmail_configure_notifications"), action);
3423         connect(action, &QAction::triggered, this, &KMMainWidget::slotEditNotifications);
3424     }
3425 
3426     {
3427         auto action = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("&Configure KMail..."), this);
3428         action->setMenuRole(QAction::PreferencesRole); // this one should move to the application menu on OS X
3429         actionCollection()->addAction(QStringLiteral("kmail_configure_kmail"), action);
3430         connect(action, &QAction::triggered, kmkernel, &KMKernel::slotShowConfigurationDialog);
3431     }
3432 
3433     {
3434         mExpireConfigAction = new QAction(i18n("Expire..."), this);
3435         actionCollection()->addAction(QStringLiteral("expire_settings"), mExpireConfigAction);
3436         connect(mExpireConfigAction, &QAction::triggered, mManageShowCollectionProperties, &ManageShowCollectionProperties::slotShowExpiryProperties);
3437     }
3438 
3439     {
3440         auto action = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("Add Favorite Folder..."), this);
3441         actionCollection()->addAction(QStringLiteral("add_favorite_folder"), action);
3442         connect(action, &QAction::triggered, this, &KMMainWidget::slotAddFavoriteFolder);
3443     }
3444 
3445     {
3446         mServerSideSubscription = new QAction(QIcon::fromTheme(QStringLiteral("folder-bookmarks")), i18n("Serverside Subscription..."), this);
3447         actionCollection()->addAction(QStringLiteral("serverside_subscription"), mServerSideSubscription);
3448         connect(mServerSideSubscription, &QAction::triggered, this, &KMMainWidget::slotServerSideSubscription);
3449     }
3450 
3451     {
3452         mApplyAllFiltersFolderAction = new QAction(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("Apply All Filters"), this);
3453         actionCollection()->addAction(QStringLiteral("apply_filters_folder"), mApplyAllFiltersFolderAction);
3454         connect(mApplyAllFiltersFolderAction, &QAction::triggered, this, [this] {
3455             slotApplyFiltersOnFolder(/* recursive */ false);
3456         });
3457     }
3458 
3459     {
3460         mApplyAllFiltersFolderRecursiveAction = new QAction(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("Apply All Filters"), this);
3461         actionCollection()->addAction(QStringLiteral("apply_filters_folder_recursive"), mApplyAllFiltersFolderRecursiveAction);
3462         connect(mApplyAllFiltersFolderRecursiveAction, &QAction::triggered, this, [this] {
3463             slotApplyFiltersOnFolder(/* recursive */ true);
3464         });
3465     }
3466 
3467     {
3468         mApplyFilterFolderActionsMenu = new KActionMenu(i18n("Apply Filters on Folder"), this);
3469         actionCollection()->addAction(QStringLiteral("apply_filters_on_folder_actions"), mApplyFilterFolderActionsMenu);
3470     }
3471 
3472     {
3473         mApplyFilterFolderRecursiveActionsMenu = new KActionMenu(i18n("Apply Filters on Folder and All its Subfolders"), this);
3474         actionCollection()->addAction(QStringLiteral("apply_filters_on_folder_recursive_actions"), mApplyFilterFolderRecursiveActionsMenu);
3475     }
3476 
3477     {
3478         auto action = new QAction(QIcon::fromTheme(QStringLiteral("kontact")), i18n("Import/Export KMail Data..."), this);
3479         actionCollection()->addAction(QStringLiteral("kmail_export_data"), action);
3480         connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotExportData);
3481     }
3482 
3483     {
3484         auto action = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("New AddressBook Contact..."), this);
3485         actionCollection()->addAction(QStringLiteral("kmail_new_addressbook_contact"), action);
3486         connect(action, &QAction::triggered, this, &KMMainWidget::slotCreateAddressBookContact);
3487     }
3488 
3489     QAction *act = actionCollection()->addAction(KStandardAction::Undo, QStringLiteral("kmail_undo"));
3490     connect(act, &QAction::triggered, this, &KMMainWidget::slotUndo);
3491 
3492     mAccountSettings = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Account &Settings"), this);
3493     actionCollection()->addAction(QStringLiteral("resource_settings"), mAccountSettings);
3494     connect(mAccountSettings, &QAction::triggered, this, &KMMainWidget::slotAccountSettings);
3495 
3496     mRestartAccountSettings = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Restart Account"), this);
3497     actionCollection()->addAction(QStringLiteral("resource_restart"), mRestartAccountSettings);
3498     connect(mRestartAccountSettings, &QAction::triggered, this, &KMMainWidget::slotRestartAccount);
3499 
3500     menutimer = new QTimer(this);
3501     menutimer->setObjectName(QLatin1StringView("menutimer"));
3502     menutimer->setSingleShot(true);
3503     connect(menutimer, &QTimer::timeout, this, &KMMainWidget::updateMessageActionsDelayed);
3504     connect(kmkernel->undoStack(), &KMail::UndoStack::undoStackChanged, this, &KMMainWidget::slotUpdateUndo);
3505 
3506     updateMessageActions();
3507     updateFolderMenu();
3508     mTagActionManager = new KMail::TagActionManager(this, actionCollection(), mMsgActions, mGUIClient);
3509     mFolderShortcutActionManager = new KMail::FolderShortcutActionManager(this, actionCollection());
3510 
3511     {
3512         auto action = new QAction(i18n("Copy Message to Folder"), this);
3513         actionCollection()->addAction(QStringLiteral("copy_message_to_folder"), action);
3514         connect(action, &QAction::triggered, this, &KMMainWidget::slotCopySelectedMessagesToFolder);
3515         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_C));
3516     }
3517     {
3518         auto action = new QAction(QIcon::fromTheme(QStringLiteral("go-jump")), i18n("Jump to Folder..."), this);
3519         actionCollection()->addAction(QStringLiteral("jump_to_folder"), action);
3520         connect(action, &QAction::triggered, this, &KMMainWidget::slotJumpToFolder);
3521         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_J));
3522     }
3523     {
3524         auto action = new QAction(i18n("Abort Current Operation"), this);
3525         actionCollection()->addAction(QStringLiteral("cancel"), action);
3526         connect(action, &QAction::triggered, ProgressManager::instance(), &KPIM::ProgressManager::slotAbortAll);
3527         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Escape));
3528     }
3529     {
3530         auto action = new QAction(i18n("Focus on Next Folder"), this);
3531         actionCollection()->addAction(QStringLiteral("inc_current_folder"), action);
3532         connect(action, &QAction::triggered, mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusNextFolder);
3533         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_Right));
3534     }
3535     {
3536         auto action = new QAction(i18n("Focus on Previous Folder"), this);
3537         actionCollection()->addAction(QStringLiteral("dec_current_folder"), action);
3538         connect(action, &QAction::triggered, mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusPrevFolder);
3539         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_Left));
3540     }
3541     {
3542         auto action = new QAction(i18n("Select Folder with Focus"), this);
3543         actionCollection()->addAction(QStringLiteral("select_current_folder"), action);
3544 
3545         connect(action, &QAction::triggered, mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotSelectFocusFolder);
3546         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_Space));
3547     }
3548     {
3549         auto action = new QAction(i18n("Focus on First Folder"), this);
3550         actionCollection()->addAction(QStringLiteral("focus_first_folder"), action);
3551         connect(action, &QAction::triggered, mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusFirstFolder);
3552         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_Home));
3553     }
3554     {
3555         auto action = new QAction(i18n("Focus on Last Folder"), this);
3556         actionCollection()->addAction(QStringLiteral("focus_last_folder"), action);
3557         connect(action, &QAction::triggered, mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusLastFolder);
3558         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_End));
3559     }
3560     {
3561         auto action = new QAction(i18n("Focus on Next Message"), this);
3562         actionCollection()->addAction(QStringLiteral("inc_current_message"), action);
3563         connect(action, &QAction::triggered, this, &KMMainWidget::slotFocusOnNextMessage);
3564         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::Key_Right));
3565     }
3566     {
3567         auto action = new QAction(i18n("Focus on Previous Message"), this);
3568         actionCollection()->addAction(QStringLiteral("dec_current_message"), action);
3569         connect(action, &QAction::triggered, this, &KMMainWidget::slotFocusOnPrevMessage);
3570         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::Key_Left));
3571     }
3572     {
3573         auto action = new QAction(i18n("Select First Message"), this);
3574         actionCollection()->addAction(QStringLiteral("select_first_message"), action);
3575         connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectFirstMessage);
3576         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::Key_Home));
3577     }
3578     {
3579         auto action = new QAction(i18n("Select Last Message"), this);
3580         actionCollection()->addAction(QStringLiteral("select_last_message"), action);
3581         connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectLastMessage);
3582         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::Key_End));
3583     }
3584     {
3585         auto action = new QAction(i18n("Select Message with Focus"), this);
3586         actionCollection()->addAction(QStringLiteral("select_current_message"), action);
3587         connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectFocusedMessage);
3588         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT | Qt::Key_Space));
3589     }
3590 
3591     {
3592         mQuickSearchAction = new QAction(i18n("Set Focus to Quick Search"), this);
3593         // If change shortcut change Panel::setQuickSearchClickMessage(...) message
3594         actionCollection()->setDefaultShortcut(mQuickSearchAction, QKeySequence(Qt::ALT | Qt::Key_Q));
3595         actionCollection()->addAction(QStringLiteral("focus_to_quickseach"), mQuickSearchAction);
3596         connect(mQuickSearchAction, &QAction::triggered, this, &KMMainWidget::slotFocusQuickSearch);
3597         updateQuickSearchLineText();
3598     }
3599     {
3600         auto action = new QAction(i18n("Extend Selection to Previous Message"), this);
3601         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT | Qt::Key_Left));
3602         actionCollection()->addAction(QStringLiteral("previous_message"), action);
3603         connect(action, &QAction::triggered, this, &KMMainWidget::slotExtendSelectionToPreviousMessage);
3604     }
3605     {
3606         auto action = new QAction(i18n("Extend Selection to Next Message"), this);
3607         actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT | Qt::Key_Right));
3608         actionCollection()->addAction(QStringLiteral("next_message"), action);
3609         connect(action, &QAction::triggered, this, &KMMainWidget::slotExtendSelectionToNextMessage);
3610     }
3611 
3612     {
3613         mMoveMsgToFolderAction = new QAction(i18n("Move Message to Folder"), this);
3614         actionCollection()->setDefaultShortcut(mMoveMsgToFolderAction, QKeySequence(Qt::Key_M));
3615         actionCollection()->addAction(QStringLiteral("move_message_to_folder"), mMoveMsgToFolderAction);
3616         connect(mMoveMsgToFolderAction, &QAction::triggered, this, &KMMainWidget::slotMoveSelectedMessageToFolder);
3617     }
3618 
3619     mArchiveAction = new QAction(i18nc("@action", "Archive"), this);
3620     actionCollection()->addAction(QStringLiteral("archive_mails"), mArchiveAction);
3621     connect(mArchiveAction, &QAction::triggered, this, &KMMainWidget::slotArchiveMails);
3622 
3623     mMarkAllMessageAsReadAndInAllSubFolder = new QAction(i18n("Mark All Messages As Read in This Folder and All its Subfolder"), this);
3624     mMarkAllMessageAsReadAndInAllSubFolder->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-read")));
3625     actionCollection()->addAction(QStringLiteral("markallmessagereadcurentfolderandsubfolder"), mMarkAllMessageAsReadAndInAllSubFolder);
3626     connect(mMarkAllMessageAsReadAndInAllSubFolder, &KToggleAction::triggered, this, &KMMainWidget::slotMarkAllMessageAsReadInCurrentFolderAndSubfolder);
3627 
3628     mRemoveDuplicateRecursiveAction = new QAction(i18n("Remove Duplicates in This Folder and All its Subfolder"), this);
3629     actionCollection()->addAction(QStringLiteral("remove_duplicate_recursive"), mRemoveDuplicateRecursiveAction);
3630     connect(mRemoveDuplicateRecursiveAction, &KToggleAction::triggered, this, &KMMainWidget::slotRemoveDuplicateRecursive);
3631 
3632     {
3633         QList<QAction *> listActions;
3634         auto act = new QAction(i18n("Previous Selected Folder"), this); // TODO fix me i18n
3635         actionCollection()->setDefaultShortcut(act, QKeySequence(Qt::CTRL | Qt::Key_Tab));
3636         actionCollection()->addAction(QStringLiteral("previous_folder"), act);
3637         listActions.append(act);
3638 
3639         connect(act, &QAction::triggered, this, &KMMainWidget::undoSwitchFolder);
3640 
3641         act = new QAction(i18n("Next Selected Folder"), this); // TODO fix me i18n
3642         actionCollection()->addAction(QStringLiteral("next_folder"), act);
3643         QKeyCombination combinationKeys(Qt::SHIFT, Qt::Key_Tab);
3644         actionCollection()->setDefaultShortcut(act, QKeySequence(combinationKeys, QKeyCombination(Qt::CTRL)));
3645         connect(act, &QAction::triggered, this, &KMMainWidget::redoSwitchFolder);
3646         listActions.append(act);
3647 
3648         mCollectionSwitcherTreeViewManager->addActions(listActions);
3649     }
3650     auto manager = new KColorSchemeManager(this);
3651     actionCollection()->addAction(QStringLiteral("colorscheme_menu"), KColorSchemeMenu::createMenu(manager, this));
3652 }
3653 
3654 void KMMainWidget::redoSwitchFolder()
3655 {
3656     mCollectionSwitcherTreeViewManager->selectBackward();
3657 }
3658 
3659 void KMMainWidget::undoSwitchFolder()
3660 {
3661     mCollectionSwitcherTreeViewManager->selectForward();
3662 }
3663 
3664 void KMMainWidget::slotAddFavoriteFolder()
3665 {
3666     if (!mFavoritesModel) {
3667         return;
3668     }
3669     QPointer<MailCommon::FolderSelectionDialog> dialog(selectFromAllFoldersDialog());
3670     dialog->setWindowTitle(i18nc("@title:window", "Add Favorite Folder"));
3671     if (dialog->exec() && dialog) {
3672         const Akonadi::Collection collection = dialog->selectedCollection();
3673         if (collection.isValid()) {
3674             mFavoritesModel->addCollection(collection);
3675         }
3676     }
3677     delete dialog;
3678 }
3679 
3680 //-----------------------------------------------------------------------------
3681 void KMMainWidget::slotEditNotifications()
3682 {
3683     KMail::KMKnotify notifyDlg(this);
3684     notifyDlg.exec();
3685 }
3686 
3687 //-----------------------------------------------------------------------------
3688 void KMMainWidget::slotReadOn()
3689 {
3690     if (!mMsgView) {
3691         return;
3692     }
3693     mMsgView->viewer()->atBottom();
3694 }
3695 
3696 void KMMainWidget::slotPageIsScrolledToBottom(bool isAtBottom)
3697 {
3698     if (isAtBottom) {
3699         slotSelectNextUnreadMessage();
3700     } else {
3701         if (mMsgView) {
3702             mMsgView->viewer()->slotJumpDown();
3703         }
3704     }
3705 }
3706 
3707 void KMMainWidget::slotNextUnreadFolder()
3708 {
3709     if (!mFolderTreeWidget) {
3710         return;
3711     }
3712     mGoToFirstUnreadMessageInSelectedFolder = true;
3713     mFolderTreeWidget->folderTreeView()->selectNextUnreadFolder();
3714     mGoToFirstUnreadMessageInSelectedFolder = false;
3715 }
3716 
3717 void KMMainWidget::slotPrevUnreadFolder()
3718 {
3719     if (!mFolderTreeWidget) {
3720         return;
3721     }
3722     mGoToFirstUnreadMessageInSelectedFolder = true;
3723     mFolderTreeWidget->folderTreeView()->selectPrevUnreadFolder();
3724     mGoToFirstUnreadMessageInSelectedFolder = false;
3725 }
3726 
3727 void KMMainWidget::slotExpandThread()
3728 {
3729     mMessagePane->setCurrentThreadExpanded(true);
3730 }
3731 
3732 void KMMainWidget::slotCollapseThread()
3733 {
3734     mMessagePane->setCurrentThreadExpanded(false);
3735 }
3736 
3737 void KMMainWidget::slotExpandAllThreads()
3738 {
3739     // TODO: Make this asynchronous ? (if there is enough demand)
3740     KCursorSaver saver(Qt::WaitCursor);
3741     mMessagePane->setAllThreadsExpanded(true);
3742 }
3743 
3744 void KMMainWidget::slotCollapseAllThreads()
3745 {
3746     KCursorSaver saver(Qt::WaitCursor);
3747     mMessagePane->setAllThreadsExpanded(false);
3748 }
3749 
3750 //-----------------------------------------------------------------------------
3751 void KMMainWidget::updateMessageMenu()
3752 {
3753     updateMessageActions();
3754 }
3755 
3756 void KMMainWidget::startUpdateMessageActionsTimer()
3757 {
3758     // FIXME: This delay effectively CAN make the actions to be in an incoherent state
3759     //        Maybe we should mark actions as "dirty" here and check it in every action handler...
3760     updateMessageActions(true);
3761 
3762     menutimer->stop();
3763     menutimer->start(500ms);
3764 }
3765 
3766 void KMMainWidget::updateMessageActions(bool fast)
3767 {
3768     Akonadi::Item::List selectedItems;
3769     Akonadi::Item::List selectedVisibleItems;
3770     bool allSelectedBelongToSameThread = false;
3771     if (mCurrentFolderSettings && mCurrentFolderSettings->isValid()
3772         && mMessagePane->getSelectionStats(selectedItems, selectedVisibleItems, &allSelectedBelongToSameThread)) {
3773         mMsgActions->setCurrentMessage(mMessagePane->currentItem(), selectedVisibleItems);
3774     } else {
3775         mMsgActions->setCurrentMessage(Akonadi::Item());
3776     }
3777 
3778     if (!fast) {
3779         updateMessageActionsDelayed();
3780     }
3781 }
3782 
3783 void KMMainWidget::updateMessageActionsDelayed()
3784 {
3785     int count;
3786     Akonadi::Item::List selectedItems;
3787     Akonadi::Item::List selectedVisibleItems;
3788     bool allSelectedBelongToSameThread = false;
3789     Akonadi::Item currentMessage;
3790     bool currentFolderSettingsIsValid = mCurrentFolderSettings && mCurrentFolderSettings->isValid();
3791     if (currentFolderSettingsIsValid && mMessagePane->getSelectionStats(selectedItems, selectedVisibleItems, &allSelectedBelongToSameThread)) {
3792         count = selectedItems.count();
3793 
3794         currentMessage = mMessagePane->currentItem();
3795     } else {
3796         count = 0;
3797         currentMessage = Akonadi::Item();
3798     }
3799 
3800     mApplyFilterActionsMenu->setEnabled(currentFolderSettingsIsValid);
3801     mApplyFilterFolderRecursiveActionsMenu->setEnabled(currentFolderSettingsIsValid);
3802 
3803     //
3804     // Here we have:
3805     //
3806     // - A list of selected messages stored in selectedSernums.
3807     //   The selected messages might contain some invisible ones as a selected
3808     //   collapsed node "includes" all the children in the selection.
3809     // - A list of selected AND visible messages stored in selectedVisibleSernums.
3810     //   This list does not contain children of selected and collapsed nodes.
3811     //
3812     // Now, actions can operate on:
3813     // - Any set of messages
3814     //     These are called "mass actions" and are enabled whenever we have a message selected.
3815     //     In fact we should differentiate between actions that operate on visible selection
3816     //     and those that operate on the selection as a whole (without considering visibility)...
3817     // - A single thread
3818     //     These are called "thread actions" and are enabled whenever all the selected messages belong
3819     //     to the same thread. If the selection doesn't cover the whole thread then the action
3820     //     will act on the whole thread anyway (thus will silently extend the selection)
3821     // - A single message
3822     //     And we have two sub-cases:
3823     //     - The selection must contain exactly one message
3824     //       These actions can't ignore the hidden messages and thus must be disabled if
3825     //       the selection contains any.
3826     //     - The selection must contain exactly one visible message
3827     //       These actions will ignore the hidden message and thus can be enabled if
3828     //       the selection contains any.
3829     //
3830 
3831     const bool readOnly = currentFolderSettingsIsValid && (mCurrentFolderSettings->rights() & Akonadi::Collection::ReadOnly);
3832     // can we apply strictly single message actions ? (this is false if the whole selection contains more than one message)
3833     const bool single_actions = count == 1;
3834     // can we apply loosely single message actions ? (this is false if the VISIBLE selection contains more than one message)
3835     const bool singleVisibleMessageSelected = selectedVisibleItems.count() == 1;
3836     // can we apply "mass" actions to the selection ? (this is actually always true if the selection is non-empty)
3837     const bool mass_actions = count >= 1;
3838     // does the selection identify a single thread ?
3839     const bool thread_actions = mass_actions && allSelectedBelongToSameThread && mMessagePane->isThreaded();
3840     // can we apply flags to the selected messages ?
3841     const bool flags_available = KMailSettings::self()->allowLocalFlags() || !(currentFolderSettingsIsValid ? readOnly : true);
3842 
3843     mThreadStatusMenu->setEnabled(thread_actions);
3844     // these need to be handled individually, the user might have them
3845     // in the toolbar
3846     mWatchThreadAction->setEnabled(thread_actions && flags_available);
3847     mIgnoreThreadAction->setEnabled(thread_actions && flags_available);
3848     mMarkThreadAsReadAction->setEnabled(thread_actions);
3849     mMarkThreadAsUnreadAction->setEnabled(thread_actions);
3850     mToggleThreadToActAction->setEnabled(thread_actions && flags_available);
3851     mToggleThreadImportantAction->setEnabled(thread_actions && flags_available);
3852     bool canDeleteMessages = currentFolderSettingsIsValid && (mCurrentFolderSettings->rights() & Akonadi::Collection::CanDeleteItem);
3853 
3854     mTrashThreadAction->setEnabled(thread_actions && canDeleteMessages);
3855     mDeleteThreadAction->setEnabled(thread_actions && canDeleteMessages);
3856     if (messageView() && messageView()->viewer() && messageView()->viewer()->headerStylePlugin()) {
3857         messageView()->viewer()->headerStylePlugin()->headerStyle()->setReadOnlyMessage(!canDeleteMessages);
3858     }
3859 
3860     if (currentMessage.isValid()) {
3861         MessageStatus status;
3862         status.setStatusFromFlags(currentMessage.flags());
3863         mTagActionManager->updateActionStates(count, mMessagePane->currentItem());
3864         if (thread_actions) {
3865             mToggleThreadToActAction->setChecked(status.isToAct());
3866             mToggleThreadImportantAction->setChecked(status.isImportant());
3867             mWatchThreadAction->setChecked(status.isWatched());
3868             mIgnoreThreadAction->setChecked(status.isIgnored());
3869         }
3870     }
3871 
3872     mMoveActionMenu->setEnabled(mass_actions && canDeleteMessages);
3873     if (mMoveMsgToFolderAction) {
3874         mMoveMsgToFolderAction->setEnabled(mass_actions && canDeleteMessages);
3875     }
3876     // mCopyActionMenu->setEnabled( mass_actions );
3877 
3878     mDeleteAction->setEnabled(mass_actions && canDeleteMessages);
3879 
3880     mExpireConfigAction->setEnabled(canDeleteMessages && !MailCommon::Util::isVirtualCollection(mCurrentCollection));
3881 
3882     if (mMsgView) {
3883         mMsgView->findInMessageAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentCollection));
3884     }
3885     mMsgActions->forwardInlineAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentCollection));
3886     mMsgActions->forwardAttachedAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentCollection));
3887     mMsgActions->forwardMenu()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentCollection));
3888 
3889     mMsgActions->editAsNewAction()->setEnabled(single_actions);
3890     mMsgActions->newMessageFromTemplateAction()->setEnabled(single_actions && CommonKernel->folderIsTemplates(mCurrentCollection));
3891     filterMenu()->setEnabled(single_actions);
3892     mMsgActions->redirectAction()->setEnabled(/*single_actions &&*/ mass_actions && !CommonKernel->folderIsTemplates(mCurrentCollection));
3893     mMsgActions->newToRecipientsAction()->setEnabled(single_actions);
3894 
3895     if (auto menuCustom = mMsgActions->customTemplatesMenu()) {
3896         menuCustom->forwardActionMenu()->setEnabled(mass_actions);
3897         menuCustom->replyActionMenu()->setEnabled(single_actions);
3898         menuCustom->replyAllActionMenu()->setEnabled(single_actions);
3899     }
3900 
3901     // "Print" will act on the current message: it will ignore any hidden selection
3902     mMsgActions->printAction()->setEnabled(singleVisibleMessageSelected && mMsgView);
3903     // "Print preview" will act on the current message: it will ignore any hidden selection
3904     if (QAction *printPreviewAction = mMsgActions->printPreviewAction()) {
3905         printPreviewAction->setEnabled(singleVisibleMessageSelected && mMsgView);
3906     }
3907 
3908     // "View Source" will act on the current message: it will ignore any hidden selection
3909     if (mMsgView) {
3910         mMsgView->viewSourceAction()->setEnabled(singleVisibleMessageSelected);
3911         mMsgView->selectAllAction()->setEnabled(count);
3912     }
3913     MessageStatus status;
3914     status.setStatusFromFlags(currentMessage.flags());
3915 
3916     QList<QAction *> actionList;
3917     bool statusSendAgain = single_actions
3918         && ((currentMessage.isValid() && status.isSent()) || (currentMessage.isValid() && CommonKernel->folderIsSentMailFolder(mCurrentCollection)));
3919     if (statusSendAgain) {
3920         actionList << mMsgActions->sendAgainAction();
3921     } else if (single_actions) {
3922         actionList << mMsgActions->editAsNewAction();
3923     }
3924     actionList << mSaveAttachmentsAction << mDeleteAttachmentsAction;
3925     if (mCurrentCollection.isValid() && FolderArchive::FolderArchiveUtil::resourceSupportArchiving(mCurrentCollection.resource())) {
3926         actionList << mArchiveAction;
3927     }
3928     mGUIClient->unplugActionList(QStringLiteral("messagelist_actionlist"));
3929     mGUIClient->plugActionList(QStringLiteral("messagelist_actionlist"), actionList);
3930     mMsgActions->sendAgainAction()->setEnabled(statusSendAgain);
3931 
3932     if (currentFolderSettingsIsValid) {
3933         updateMoveAction(mCurrentFolderSettings->statistics());
3934     } else {
3935         updateMoveAction(false);
3936     }
3937 
3938     const auto col = CommonKernel->collectionFromId(CommonKernel->outboxCollectionFolder().id());
3939     const bool nbMsgOutboxCollectionIsNotNull = (col.statistics().count() > 0);
3940 
3941     mSendQueued->setEnabled(nbMsgOutboxCollectionIsNotNull);
3942     mSendActionMenu->setEnabled(nbMsgOutboxCollectionIsNotNull);
3943 
3944     const bool newPostToMailingList = mCurrentFolderSettings && mCurrentFolderSettings->isMailingListEnabled();
3945     mMessageNewList->setEnabled(newPostToMailingList);
3946 
3947     slotUpdateOnlineStatus(static_cast<GlobalSettingsBase::EnumNetworkState::type>(KMailSettings::self()->networkState()));
3948     if (QAction *act = action(QStringLiteral("kmail_undo"))) {
3949         act->setEnabled(kmkernel->undoStack() && !kmkernel->undoStack()->isEmpty());
3950     }
3951 
3952     // Enable / disable all filters.
3953     for (QAction *filterAction : std::as_const(mFilterMenuActions)) {
3954         filterAction->setEnabled(count > 0);
3955     }
3956 
3957     mApplyAllFiltersAction->setEnabled(count);
3958     mApplyFilterActionsMenu->setEnabled(count);
3959     mSelectAllMessages->setEnabled(count);
3960 
3961     if (currentMessage.hasFlag(Akonadi::MessageFlags::Encrypted) || count > 1) {
3962         mCopyDecryptedActionMenu->setVisible(true);
3963         mCopyDecryptedActionMenu->menu()->clear();
3964         mAkonadiStandardActionManager->standardActionManager()->createActionFolderMenu(mCopyDecryptedActionMenu->menu(),
3965                                                                                        Akonadi::StandardActionManager::CopyItemToMenu);
3966     } else {
3967         mCopyDecryptedActionMenu->setVisible(false);
3968     }
3969 }
3970 
3971 void KMMainWidget::slotAkonadiStandardActionUpdated()
3972 {
3973     if (mCollectionProperties) {
3974         if (mCurrentCollection.isValid()) {
3975             const Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(mCurrentCollection.resource());
3976 
3977             mCollectionProperties->setEnabled(!mCurrentFolderSettings->isStructural() && (instance.status() != Akonadi::AgentInstance::Broken));
3978         } else {
3979             mCollectionProperties->setEnabled(false);
3980         }
3981         QList<QAction *> collectionProperties;
3982         if (mCollectionProperties->isEnabled()) {
3983             collectionProperties << mCollectionProperties;
3984         }
3985         mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_collectionproperties_actionlist"));
3986         mGUIClient->plugActionList(QStringLiteral("akonadi_collection_collectionproperties_actionlist"), collectionProperties);
3987     }
3988 
3989     const bool folderWithContent = mCurrentFolderSettings && !mCurrentFolderSettings->isStructural();
3990 
3991     if (QAction *act = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections)) {
3992         act->setEnabled(mCurrentFolderSettings && (mCurrentCollection.rights() & Collection::CanDeleteCollection) && !mCurrentFolderSettings->isSystemFolder()
3993                         && folderWithContent);
3994     }
3995 
3996     updateMoveAllToTrash();
3997 
3998     QList<QAction *> addToFavorite;
3999     QAction *actionAddToFavoriteCollections = akonadiStandardAction(Akonadi::StandardActionManager::AddToFavoriteCollections);
4000     if (actionAddToFavoriteCollections) {
4001         if (mEnableFavoriteFolderView && actionAddToFavoriteCollections->isEnabled()) {
4002             addToFavorite << actionAddToFavoriteCollections;
4003         }
4004         mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_add_to_favorites_actionlist"));
4005         mGUIClient->plugActionList(QStringLiteral("akonadi_collection_add_to_favorites_actionlist"), addToFavorite);
4006     }
4007 
4008     QList<QAction *> syncActionList;
4009     QAction *actionSync = akonadiStandardAction(Akonadi::StandardActionManager::SynchronizeCollections);
4010     if (actionSync && actionSync->isEnabled()) {
4011         syncActionList << actionSync;
4012     }
4013     actionSync = akonadiStandardAction(Akonadi::StandardActionManager::SynchronizeCollectionsRecursive);
4014     if (actionSync && actionSync->isEnabled()) {
4015         syncActionList << actionSync;
4016     }
4017     mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_sync_actionlist"));
4018     mGUIClient->plugActionList(QStringLiteral("akonadi_collection_sync_actionlist"), syncActionList);
4019 
4020     QList<QAction *> actionList;
4021 
4022     QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CreateCollection);
4023     if (action && action->isEnabled()) {
4024         actionList << action;
4025     }
4026 
4027     action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::MoveCollectionToMenu);
4028     if (action && action->isEnabled()) {
4029         actionList << action;
4030     }
4031 
4032     action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyCollectionToMenu);
4033     if (action && action->isEnabled()) {
4034         actionList << action;
4035     }
4036     mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_move_copy_menu_actionlist"));
4037     mGUIClient->plugActionList(QStringLiteral("akonadi_collection_move_copy_menu_actionlist"), actionList);
4038 }
4039 
4040 void KMMainWidget::updateHtmlMenuEntry()
4041 {
4042     if (mDisplayMessageFormatMenu && mPreferHtmlLoadExtAction) {
4043         // the visual ones only make sense if we are showing a message list
4044         const bool enabledAction = (mFolderTreeWidget && mFolderTreeWidget->folderTreeView()->currentFolder().isValid());
4045 
4046         mDisplayMessageFormatMenu->setEnabled(enabledAction);
4047         const bool isEnabled = (mFolderTreeWidget && mFolderTreeWidget->folderTreeView()->currentFolder().isValid());
4048         const bool useHtml = (mFolderDisplayFormatPreference == MessageViewer::Viewer::Html
4049                               || (mHtmlGlobalSetting && mFolderDisplayFormatPreference == MessageViewer::Viewer::UseGlobalSetting));
4050         mPreferHtmlLoadExtAction->setEnabled(isEnabled && useHtml);
4051 
4052         mDisplayMessageFormatMenu->setDisplayMessageFormat(mFolderDisplayFormatPreference);
4053 
4054         if (mFolderHtmlLoadExtPreference) {
4055             mPreferHtmlLoadExtAction->setChecked(true);
4056         } else {
4057             mPreferHtmlLoadExtAction->setChecked(mHtmlLoadExtGlobalSetting);
4058         }
4059     }
4060 }
4061 
4062 //-----------------------------------------------------------------------------
4063 void KMMainWidget::updateFolderMenu()
4064 {
4065     if (!CommonKernel->outboxCollectionFolder().isValid()) {
4066         QTimer::singleShot(1s, this, &KMMainWidget::updateFolderMenu);
4067         return;
4068     }
4069 
4070     const bool folderWithContent = mCurrentFolderSettings && !mCurrentFolderSettings->isStructural() && mCurrentFolderSettings->isValid();
4071     mFolderMailingListPropertiesAction->setEnabled(folderWithContent && !mCurrentFolderSettings->isSystemFolder());
4072 
4073     QList<QAction *> actionlist;
4074     if (mCurrentCollection.id() == CommonKernel->outboxCollectionFolder().id() && (mCurrentCollection).statistics().count() > 0) {
4075         qCDebug(KMAIL_LOG) << "Enabling send queued";
4076         mSendQueued->setEnabled(true);
4077         actionlist << mSendQueued;
4078     }
4079     //   if ( mCurrentCollection.id() != CommonKernel->trashCollectionFolder().id() ) {
4080     //     actionlist << mTrashAction;
4081     //   }
4082     mGUIClient->unplugActionList(QStringLiteral("outbox_folder_actionlist"));
4083     mGUIClient->plugActionList(QStringLiteral("outbox_folder_actionlist"), actionlist);
4084     actionlist.clear();
4085 
4086     const bool isASearchFolder = mCurrentCollection.resource() == QLatin1StringView("akonadi_search_resource");
4087     if (isASearchFolder) {
4088         mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections)->setText(i18n("&Delete Search"));
4089     }
4090 
4091     mArchiveFolderAction->setEnabled(mCurrentFolderSettings && folderWithContent);
4092 
4093     const bool isInTrashFolder = (mCurrentFolderSettings && CommonKernel->folderIsTrash(mCurrentCollection));
4094     QAction *moveToTrash = akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash);
4095     updateMoveAllToTrash();
4096 
4097     KMail::Util::setActionTrashOrDelete(moveToTrash, isInTrashFolder);
4098 
4099     mTrashThreadAction->setIcon(isInTrashFolder ? QIcon::fromTheme(QStringLiteral("edit-delete-shred")) : QIcon::fromTheme(QStringLiteral("edit-delete")));
4100     mTrashThreadAction->setText(isInTrashFolder ? i18n("Delete T&hread") : i18n("M&ove Thread to Trash"));
4101 
4102     mSearchMessages->setText((mCurrentCollection.resource() == QLatin1StringView("akonadi_search_resource")) ? i18n("Edit Search...")
4103                                                                                                              : i18n("&Find Messages..."));
4104 
4105     mExpireConfigAction->setEnabled(mCurrentFolderSettings && !mCurrentFolderSettings->isStructural() && mCurrentFolderSettings->canDeleteMessages()
4106                                     && folderWithContent && !MailCommon::Util::isVirtualCollection(mCurrentCollection));
4107 
4108     updateHtmlMenuEntry();
4109 
4110     mShowFolderShortcutDialogAction->setEnabled(folderWithContent);
4111     actionlist << akonadiStandardAction(Akonadi::StandardActionManager::ManageLocalSubscriptions);
4112     bool imapFolderIsOnline = false;
4113     if (mCurrentFolderSettings && PimCommon::MailUtil::isImapFolder(mCurrentCollection, imapFolderIsOnline)) {
4114         if (imapFolderIsOnline) {
4115             actionlist << mServerSideSubscription;
4116         }
4117     }
4118     if (mCurrentCollection.parentCollection() != Akonadi::Collection::root()) {
4119         mGUIClient->unplugActionList(QStringLiteral("resource_settings"));
4120         mGUIClient->unplugActionList(QStringLiteral("resource_restart"));
4121     } else {
4122         mGUIClient->plugActionList(QStringLiteral("resource_settings"), {mAccountSettings});
4123         mGUIClient->plugActionList(QStringLiteral("resource_restart"), {mRestartAccountSettings});
4124     }
4125 
4126     mGUIClient->unplugActionList(QStringLiteral("collectionview_actionlist"));
4127     mGUIClient->plugActionList(QStringLiteral("collectionview_actionlist"), actionlist);
4128 
4129     const bool folderIsValid = folderWithContent;
4130     mApplyAllFiltersFolderAction->setEnabled(folderIsValid);
4131     mApplyFilterFolderActionsMenu->setEnabled(folderIsValid);
4132     mApplyFilterFolderRecursiveActionsMenu->setEnabled(folderIsValid);
4133     for (auto a : std::as_const(mFilterFolderMenuActions)) {
4134         a->setEnabled(folderIsValid);
4135     }
4136     for (auto a : std::as_const(mFilterFolderMenuRecursiveActions)) {
4137         a->setEnabled(folderIsValid);
4138     }
4139     if (mCurrentCollection.resource() == QLatin1StringView("akonadi_unifiedmailbox_agent")) {
4140         mAccountSettings->setText(i18n("Configure Unified Mailbox"));
4141     } else {
4142         mAccountSettings->setText(i18n("Account &Settings"));
4143     }
4144 }
4145 
4146 void KMMainWidget::updateMoveAllToTrash()
4147 {
4148     if (QAction *act = mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)) {
4149         const bool folderWithContent = mCurrentFolderSettings && !mCurrentFolderSettings->isStructural();
4150         act->setEnabled(folderWithContent && (mCurrentFolderSettings->count() > 0) && mCurrentFolderSettings->canDeleteMessages());
4151         act->setText((mCurrentFolderSettings && CommonKernel->folderIsTrash(mCurrentCollection)) ? i18n("E&mpty Trash") : i18n("&Move All Messages to Trash"));
4152     }
4153 }
4154 
4155 //-----------------------------------------------------------------------------
4156 void KMMainWidget::slotIntro()
4157 {
4158     if (!mMsgView) {
4159         return;
4160     }
4161 
4162     mMsgView->clear(true);
4163 
4164     // hide widgets that are in the way:
4165     if (mMessagePane && mLongFolderList) {
4166         mMessagePane->hide();
4167     }
4168     mMsgView->displayAboutPage();
4169 
4170     clearCurrentFolder();
4171 }
4172 
4173 void KMMainWidget::slotShowStartupFolder()
4174 {
4175     connect(MailCommon::FilterManager::instance(), &FilterManager::filtersChanged, this, [this]() {
4176         initializeFilterActions(true);
4177     });
4178     // Plug various action lists. This can't be done in the constructor, as that is called before
4179     // the main window or Kontact calls createGUI().
4180     // This function however is called with a single shot timer.
4181     checkAkonadiServerManagerState();
4182     mFolderShortcutActionManager->createActions();
4183     mTagActionManager->createActions();
4184     messageActions()->setupForwardingActionsList(mGUIClient);
4185     initializePluginActions();
4186 
4187     const QString newFeaturesMD5 = KMReaderWin::newFeaturesMD5();
4188     if (kmkernel->firstStart() || KMailSettings::self()->previousNewFeaturesMD5() != newFeaturesMD5) {
4189         KMailSettings::self()->setPreviousNewFeaturesMD5(newFeaturesMD5);
4190         slotIntro();
4191     }
4192 }
4193 
4194 void KMMainWidget::checkAkonadiServerManagerState()
4195 {
4196     Akonadi::ServerManager::State state = Akonadi::ServerManager::self()->state();
4197     if (state == Akonadi::ServerManager::Running) {
4198         initializeFilterActions(true);
4199     } else {
4200         connect(Akonadi::ServerManager::self(), &ServerManager::stateChanged, this, &KMMainWidget::slotServerStateChanged);
4201     }
4202 }
4203 
4204 void KMMainWidget::slotServerStateChanged(Akonadi::ServerManager::State state)
4205 {
4206     if (state == Akonadi::ServerManager::Running) {
4207         initializeFilterActions(true);
4208         disconnect(Akonadi::ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State)));
4209     }
4210 }
4211 
4212 QList<KActionCollection *> KMMainWidget::actionCollections() const
4213 {
4214     return QList<KActionCollection *>() << actionCollection();
4215 }
4216 
4217 //-----------------------------------------------------------------------------
4218 void KMMainWidget::slotUpdateUndo()
4219 {
4220     if (actionCollection()->action(QStringLiteral("kmail_undo"))) {
4221         QAction *act = actionCollection()->action(QStringLiteral("kmail_undo"));
4222         act->setEnabled(!kmkernel->undoStack()->isEmpty());
4223         const QString infoStr = kmkernel->undoStack()->undoInfo();
4224         if (infoStr.isEmpty()) {
4225             act->setText(i18n("&Undo"));
4226         } else {
4227             act->setText(i18n("&Undo: \"%1\"", kmkernel->undoStack()->undoInfo()));
4228         }
4229     }
4230 }
4231 
4232 //-----------------------------------------------------------------------------
4233 void KMMainWidget::clearFilterActions()
4234 {
4235     if (mGUIClient->factory()) {
4236         if (!mFilterTBarActions.isEmpty()) {
4237             mGUIClient->unplugActionList(QStringLiteral("toolbar_filter_actions"));
4238         }
4239         if (!mFilterMenuActions.isEmpty()) {
4240             mGUIClient->unplugActionList(QStringLiteral("menu_filter_actions"));
4241         }
4242         if (!mFilterFolderMenuActions.isEmpty()) {
4243             mGUIClient->unplugActionList(QStringLiteral("menu_filter_folder_actions"));
4244         }
4245         if (!mFilterFolderMenuRecursiveActions.isEmpty()) {
4246             mGUIClient->unplugActionList(QStringLiteral("menu_filter_folder_recursive_actions"));
4247         }
4248     }
4249 
4250     for (QAction *a : std::as_const(mFilterMenuActions)) {
4251         actionCollection()->removeAction(a);
4252     }
4253     for (QAction *a : std::as_const(mFilterFolderMenuActions)) {
4254         actionCollection()->removeAction(a);
4255     }
4256     for (QAction *a : std::as_const(mFilterFolderMenuRecursiveActions)) {
4257         actionCollection()->removeAction(a);
4258     }
4259 
4260     mApplyFilterActionsMenu->menu()->clear();
4261     mApplyFilterFolderActionsMenu->menu()->clear();
4262     mApplyFilterFolderRecursiveActionsMenu->menu()->clear();
4263     mFilterTBarActions.clear();
4264     mFilterMenuActions.clear();
4265     mFilterFolderMenuActions.clear();
4266     mFilterFolderMenuRecursiveActions.clear();
4267 
4268     qDeleteAll(mFilterCommands);
4269     mFilterCommands.clear();
4270 }
4271 
4272 void KMMainWidget::clearPluginActions()
4273 {
4274     KMailPluginInterface::self()->clearPluginActions(QStringLiteral("kmail"), mGUIClient);
4275 }
4276 
4277 void KMMainWidget::initializePluginActions()
4278 {
4279     KMailPluginInterface::self()->initializePluginActions(QStringLiteral("kmail"), mGUIClient);
4280 }
4281 
4282 QAction *KMMainWidget::filterToAction(MailCommon::MailFilter *filter)
4283 {
4284     const QString displayText = i18n("Filter %1", filter->name());
4285     QString icon = filter->icon();
4286     if (icon.isEmpty()) {
4287         icon = QStringLiteral("system-run");
4288     }
4289     auto filterAction = new QAction(QIcon::fromTheme(icon), displayText, actionCollection());
4290     filterAction->setProperty("filter_id", filter->identifier());
4291     filterAction->setIconText(filter->toolbarName());
4292 
4293     // The shortcut configuration is done in the filter dialog.
4294     // The shortcut set in the shortcut dialog would not be saved back to
4295     // the filter settings correctly.
4296     actionCollection()->setShortcutsConfigurable(filterAction, false);
4297 
4298     return filterAction;
4299 }
4300 
4301 //-----------------------------------------------------------------------------
4302 void KMMainWidget::initializeFilterActions(bool clearFilter)
4303 {
4304     if (clearFilter) {
4305         clearFilterActions();
4306     }
4307     mApplyFilterActionsMenu->menu()->addAction(mApplyAllFiltersAction);
4308     mApplyFilterFolderActionsMenu->menu()->addAction(mApplyAllFiltersFolderAction);
4309     mApplyFilterFolderRecursiveActionsMenu->menu()->addAction(mApplyAllFiltersFolderRecursiveAction);
4310     bool addedSeparator = false;
4311 
4312     const QList<MailFilter *> lstFilters = MailCommon::FilterManager::instance()->filters();
4313     // qDebug() << " lstFilters " << lstFilters.count();
4314     for (MailFilter *filter : lstFilters) {
4315         if (!filter->isEmpty() && filter->configureShortcut() && filter->isEnabled()) {
4316             QString filterName = QStringLiteral("Filter %1").arg(filter->name());
4317             filterName.replace(QLatin1Char(' '), QLatin1Char('_'));
4318             if (action(filterName)) {
4319                 continue;
4320             }
4321 
4322             if (!addedSeparator) {
4323                 QAction *a = mApplyFilterActionsMenu->menu()->addSeparator();
4324                 mFilterMenuActions.append(a);
4325                 a = mApplyFilterFolderActionsMenu->menu()->addSeparator();
4326                 mFilterFolderMenuActions.append(a);
4327                 a = mApplyFilterFolderRecursiveActionsMenu->menu()->addSeparator();
4328                 mFilterFolderMenuRecursiveActions.append(a);
4329                 addedSeparator = true;
4330             }
4331 
4332             auto filterCommand = new KMMetaFilterActionCommand(filter->identifier(), this);
4333             mFilterCommands.append(filterCommand);
4334 
4335             auto filterAction = filterToAction(filter);
4336             actionCollection()->addAction(filterName, filterAction);
4337             connect(filterAction, &QAction::triggered, filterCommand, &KMMetaFilterActionCommand::start);
4338             actionCollection()->setDefaultShortcut(filterAction, filter->shortcut());
4339             mApplyFilterActionsMenu->menu()->addAction(filterAction);
4340             mFilterMenuActions.append(filterAction);
4341             if (filter->configureToolbar()) {
4342                 mFilterTBarActions.append(filterAction);
4343             }
4344 
4345             filterAction = filterToAction(filter);
4346             actionCollection()->addAction(filterName + QStringLiteral("___folder"), filterAction);
4347             connect(filterAction, &QAction::triggered, this, [this] {
4348                 slotApplyFilterOnFolder(/* recursive */ false);
4349             });
4350             mApplyFilterFolderActionsMenu->menu()->addAction(filterAction);
4351             mFilterFolderMenuActions.append(filterAction);
4352 
4353             filterAction = filterToAction(filter);
4354             actionCollection()->addAction(filterName + QStringLiteral("___folder_recursive"), filterAction);
4355             connect(filterAction, &QAction::triggered, this, [this] {
4356                 slotApplyFilterOnFolder(/* recursive */ true);
4357             });
4358             mApplyFilterFolderRecursiveActionsMenu->menu()->addAction(filterAction);
4359             mFilterFolderMenuRecursiveActions.append(filterAction);
4360         }
4361     }
4362 
4363     if (mGUIClient->factory()) {
4364         if (!mFilterMenuActions.isEmpty()) {
4365             mGUIClient->plugActionList(QStringLiteral("menu_filter_actions"), mFilterMenuActions);
4366         }
4367         if (!mFilterTBarActions.isEmpty()) {
4368             mFilterTBarActions.prepend(mToolbarActionSeparator);
4369             mGUIClient->plugActionList(QStringLiteral("toolbar_filter_actions"), mFilterTBarActions);
4370         }
4371         if (!mFilterFolderMenuActions.isEmpty()) {
4372             mGUIClient->plugActionList(QStringLiteral("menu_filter_folder_actions"), mFilterFolderMenuActions);
4373         }
4374         if (!mFilterFolderMenuRecursiveActions.isEmpty()) {
4375             mGUIClient->plugActionList(QStringLiteral("menu_filter_folder_recursive_actions"), mFilterFolderMenuRecursiveActions);
4376         }
4377     }
4378 
4379     // Our filters have changed, now enable/disable them
4380     updateMessageActions();
4381 }
4382 
4383 void KMMainWidget::updateFileMenu()
4384 {
4385     const bool isEmpty = MailCommon::Util::agentInstances().isEmpty();
4386     actionCollection()->action(QStringLiteral("check_mail"))->setEnabled(!isEmpty);
4387     actionCollection()->action(QStringLiteral("check_mail_in"))->setEnabled(!isEmpty);
4388 }
4389 
4390 //-----------------------------------------------------------------------------
4391 const KMMainWidget *KMMainWidget::mainWidgetList()
4392 {
4393     // better safe than sorry; check whether the global static has already been destroyed
4394     if (!myMainWidget) {
4395         return nullptr;
4396     }
4397     return myMainWidget;
4398 }
4399 
4400 QSharedPointer<FolderSettings> KMMainWidget::currentFolder() const
4401 {
4402     return mCurrentFolderSettings;
4403 }
4404 
4405 QAction *KMMainWidget::action(const QString &name)
4406 {
4407     return mActionCollection->action(name);
4408 }
4409 
4410 KActionMenu *KMMainWidget::filterMenu() const
4411 {
4412     return mFilterMenu;
4413 }
4414 
4415 KActionMenu *KMMainWidget::mailingListActionMenu() const
4416 {
4417     return mMsgActions->mailingListActionMenu();
4418 }
4419 
4420 QAction *KMMainWidget::sendQueuedAction() const
4421 {
4422     return mSendQueued;
4423 }
4424 
4425 KActionMenuTransport *KMMainWidget::sendQueueViaMenu() const
4426 {
4427     return mSendActionMenu;
4428 }
4429 
4430 KMail::MessageActions *KMMainWidget::messageActions() const
4431 {
4432     return mMsgActions;
4433 }
4434 
4435 //-----------------------------------------------------------------------------
4436 QString KMMainWidget::overrideEncoding() const
4437 {
4438     if (mMsgView) {
4439         return mMsgView->overrideEncoding();
4440     } else {
4441         return MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding();
4442     }
4443 }
4444 
4445 void KMMainWidget::showEvent(QShowEvent *event)
4446 {
4447     QWidget::showEvent(event);
4448     mWasEverShown = true;
4449 }
4450 
4451 KActionCollection *KMMainWidget::actionCollection() const
4452 {
4453     return mActionCollection;
4454 }
4455 
4456 void KMMainWidget::slotRequestFullSearchFromQuickSearch()
4457 {
4458     // First, open the search window. If we are currently on a search folder,
4459     // the search associated with that will be loaded.
4460     if (!showSearchDialog()) {
4461         return;
4462     }
4463 
4464     assert(mSearchWin);
4465 
4466     // Now we look at the current state of the quick search, and if there's
4467     // something in there, we add the criteria to the existing search for
4468     // the search folder, if applicable, or make a new one from it.
4469     SearchPattern pattern;
4470     const QString searchString = mMessagePane->currentFilterSearchString();
4471     if (!searchString.isEmpty()) {
4472         MessageList::Core::QuickSearchLine::SearchOptions options = mMessagePane->currentOptions();
4473         QByteArray searchStringVal;
4474         if (options & MessageList::Core::QuickSearchLine::SearchEveryWhere) {
4475             searchStringVal = QByteArrayLiteral("<message>");
4476         } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstSubject) {
4477             searchStringVal = QByteArrayLiteral("subject");
4478         } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstBody) {
4479             searchStringVal = QByteArrayLiteral("<body>");
4480         } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstFrom) {
4481             searchStringVal = QByteArrayLiteral("from");
4482         } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstBcc) {
4483             searchStringVal = QByteArrayLiteral("bcc");
4484         } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstTo) {
4485             searchStringVal = QByteArrayLiteral("to");
4486         } else {
4487             searchStringVal = QByteArrayLiteral("<message>");
4488         }
4489         pattern.append(SearchRule::createInstance(searchStringVal, SearchRule::FuncContains, searchString));
4490         const QList<MessageStatus> statusList = mMessagePane->currentFilterStatus();
4491         for (MessageStatus status : statusList) {
4492             if (status.hasAttachment()) {
4493                 pattern.append(SearchRule::createInstance(searchStringVal, SearchRule::FuncHasAttachment, searchString));
4494                 status.setHasAttachment(false);
4495             }
4496             if (!status.isOfUnknownStatus()) {
4497                 pattern.append(SearchRule::Ptr(new SearchRuleStatus(status)));
4498             }
4499         }
4500     }
4501     if (!pattern.isEmpty()) {
4502         mSearchWin->addRulesToSearchPattern(pattern);
4503     }
4504 }
4505 
4506 void KMMainWidget::updateVacationScriptStatus(bool active, const QString &serverName)
4507 {
4508     mVacationScriptIndicator->setVacationScriptActive(active, serverName);
4509     mVacationIndicatorActive = mVacationScriptIndicator->hasVacationScriptActive();
4510 }
4511 
4512 QWidget *KMMainWidget::vacationScriptIndicator() const
4513 {
4514     return mVacationScriptIndicator;
4515 }
4516 
4517 QWidget *KMMainWidget::zoomLabelIndicator() const
4518 {
4519     return mZoomLabelIndicator;
4520 }
4521 
4522 FolderTreeView *KMMainWidget::folderTreeView() const
4523 {
4524     return mFolderTreeWidget->folderTreeView();
4525 }
4526 
4527 KXMLGUIClient *KMMainWidget::guiClient() const
4528 {
4529     return mGUIClient;
4530 }
4531 
4532 KMail::TagActionManager *KMMainWidget::tagActionManager() const
4533 {
4534     return mTagActionManager;
4535 }
4536 
4537 KMail::FolderShortcutActionManager *KMMainWidget::folderShortcutActionManager() const
4538 {
4539     return mFolderShortcutActionManager;
4540 }
4541 
4542 void KMMainWidget::slotMessageSelected(const Akonadi::Item &item)
4543 {
4544     delete mShowBusySplashTimer;
4545     mShowBusySplashTimer = nullptr;
4546     if (mMsgView) {
4547         // The current selection was cleared, so we'll remove the previously
4548         // selected message from the preview pane
4549         if (!item.isValid()) {
4550             mMsgView->clear();
4551         } else {
4552             mShowBusySplashTimer = new QTimer(this);
4553             mShowBusySplashTimer->setSingleShot(true);
4554             connect(mShowBusySplashTimer, &QTimer::timeout, this, &KMMainWidget::slotShowBusySplash);
4555             mShowBusySplashTimer->start(1s);
4556 
4557             Akonadi::ItemFetchJob *itemFetchJob = mMsgView->viewer()->createFetchJob(item);
4558             if (mCurrentCollection.isValid()) {
4559                 const QString resource = mCurrentCollection.resource();
4560                 itemFetchJob->setProperty("_resource", QVariant::fromValue(resource));
4561                 connect(itemFetchJob, &ItemFetchJob::itemsReceived, this, &KMMainWidget::itemsReceived);
4562                 connect(itemFetchJob, &Akonadi::ItemFetchJob::result, this, &KMMainWidget::itemsFetchDone);
4563             }
4564         }
4565     }
4566 }
4567 
4568 void KMMainWidget::itemsReceived(const Akonadi::Item::List &list)
4569 {
4570     // qCDebug(KMAIL_LOG) << " list count  " << list.count();
4571     // Q_ASSERT(list.size() == 1);
4572     delete mShowBusySplashTimer;
4573     mShowBusySplashTimer = nullptr;
4574 
4575     if (!mMsgView) {
4576         return;
4577     }
4578 
4579     Item item = list.first();
4580 
4581     if (mMessagePane) {
4582         mMessagePane->show();
4583 
4584         if (mMessagePane->currentItem() != item) {
4585             // The user has selected another email already, so don't render this one.
4586             // Mark it as read, though, if the user settings say so.
4587             if (MessageViewer::MessageViewerSettings::self()->delayedMarkAsRead() && MessageViewer::MessageViewerSettings::self()->delayedMarkTime() == 0) {
4588                 item.setFlag(Akonadi::MessageFlags::Seen);
4589                 auto modifyJob = new Akonadi::ItemModifyJob(item, this);
4590                 modifyJob->disableRevisionCheck();
4591                 modifyJob->setIgnorePayload(true);
4592             }
4593             return;
4594         }
4595     }
4596 
4597     Akonadi::Item copyItem(item);
4598     if (mCurrentCollection.isValid()) {
4599         copyItem.setParentCollection(mCurrentCollection);
4600     }
4601 
4602     mMsgView->setMessage(copyItem);
4603     assignLoadExternalReference();
4604     mMsgView->setDecryptMessageOverwrite(false);
4605     mMsgActions->setCurrentMessage(copyItem);
4606 }
4607 
4608 void KMMainWidget::itemsFetchDone(KJob *job)
4609 {
4610     delete mShowBusySplashTimer;
4611     mShowBusySplashTimer = nullptr;
4612     if (job->error()) {
4613         // Unfortunately job->error() is Job::Unknown in many cases.
4614         // (see JobPrivate::handleResponse in akonadi/job.cpp)
4615         // So we show the "offline" page after checking the resource status.
4616         qCDebug(KMAIL_LOG) << job->error() << job->errorString();
4617 
4618         const QString resource = job->property("_resource").toString();
4619         const Akonadi::AgentInstance agentInstance = Akonadi::AgentManager::self()->instance(resource);
4620         if (!agentInstance.isOnline()) {
4621             // The resource is offline
4622             mMessagePane->show();
4623             if (mMsgView) {
4624                 mMsgView->viewer()->enableMessageDisplay();
4625                 mMsgView->clear(true);
4626                 if (kmkernel->isOffline()) {
4627                     showOfflinePage();
4628                 } else {
4629                     showResourceOfflinePage();
4630                 }
4631             }
4632         } else {
4633             // Some other error
4634             showMessageActivities(job->errorString());
4635         }
4636     }
4637 }
4638 
4639 QAction *KMMainWidget::akonadiStandardAction(Akonadi::StandardActionManager::Type type)
4640 {
4641     return mAkonadiStandardActionManager->action(type);
4642 }
4643 
4644 QAction *KMMainWidget::akonadiStandardAction(Akonadi::StandardMailActionManager::Type type)
4645 {
4646     return mAkonadiStandardActionManager->action(type);
4647 }
4648 
4649 StandardMailActionManager *KMMainWidget::standardMailActionManager() const
4650 {
4651     return mAkonadiStandardActionManager;
4652 }
4653 
4654 void KMMainWidget::slotRemoveDuplicates()
4655 {
4656     auto job = new RemoveDuplicateMailJob(mFolderTreeWidget->folderTreeView()->selectionModel(), this, this);
4657     job->start();
4658 }
4659 
4660 void KMMainWidget::slotServerSideSubscription()
4661 {
4662     if (!mCurrentCollection.isValid()) {
4663         return;
4664     }
4665     auto job = new PimCommon::ManageServerSideSubscriptionJob(this);
4666     job->setCurrentCollection(mCurrentCollection);
4667     job->setParentWidget(this);
4668     job->start();
4669 }
4670 
4671 void KMMainWidget::slotAccountSettings()
4672 {
4673     if (!mCurrentCollection.isValid() || mCurrentCollection.parentCollection() != Akonadi::Collection::root()) {
4674         return;
4675     }
4676 
4677     auto instance = Akonadi::AgentManager::self()->instance(mCurrentCollection.resource());
4678     if (!instance.isValid()) {
4679         return;
4680     }
4681 
4682     QPointer<AgentConfigurationDialog> dlg = new AgentConfigurationDialog(instance, this);
4683     dlg->exec();
4684     delete dlg;
4685 }
4686 
4687 void KMMainWidget::slotRestartAccount()
4688 {
4689     if (!mCurrentCollection.isValid() || mCurrentCollection.parentCollection() != Akonadi::Collection::root()) {
4690         return;
4691     }
4692 
4693     auto instance = Akonadi::AgentManager::self()->instance(mCurrentCollection.resource());
4694     if (!instance.isValid()) {
4695         return;
4696     }
4697 
4698     instance.restart();
4699 }
4700 
4701 void KMMainWidget::savePaneSelection()
4702 {
4703     if (mMessagePane) {
4704         mMessagePane->saveCurrentSelection();
4705     }
4706 }
4707 
4708 void KMMainWidget::updatePaneTagComboBox()
4709 {
4710     if (mMessagePane) {
4711         mMessagePane->updateTagComboBox();
4712     }
4713 }
4714 
4715 void KMMainWidget::slotCreateAddressBookContact()
4716 {
4717     auto job = new CreateNewContactJob(this, this);
4718     job->start();
4719 }
4720 
4721 void KMMainWidget::slotOpenRecentMessage(const QUrl &url)
4722 {
4723     auto openCommand = new KMOpenMsgCommand(this, url, overrideEncoding(), this);
4724     openCommand->start();
4725 }
4726 
4727 void KMMainWidget::addRecentFile(const QUrl &url)
4728 {
4729     mOpenRecentMenu->addUrl(url);
4730 }
4731 
4732 void KMMainWidget::slotMoveMessageToTrash()
4733 {
4734     if (messageView() && messageView()->viewer() && mCurrentCollection.isValid()) {
4735         auto command = new KMTrashMsgCommand(mCurrentCollection, messageView()->viewer()->messageItem(), -1);
4736         command->start();
4737     }
4738 }
4739 
4740 void KMMainWidget::slotArchiveMails()
4741 {
4742     if (mCurrentCollection.isValid()) {
4743         const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
4744         KMKernel::self()->folderArchiveManager()->setArchiveItems(selectedMessages, mCurrentCollection.resource());
4745     }
4746 }
4747 
4748 void KMMainWidget::updateQuickSearchLineText()
4749 {
4750     // If change change shortcut
4751     mMessagePane->setQuickSearchClickMessage(
4752         i18nc("Show shortcut for focus quick search. Don't change it", "Search... <%1>", mQuickSearchAction->shortcut().toString()));
4753 }
4754 
4755 void KMMainWidget::slotChangeDisplayMessageFormat(MessageViewer::Viewer::DisplayFormatMessage format)
4756 {
4757     if (format == MessageViewer::Viewer::Html) {
4758         const int result = KMessageBox::warningContinueCancel(this,
4759                                                               // the warning text is taken from configuredialog.cpp:
4760                                                               i18n("Use of HTML in mail will make you more vulnerable to "
4761                                                                    "\"spam\" and may increase the likelihood that your system will be "
4762                                                                    "compromised by other present and anticipated security exploits."),
4763                                                               i18nc("@title:window", "Security Warning"),
4764                                                               KGuiItem(i18n("Use HTML")),
4765                                                               KStandardGuiItem::cancel(),
4766                                                               QStringLiteral("OverrideHtmlWarning"),
4767                                                               KMessageBox::Option());
4768         if (result == KMessageBox::Cancel) {
4769             mDisplayMessageFormatMenu->setDisplayMessageFormat(MessageViewer::Viewer::Text);
4770             return;
4771         }
4772     }
4773     mFolderDisplayFormatPreference = format;
4774 
4775     // Update mPrefererHtmlLoadExtAction
4776     const bool useHtml = (mFolderDisplayFormatPreference == MessageViewer::Viewer::Html
4777                           || (mHtmlGlobalSetting && mFolderDisplayFormatPreference == MessageViewer::Viewer::UseGlobalSetting));
4778     mPreferHtmlLoadExtAction->setEnabled(useHtml);
4779 
4780     if (mMsgView) {
4781         mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference);
4782         mMsgView->update(true);
4783     }
4784     writeFolderConfig();
4785 }
4786 
4787 void KMMainWidget::populateMessageListStatusFilterCombo()
4788 {
4789     mMessagePane->populateStatusFilterCombo();
4790 }
4791 
4792 void KMMainWidget::slotCollectionRemoved(const Akonadi::Collection &col)
4793 {
4794     if (mFavoritesModel) {
4795         mFavoritesModel->removeCollection(col);
4796     }
4797 }
4798 
4799 void KMMainWidget::slotMarkAllMessageAsReadInCurrentFolderAndSubfolder()
4800 {
4801     if (mCurrentCollection.isValid()) {
4802         auto job = new MarkAllMessagesAsReadInFolderAndSubFolderJob(this);
4803         job->setTopLevelCollection(mCurrentCollection);
4804         job->start();
4805     }
4806 }
4807 
4808 void KMMainWidget::slotRemoveDuplicateRecursive()
4809 {
4810     if (mCurrentCollection.isValid()) {
4811         auto job = new RemoveDuplicateMessageInFolderAndSubFolderJob(this, this);
4812         job->setTopLevelCollection(mCurrentCollection);
4813         job->start();
4814     }
4815 }
4816 
4817 void KMMainWidget::slotUpdateConfig()
4818 {
4819     updateDisplayFormatMessage();
4820 }
4821 
4822 void KMMainWidget::slotRedirectCurrentMessage()
4823 {
4824     if (messageView() && messageView()->viewer()) {
4825         const Akonadi::Item currentItem = messageView()->viewer()->messageItem();
4826         if (!currentItem.hasPayload<KMime::Message::Ptr>()) {
4827             return;
4828         }
4829         auto command = new KMRedirectCommand(this, currentItem);
4830         command->start();
4831     }
4832 }
4833 
4834 void KMMainWidget::replyMessageTo(const Akonadi::Item &item, bool replyToAll)
4835 {
4836     auto command = new KMReplyCommand(this, item, replyToAll ? MessageComposer::ReplyAll : MessageComposer::ReplyAuthor);
4837     command->setReplyAsHtml(messageView() ? messageView()->htmlMail() : false);
4838     command->start();
4839 }
4840 
4841 void KMMainWidget::slotReplyMessageTo(const KMime::Message::Ptr &message, bool replyToAll)
4842 {
4843     Akonadi::Item item;
4844 
4845     item.setPayload<KMime::Message::Ptr>(message);
4846     item.setMimeType(KMime::Message::mimeType());
4847     replyMessageTo(item, replyToAll);
4848 }
4849 
4850 void KMMainWidget::showMessageActivities(const QString &str)
4851 {
4852     BroadcastStatus::instance()->setStatusMsg(str);
4853 }
4854 
4855 void KMMainWidget::slotCopyDecryptedTo(QAction *action)
4856 {
4857     if (action) {
4858         const auto index = action->data().toModelIndex();
4859         const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
4860 
4861         auto command = new KMCopyDecryptedCommand(collection, mMessagePane->selectionAsMessageItemList());
4862         command->start();
4863     }
4864 }
4865 
4866 void KMMainWidget::slotSetFocusToViewer()
4867 {
4868     if (messageView() && messageView()->viewer()) {
4869         messageView()->viewer()->setFocus();
4870     }
4871 }
4872 
4873 void KMMainWidget::setShowStatusBarMessage(const QString &msg)
4874 {
4875     if (mCurrentStatusBar) {
4876         mCurrentStatusBar->showMessage(msg);
4877     }
4878 }
4879 
4880 void KMMainWidget::setupUnifiedMailboxChecker()
4881 {
4882     if (!KMailSettings::self()->askEnableUnifiedMailboxes()) {
4883         return;
4884     }
4885 
4886     const auto ask = [this]() {
4887         if (!KMailSettings::self()->askEnableUnifiedMailboxes()) {
4888             return;
4889         }
4890 
4891         if (kmkernel->accounts().count() <= 1) {
4892             return;
4893         }
4894 
4895         KMailSettings::self()->setAskEnableUnifiedMailboxes(false);
4896 
4897         const auto service = Akonadi::ServerManager::self()->agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_unifiedmailbox_agent"));
4898         QDBusInterface iface(service, QStringLiteral("/"), QStringLiteral("org.freedesktop.Akonadi.UnifiedMailboxAgent"), QDBusConnection::sessionBus(), this);
4899         if (!iface.isValid()) {
4900             return;
4901         }
4902 
4903         QDBusReply<bool> reply = iface.call(QStringLiteral("enabledAgent"));
4904         if (!reply.isValid() || bool(reply)) {
4905             return;
4906         }
4907 
4908         const auto answer = KMessageBox::questionTwoActions(
4909             this,
4910             i18n("You have more than one email account set up.\nDo you want to enable the Unified Mailbox feature to "
4911                  "show unified content of your inbox, sent and drafts folders?\n"
4912                  "You can configure unified mailboxes, create custom ones or\ndisable the feature completely in KMail's Plugin settings."),
4913             i18n("Enable Unified Mailboxes?"),
4914             KGuiItem(i18n("Enable Unified Mailboxes"), QStringLiteral("dialog-ok")),
4915             KGuiItem(i18n("Cancel"), QStringLiteral("dialog-cancel")));
4916         if (answer == KMessageBox::ButtonCode::PrimaryAction) {
4917             iface.call(QStringLiteral("setEnableAgent"), true);
4918         }
4919     };
4920 
4921     connect(kmkernel, &KMKernel::incomingAccountsChanged, this, ask);
4922 
4923     // Wait for a bit before asking so we at least have the window on screen
4924     QTimer::singleShot(500ms, this, ask);
4925 }
4926 
4927 void KMMainWidget::slotClearFolder()
4928 {
4929     auto job = new Akonadi::ClearCacheFoldersJob(mCurrentCollection, this);
4930     job->setParentWidget(this);
4931     connect(job, &ClearCacheFoldersJob::clearCacheDone, this, &KMMainWidget::slotClearCacheDone);
4932     job->start();
4933 }
4934 
4935 void KMMainWidget::slotClearFolderAndSubFolders()
4936 {
4937     auto job = new ClearCacheJobInFolderAndSubFolderJob(this, this);
4938     job->setTopLevelCollection(mCurrentCollection);
4939     connect(job, &ClearCacheJobInFolderAndSubFolderJob::clearCacheDone, this, &KMMainWidget::slotClearCacheDone);
4940     job->start();
4941 }
4942 
4943 void KMMainWidget::slotClearCacheDone()
4944 {
4945     const QString akonadictlPath = QStandardPaths::findExecutable(QStringLiteral("akonadictl"));
4946     if (akonadictlPath.isEmpty()) {
4947         qCWarning(KMAIL_LOG) << "Impossible to find akonadictl apps";
4948     } else {
4949         if (KMessageBox::questionTwoActions(this,
4950                                             i18n("Do you want to restart Akonadi?"),
4951                                             i18n("Restart Akonadi"),
4952                                             KGuiItem(i18n("Restart")),
4953                                             KStandardGuiItem::cancel())
4954             == KMessageBox::ButtonCode::PrimaryAction) {
4955             auto process = new QProcess(this);
4956             process->setProgram(QStandardPaths::findExecutable(QStringLiteral("akonadictl")));
4957             process->setArguments(QStringList() << QStringLiteral("restart"));
4958             connect(process, &QProcess::finished, this, [this, process]() {
4959                 KMessageBox::information(this, i18n("Akonadi restarted."));
4960                 process->deleteLater();
4961             });
4962             process->start();
4963         }
4964     }
4965 }
4966 
4967 void KMMainWidget::slotRestoreClosedMessage(Akonadi::Item::Id id)
4968 {
4969     qDebug() << " ID " << id;
4970     slotMessageActivated(Akonadi::Item(id));
4971 }
4972 
4973 void KMMainWidget::slotHistoryClosedReaderChanged()
4974 {
4975     mRestoreClosedMessageMenu->setEnabled(!HistoryClosedReaderManager::self()->isEmpty());
4976 }
4977 
4978 #include "moc_kmmainwidget.cpp"