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"