Fix Bug 361605 - kmail crash on double click email
[kdepim.git] / kmail / kmmainwidget.cpp
blob79bf6cc4c468496649626124d1eb11736e582ade
1 /*
2 This file is part of KMail, the KDE mail client.
3 Copyright (c) 2002 Don Sanders <sanders@kde.org>
4 Copyright (c) 2009-2016 Montel Laurent <montel@kde.org>
6 Based on the work of Stefan Taferner <taferner@kde.org>
8 KMail is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License, version 2, as
10 published by the Free Software Foundation.
12 KMail is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 // KMail includes
23 #include "kmreadermainwin.h"
24 #include "editor/composer.h"
25 #include "searchdialog/searchwindow.h"
26 #include "widgets/vacationscriptindicatorwidget.h"
27 #include "undostack.h"
28 #include "kmcommands.h"
29 #include "kmmainwin.h"
30 #include "kmsystemtray.h"
31 #include <TemplateParser/CustomTemplatesMenu>
32 #include "MailCommon/FolderSelectionDialog"
33 #include "MailCommon/FolderTreeWidget"
34 #include "util.h"
35 #include "mailcommon/mailutil.h"
36 #include "mailcommon/mailkernel.h"
37 #include "dialog/archivefolderdialog.h"
38 #include "settings/kmailsettings.h"
39 #include "MailCommon/FolderTreeView"
40 #include "tag/tagactionmanager.h"
41 #include "foldershortcutactionmanager.h"
42 #include "widgets/collectionpane.h"
43 #include "manageshowcollectionproperties.h"
44 #include "widgets/kactionmenutransport.h"
45 #include "widgets/kactionmenuaccount.h"
46 #include "mailcommon/searchrulestatus.h"
47 #include "plugininterface/kmailplugininterface.h"
48 #include "PimCommon/NetworkUtil"
49 #include "kpimtextedit/texttospeech.h"
50 #include "job/markallmessagesasreadinfolderandsubfolderjob.h"
51 #include "job/removeduplicatemessageinfolderandsubfolderjob.h"
52 #if !defined(NDEBUG)
53 #include <KSieveUi/SieveDebugDialog>
54 using KSieveUi::SieveDebugDialog;
55 #endif
57 #include "collectionpage/collectionmaintenancepage.h"
58 #include "collectionpage/collectionquotapage.h"
59 #include "collectionpage/collectiontemplatespage.h"
60 #include "collectionpage/collectionshortcutpage.h"
61 #include "collectionpage/collectionviewpage.h"
62 #include "collectionpage/collectionmailinglistpage.h"
63 #include "tag/tagselectdialog.h"
64 #include "job/createnewcontactjob.h"
65 #include "folderarchive/folderarchiveutil.h"
66 #include "folderarchive/folderarchivemanager.h"
68 #include "PimCommon/CollectionAclPage"
69 #include "PimCommon/PimUtil"
70 #include "MailCommon/CollectionGeneralPage"
71 #include "MailCommon/CollectionExpiryPage"
72 #include "MailCommon/ExpireCollectionAttribute"
73 #include "MailCommon/FilterManager"
74 #include "MailCommon/MailFilter"
75 #include "MailCommon/FavoriteCollectionWidget"
76 #include "MailCommon/FolderTreeWidget"
77 #include "MailCommon/FolderTreeView"
78 #include "mailcommonsettings_base.h"
79 #include "kmmainwidget.h"
81 // Other PIM includes
82 #include "kdepim-version.h"
84 #include "messageviewer/messageviewersettings.h"
85 #include "messageviewer/viewer.h"
86 #include "messageviewer/attachmentstrategy.h"
87 #include "messageviewer/headerstyleplugin.h"
88 #include "messageviewer/headerstyle.h"
89 #ifndef QT_NO_CURSOR
90 #include "Libkdepim/KCursorSaver"
91 #endif
93 #include "MessageComposer/MessageSender"
94 #include "MessageComposer/MessageHelper"
96 #include "TemplateParser/TemplateParser"
98 #include "MessageCore/MessageCoreSettings"
99 #include "MessageCore/MailingList"
100 #include "messagecore/messagehelpers.h"
102 #include "dialog/kmknotify.h"
103 #include "widgets/displaymessageformatactionmenu.h"
105 #include "ksieveui/vacationmanager.h"
106 #include "kmlaunchexternalcomponent.h"
108 // LIBKDEPIM includes
109 #include "libkdepim/progressmanager.h"
110 #include "libkdepim/broadcaststatus.h"
112 // KDEPIMLIBS includes
113 #include <AkonadiCore/AgentManager>
114 #include <AkonadiCore/AttributeFactory>
115 #include <AkonadiCore/itemfetchjob.h>
116 #include <AkonadiCore/collectionattributessynchronizationjob.h>
117 #include <AkonadiCore/collectionfetchjob.h>
118 #include <AkonadiCore/collectionfetchscope.h>
119 #include <Akonadi/Contact/ContactSearchJob>
120 #include <AkonadiWidgets/collectionpropertiesdialog.h>
121 #include <AkonadiCore/entitydisplayattribute.h>
122 #include <AkonadiWidgets/entitylistview.h>
123 #include <AkonadiWidgets/etmviewstatesaver.h>
124 #include <AkonadiCore/agentinstance.h>
125 #include <AkonadiCore/agenttype.h>
126 #include <AkonadiCore/changerecorder.h>
127 #include <AkonadiCore/session.h>
128 #include <AkonadiCore/entitytreemodel.h>
129 #include <AkonadiCore/favoritecollectionsmodel.h>
130 #include <AkonadiCore/itemfetchscope.h>
131 #include <AkonadiCore/itemmodifyjob.h>
132 #include <AkonadiWidgets/controlgui.h>
133 #include <AkonadiWidgets/collectiondialog.h>
134 #include <AkonadiCore/collectionstatistics.h>
135 #include <AkonadiCore/EntityMimeTypeFilterModel>
136 #include <Akonadi/KMime/MessageFlags>
137 #include <AkonadiCore/CachePolicy>
139 #include <kidentitymanagement/identity.h>
140 #include <kidentitymanagement/identitymanager.h>
141 #include <KEmailAddress>
142 #include <mailtransport/transportmanager.h>
143 #include <mailtransport/transport.h>
144 #include <kmime/kmime_mdn.h>
145 #include <kmime/kmime_header_parsing.h>
146 #include <kmime/kmime_message.h>
147 #include <ksieveui/managesievescriptsdialog.h>
148 #include <ksieveui/util.h>
150 // KDELIBS includes
151 #include <kwindowsystem.h>
152 #include <kmessagebox.h>
153 #include <kactionmenu.h>
154 #include <QMenu>
155 #include <kacceleratormanager.h>
156 #include <kstandardshortcut.h>
157 #include <kcharsets.h>
158 #include "kmail_debug.h"
159 #include <ktip.h>
161 #include <kstandardaction.h>
162 #include <ktoggleaction.h>
163 #include <knotification.h>
164 #include <knotifyconfigwidget.h>
165 #include <kstringhandler.h>
166 #include <kconfiggroup.h>
167 #include <ktoolinvocation.h>
168 #include <kxmlguifactory.h>
169 #include <kxmlguiclient.h>
170 #include <QStatusBar>
171 #include <QAction>
172 #include <ktreewidgetsearchline.h>
173 #include <KRecentFilesAction>
175 // Qt includes
176 #include <QByteArray>
177 #include <QHeaderView>
178 #include <QList>
179 #include <QSplitter>
180 #include <QVBoxLayout>
181 #include <QShortcut>
182 #include <QProcess>
183 #include <QDBusConnection>
184 #include <QTextDocument>
185 #include <QDir>
186 // System includes
187 #include <AkonadiWidgets/standardactionmanager.h>
188 #include <KHelpClient>
189 #include <QStandardPaths>
191 #include "PimCommon/ManageServerSideSubscriptionJob"
192 #include <job/removeduplicatemailjob.h>
193 #include <job/removecollectionjob.h>
195 using namespace KMime;
196 using namespace Akonadi;
197 using namespace MailCommon;
198 using KPIM::ProgressManager;
199 using KPIM::BroadcastStatus;
200 using KMail::SearchWindow;
201 using KMime::Types::AddrSpecList;
202 using MessageViewer::AttachmentStrategy;
204 Q_GLOBAL_STATIC(KMMainWidget::PtrList, theMainWidgetList)
206 //-----------------------------------------------------------------------------
207 KMMainWidget::KMMainWidget(QWidget *parent, KXMLGUIClient *aGUIClient,
208 KActionCollection *actionCollection, KSharedConfig::Ptr config) :
209 QWidget(parent),
210 mMoveMsgToFolderAction(Q_NULLPTR),
211 mCollectionProperties(Q_NULLPTR),
212 mFavoriteCollectionsView(Q_NULLPTR),
213 mMsgView(Q_NULLPTR),
214 mSplitter1(Q_NULLPTR),
215 mSplitter2(Q_NULLPTR),
216 mFolderViewSplitter(Q_NULLPTR),
217 mArchiveFolderAction(Q_NULLPTR),
218 mShowBusySplashTimer(Q_NULLPTR),
219 mMsgActions(Q_NULLPTR),
220 mCurrentFolder(Q_NULLPTR),
221 mVacationIndicatorActive(false),
222 mGoToFirstUnreadMessageInSelectedFolder(false),
223 mDisplayMessageFormatMenu(Q_NULLPTR),
224 mFolderDisplayFormatPreference(MessageViewer::Viewer::UseGlobalSetting),
225 mSearchMessages(Q_NULLPTR),
226 mManageShowCollectionProperties(new ManageShowCollectionProperties(this, this)),
227 mShowIntroductionAction(Q_NULLPTR),
228 mMarkAllMessageAsReadAndInAllSubFolder(Q_NULLPTR),
229 mAccountActionMenu(Q_NULLPTR),
230 mRemoveDuplicateRecursiveAction(Q_NULLPTR)
232 mLaunchExternalComponent = new KMLaunchExternalComponent(this, this);
233 // must be the first line of the constructor:
234 mStartupDone = false;
235 mWasEverShown = false;
236 mReaderWindowActive = true;
237 mReaderWindowBelow = true;
238 mFolderHtmlLoadExtPreference = false;
239 mDestructed = false;
240 mActionCollection = actionCollection;
241 mTopLayout = new QVBoxLayout(this);
242 mTopLayout->setMargin(0);
243 mConfig = config;
244 mGUIClient = aGUIClient;
245 mFolderTreeWidget = Q_NULLPTR;
246 mPreferHtmlLoadExtAction = Q_NULLPTR;
247 Akonadi::ControlGui::widgetNeedsAkonadi(this);
248 mFavoritesModel = Q_NULLPTR;
249 mVacationManager = new KSieveUi::VacationManager(this);
251 mToolbarActionSeparator = new QAction(this);
252 mToolbarActionSeparator->setSeparator(true);
254 mPluginInterface = new KMailPluginInterface(mActionCollection, this);
255 mPluginInterface->setMainWidget(this);
257 theMainWidgetList->append(this);
259 readPreConfig();
260 createWidgets();
261 setupActions();
263 readConfig();
265 if (!kmkernel->isOffline()) { //kmail is set to online mode, make sure the agents are also online
266 kmkernel->setAccountStatus(true);
269 QTimer::singleShot(0, this, &KMMainWidget::slotShowStartupFolder);
271 connect(kmkernel, &KMKernel::startCheckMail,
272 this, &KMMainWidget::slotStartCheckMail);
274 connect(kmkernel, &KMKernel::endCheckMail,
275 this, &KMMainWidget::slotEndCheckMail);
277 connect(kmkernel, &KMKernel::configChanged,
278 this, &KMMainWidget::slotConfigChanged);
280 connect(kmkernel, &KMKernel::onlineStatusChanged,
281 this, &KMMainWidget::slotUpdateOnlineStatus);
283 connect(mTagActionManager, &KMail::TagActionManager::tagActionTriggered,
284 this, &KMMainWidget::slotUpdateMessageTagList);
286 connect(mTagActionManager, &KMail::TagActionManager::tagMoreActionClicked,
287 this, &KMMainWidget::slotSelectMoreMessageTagList);
289 kmkernel->toggleSystemTray();
292 // make sure the pages are registered only once, since there can be multiple instances of KMMainWidget
293 static bool pagesRegistered = false;
295 if (!pagesRegistered) {
296 Akonadi::CollectionPropertiesDialog::registerPage(new PimCommon::CollectionAclPageFactory);
297 Akonadi::CollectionPropertiesDialog::registerPage(new MailCommon::CollectionGeneralPageFactory);
298 Akonadi::CollectionPropertiesDialog::registerPage(new CollectionMaintenancePageFactory);
299 Akonadi::CollectionPropertiesDialog::registerPage(new CollectionQuotaPageFactory);
300 Akonadi::CollectionPropertiesDialog::registerPage(new CollectionTemplatesPageFactory);
301 Akonadi::CollectionPropertiesDialog::registerPage(new MailCommon::CollectionExpiryPageFactory);
302 Akonadi::CollectionPropertiesDialog::registerPage(new CollectionViewPageFactory);
303 Akonadi::CollectionPropertiesDialog::registerPage(new CollectionMailingListPageFactory);
304 Akonadi::CollectionPropertiesDialog::registerPage(new CollectionShortcutPageFactory);
306 pagesRegistered = true;
310 KMainWindow *mainWin = dynamic_cast<KMainWindow *>(window());
311 QStatusBar *sb = mainWin ? mainWin->statusBar() : Q_NULLPTR;
312 mVacationScriptIndicator = new KMail::VacationScriptIndicatorWidget(sb);
313 mVacationScriptIndicator->hide();
314 connect(mVacationScriptIndicator, &KMail::VacationScriptIndicatorWidget::clicked, this, &KMMainWidget::slotEditVacation);
315 if (KSieveUi::Util::checkOutOfOfficeOnStartup()) {
316 QTimer::singleShot(0, this, &KMMainWidget::slotCheckVacation);
319 connect(mFolderTreeWidget->folderTreeView()->model(), &QAbstractItemModel::modelReset,
320 this, &KMMainWidget::restoreCollectionFolderViewConfig);
321 restoreCollectionFolderViewConfig();
323 if (kmkernel->firstStart()) {
324 if (MailCommon::Util::foundMailer()) {
325 if (KMessageBox::questionYesNo(this, i18n("Another mailer was found on system. Do you want to import data from it?")) == KMessageBox::Yes) {
326 const QString path = QStandardPaths::findExecutable(QStringLiteral("importwizard"));
327 if (!QProcess::startDetached(path)) {
328 KMessageBox::error(this, i18n("Could not start the import wizard. "
329 "Please check your installation."),
330 i18n("Unable to start import wizard"));
332 } else {
333 mLaunchExternalComponent->slotAccountWizard();
335 } else {
336 mLaunchExternalComponent->slotAccountWizard();
339 // must be the last line of the constructor:
340 mStartupDone = true;
342 mCheckMailTimer.setInterval(3 * 1000);
343 mCheckMailTimer.setSingleShot(true);
344 connect(&mCheckMailTimer, &QTimer::timeout, this, &KMMainWidget::slotUpdateActionsAfterMailChecking);
348 void KMMainWidget::restoreCollectionFolderViewConfig()
350 ETMViewStateSaver *saver = new ETMViewStateSaver;
351 saver->setView(mFolderTreeWidget->folderTreeView());
352 const KConfigGroup cfg(KMKernel::self()->config(), "CollectionFolderView");
353 mFolderTreeWidget->restoreHeaderState(cfg.readEntry("HeaderState", QByteArray()));
354 saver->restoreState(cfg);
355 //Restore startup folder
357 Akonadi::Collection::Id id = -1;
358 if (mCurrentFolder && mCurrentFolder->collection().isValid()) {
359 id = mCurrentFolder->collection().id();
362 if (id == -1) {
363 if (KMailSettings::self()->startSpecificFolderAtStartup()) {
364 Akonadi::Collection::Id startupFolder = KMailSettings::self()->startupFolder();
365 if (startupFolder > 0) {
366 saver->restoreCurrentItem(QStringLiteral("c%1").arg(startupFolder));
369 } else {
370 saver->restoreCurrentItem(QStringLiteral("c%1").arg(id));
374 //-----------------------------------------------------------------------------
375 //The kernel may have already been deleted when this method is called,
376 //perform all cleanup that requires the kernel in destruct()
377 KMMainWidget::~KMMainWidget()
379 theMainWidgetList->removeAll(this);
380 qDeleteAll(mFilterCommands);
381 destruct();
384 //-----------------------------------------------------------------------------
385 //This method performs all cleanup that requires the kernel to exist.
386 void KMMainWidget::destruct()
388 if (mDestructed) {
389 return;
391 if (mSearchWin) {
392 mSearchWin->close();
394 disconnect(mFolderTreeWidget->folderTreeView()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KMMainWidget::updateFolderMenu);
395 writeConfig(false); /* don't force kmkernel sync when close BUG: 289287 */
396 writeFolderConfig();
397 deleteWidgets();
398 mCurrentFolder.clear();
399 delete mMoveOrCopyToDialog;
400 delete mSelectFromAllFoldersDialog;
402 disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)), this, Q_NULLPTR);
403 disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemRemoved(Akonadi::Item)), this, Q_NULLPTR);
404 disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)), this, Q_NULLPTR);
405 disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>)), this, Q_NULLPTR);
406 disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), this, Q_NULLPTR);
408 mDestructed = true;
411 void KMMainWidget::slotStartCheckMail()
413 if (mCheckMailTimer.isActive()) {
414 mCheckMailTimer.stop();
418 void KMMainWidget::slotEndCheckMail()
420 if (!mCheckMailTimer.isActive()) {
421 mCheckMailTimer.start();
425 void KMMainWidget::slotUpdateActionsAfterMailChecking()
427 const bool sendOnAll =
428 KMailSettings::self()->sendOnCheck() == KMailSettings::EnumSendOnCheck::SendOnAllChecks;
429 const bool sendOnManual =
430 KMailSettings::self()->sendOnCheck() == KMailSettings::EnumSendOnCheck::SendOnManualChecks;
431 if (!kmkernel->isOffline() && (sendOnAll || sendOnManual)) {
432 slotSendQueued();
434 // update folder menus in case some mail got filtered to trash/current folder
435 // and we can enable "empty trash/move all to trash" action etc.
436 updateFolderMenu();
439 void KMMainWidget::slotCollectionFetched(int collectionId)
441 // Called when a collection is fetched for the first time by the ETM.
442 // This is the right time to update the caption (which still says "Loading...")
443 // and to update the actions that depend on the number of mails in the folder.
444 if (mCurrentFolder && collectionId == mCurrentFolder->collection().id()) {
445 mCurrentFolder->setCollection(MailCommon::Util::updatedCollection(mCurrentFolder->collection()));
446 updateMessageActions();
447 updateFolderMenu();
449 // We call this for any collection, it could be one of our parents...
450 if (mCurrentFolder) {
451 Q_EMIT captionChangeRequest(MailCommon::Util::fullCollectionPath(mCurrentFolder->collection()));
455 void KMMainWidget::slotFolderChanged(const Akonadi::Collection &collection)
457 folderSelected(collection);
458 if (collection.cachePolicy().syncOnDemand()) {
459 AgentManager::self()->synchronizeCollection(collection, false);
461 mMsgActions->setCurrentMessage(Akonadi::Item());
462 Q_EMIT captionChangeRequest(MailCommon::Util::fullCollectionPath(collection));
465 void KMMainWidget::folderSelected(const Akonadi::Collection &col)
467 // This is connected to the MainFolderView signal triggering when a folder is selected
469 if (mGoToFirstUnreadMessageInSelectedFolder) {
470 // the default action has been overridden from outside
471 mPreSelectionMode = MessageList::Core::PreSelectFirstUnreadCentered;
472 } else {
473 // use the default action
474 switch (KMailSettings::self()->actionEnterFolder()) {
475 case KMailSettings::EnumActionEnterFolder::SelectFirstUnread:
476 mPreSelectionMode = MessageList::Core::PreSelectFirstUnreadCentered;
477 break;
478 case KMailSettings::EnumActionEnterFolder::SelectLastSelected:
479 mPreSelectionMode = MessageList::Core::PreSelectLastSelected;
480 break;
481 case KMailSettings::EnumActionEnterFolder::SelectNewest:
482 mPreSelectionMode = MessageList::Core::PreSelectNewestCentered;
483 break;
484 case KMailSettings::EnumActionEnterFolder::SelectOldest:
485 mPreSelectionMode = MessageList::Core::PreSelectOldestCentered;
486 break;
487 default:
488 mPreSelectionMode = MessageList::Core::PreSelectNone;
489 break;
493 mGoToFirstUnreadMessageInSelectedFolder = false;
494 #ifndef QT_NO_CURSOR
495 KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy());
496 #endif
498 if (mMsgView) {
499 mMsgView->clear(true);
501 const bool newFolder = mCurrentFolder && (mCurrentFolder->collection() != col);
503 // Delete any pending timer, if needed it will be recreated below
504 delete mShowBusySplashTimer;
505 mShowBusySplashTimer = Q_NULLPTR;
506 if (newFolder) {
507 // We're changing folder: write configuration for the old one
508 writeFolderConfig();
511 mCurrentFolder = FolderCollection::forCollection(col);
513 readFolderConfig();
514 if (mMsgView) {
515 mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference);
516 mMsgView->setHtmlLoadExtOverride(mFolderHtmlLoadExtPreference);
519 if (!mCurrentFolder->isValid() && (mMessagePane->count() < 2)) {
520 slotIntro();
523 updateMessageActions();
524 updateFolderMenu();
526 // The message pane uses the selection model of the folder view to load the correct aggregation model and theme
527 // settings. At this point the selection model hasn't been updated yet to the user's new choice, so it would load
528 // the old folder settings instead.
529 QTimer::singleShot(0, this, &KMMainWidget::slotShowSelectedFolderInPane);
532 void KMMainWidget::slotShowSelectedFolderInPane()
534 if (mCurrentFolder && mCurrentFolder->collection().isValid()) {
535 mMessagePane->setCurrentFolder(mCurrentFolder->collection(), false, mPreSelectionMode);
539 void KMMainWidget::clearViewer()
541 if (mMsgView) {
542 mMsgView->clear(true);
543 mMsgView->displayAboutPage();
547 //-----------------------------------------------------------------------------
548 void KMMainWidget::readPreConfig()
550 mLongFolderList = KMailSettings::self()->folderList() == KMailSettings::EnumFolderList::longlist;
551 mReaderWindowActive = KMailSettings::self()->readerWindowMode() != KMailSettings::EnumReaderWindowMode::hide;
552 mReaderWindowBelow = KMailSettings::self()->readerWindowMode() == KMailSettings::EnumReaderWindowMode::below;
554 mHtmlGlobalSetting = MessageViewer::MessageViewerSettings::self()->htmlMail();
555 mHtmlLoadExtGlobalSetting = MessageViewer::MessageViewerSettings::self()->htmlLoadExternal();
557 mEnableFavoriteFolderView = (MailCommon::MailCommonSettings::self()->favoriteCollectionViewMode() != MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::HiddenMode);
558 readFolderConfig();
559 updateHtmlMenuEntry();
560 if (mMsgView) {
561 mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference);
562 mMsgView->update(true);
566 //-----------------------------------------------------------------------------
567 void KMMainWidget::readFolderConfig()
569 if (!mCurrentFolder || !mCurrentFolder->isValid()) {
570 return;
572 KSharedConfig::Ptr config = KMKernel::self()->config();
573 KConfigGroup group(config, MailCommon::FolderCollection::configGroupName(mCurrentFolder->collection()));
574 if (group.hasKey("htmlMailOverride")) {
575 const bool useHtml = group.readEntry("htmlMailOverride", false);
576 mFolderDisplayFormatPreference = useHtml ? MessageViewer::Viewer::Html : MessageViewer::Viewer::Text;
577 group.deleteEntry("htmlMailOverride");
578 group.sync();
579 } else {
580 mFolderDisplayFormatPreference = static_cast<MessageViewer::Viewer::DisplayFormatMessage>(group.readEntry("displayFormatOverride", static_cast<int>(MessageViewer::Viewer::UseGlobalSetting)));
582 mFolderHtmlLoadExtPreference =
583 group.readEntry("htmlLoadExternalOverride", false);
586 //-----------------------------------------------------------------------------
587 void KMMainWidget::writeFolderConfig()
589 if (mCurrentFolder && mCurrentFolder->isValid()) {
590 KSharedConfig::Ptr config = KMKernel::self()->config();
591 KConfigGroup group(config, MailCommon::FolderCollection::configGroupName(mCurrentFolder->collection()));
592 group.writeEntry("htmlLoadExternalOverride", mFolderHtmlLoadExtPreference);
593 if (mFolderDisplayFormatPreference == MessageViewer::Viewer::UseGlobalSetting) {
594 group.deleteEntry("displayFormatOverride");
595 } else {
596 group.writeEntry("displayFormatOverride", static_cast<int>(mFolderDisplayFormatPreference));
601 //-----------------------------------------------------------------------------
602 void KMMainWidget::layoutSplitters()
604 // This function can only be called when the old splitters are already deleted
605 Q_ASSERT(!mSplitter1);
606 Q_ASSERT(!mSplitter2);
608 // For some reason, this is necessary here so that the copy action still
609 // works after changing the folder layout.
610 if (mMsgView)
611 disconnect(mMsgView->copyAction(), &QAction::triggered,
612 mMsgView, &KMReaderWin::slotCopySelectedText);
614 // If long folder list is enabled, the splitters are:
615 // Splitter 1: FolderView vs (HeaderAndSearch vs MessageViewer)
616 // Splitter 2: HeaderAndSearch vs MessageViewer
618 // If long folder list is disabled, the splitters are:
619 // Splitter 1: (FolderView vs HeaderAndSearch) vs MessageViewer
620 // Splitter 2: FolderView vs HeaderAndSearch
622 // The folder view is both the folder tree and the favorite folder view, if
623 // enabled
625 const bool readerWindowAtSide = !mReaderWindowBelow && mReaderWindowActive;
626 const bool readerWindowBelow = mReaderWindowBelow && mReaderWindowActive;
628 mSplitter1 = new QSplitter(this);
629 mSplitter2 = new QSplitter(mSplitter1);
631 QWidget *folderTreeWidget = mSearchAndTree;
632 if (mFavoriteCollectionsView) {
633 mFolderViewSplitter = new QSplitter(Qt::Vertical);
634 mFolderViewSplitter->setChildrenCollapsible(false);
635 mFolderViewSplitter->addWidget(mFavoriteCollectionsView);
636 mFavoriteCollectionsView->setParent(mFolderViewSplitter);
637 mFolderViewSplitter->addWidget(mSearchAndTree);
638 folderTreeWidget = mFolderViewSplitter;
641 if (mLongFolderList) {
643 // add folder tree
644 mSplitter1->setOrientation(Qt::Horizontal);
645 mSplitter1->addWidget(folderTreeWidget);
647 // and the rest to the right
648 mSplitter1->addWidget(mSplitter2);
650 // add the message list to the right or below
651 if (readerWindowAtSide) {
652 mSplitter2->setOrientation(Qt::Horizontal);
653 } else {
654 mSplitter2->setOrientation(Qt::Vertical);
656 mSplitter2->addWidget(mMessagePane);
658 // add the preview window, if there is one
659 if (mMsgView) {
660 mSplitter2->addWidget(mMsgView);
663 } else { // short folder list
664 if (mReaderWindowBelow) {
665 mSplitter1->setOrientation(Qt::Vertical);
666 mSplitter2->setOrientation(Qt::Horizontal);
667 } else { // at side or none
668 mSplitter1->setOrientation(Qt::Horizontal);
669 mSplitter2->setOrientation(Qt::Vertical);
672 mSplitter1->addWidget(mSplitter2);
674 // add folder tree
675 mSplitter2->addWidget(folderTreeWidget);
676 // add message list to splitter 2
677 mSplitter2->addWidget(mMessagePane);
679 // add the preview window, if there is one
680 if (mMsgView) {
681 mSplitter1->addWidget(mMsgView);
686 // Set splitter properties
688 mSplitter1->setObjectName(QStringLiteral("splitter1"));
689 mSplitter1->setChildrenCollapsible(false);
690 mSplitter2->setObjectName(QStringLiteral("splitter2"));
691 mSplitter2->setChildrenCollapsible(false);
694 // Set the stretch factors
696 mSplitter1->setStretchFactor(0, 0);
697 mSplitter2->setStretchFactor(0, 0);
698 mSplitter1->setStretchFactor(1, 1);
699 mSplitter2->setStretchFactor(1, 1);
701 if (mFavoriteCollectionsView) {
702 mFolderViewSplitter->setStretchFactor(0, 0);
703 mFolderViewSplitter->setStretchFactor(1, 1);
706 // Because the reader windows's width increases a tiny bit after each
707 // restart in short folder list mode with message window at side, disable
708 // the stretching as a workaround here
709 if (readerWindowAtSide && !mLongFolderList) {
710 mSplitter1->setStretchFactor(0, 1);
711 mSplitter1->setStretchFactor(1, 0);
715 // Set the sizes of the splitters to the values stored in the config
717 QList<int> splitter1Sizes;
718 QList<int> splitter2Sizes;
720 const int folderViewWidth = KMailSettings::self()->folderViewWidth();
721 int ftHeight = KMailSettings::self()->folderTreeHeight();
722 int headerHeight = KMailSettings::self()->searchAndHeaderHeight();
723 const int messageViewerWidth = KMailSettings::self()->readerWindowWidth();
724 int headerWidth = KMailSettings::self()->searchAndHeaderWidth();
725 int messageViewerHeight = KMailSettings::self()->readerWindowHeight();
727 int ffvHeight = mFolderViewSplitter ? MailCommon::MailCommonSettings::self()->favoriteCollectionViewHeight() : 0;
729 // If the message viewer was hidden before, make sure it is not zero height
730 if (messageViewerHeight < 10 && readerWindowBelow) {
731 headerHeight /= 2;
732 messageViewerHeight = headerHeight;
735 if (mLongFolderList) {
736 if (!readerWindowAtSide) {
737 splitter1Sizes << folderViewWidth << headerWidth;
738 splitter2Sizes << headerHeight << messageViewerHeight;
739 } else {
740 splitter1Sizes << folderViewWidth << (headerWidth + messageViewerWidth);
741 splitter2Sizes << headerWidth << messageViewerWidth;
743 } else {
744 if (!readerWindowAtSide) {
745 splitter1Sizes << headerHeight << messageViewerHeight;
746 splitter2Sizes << folderViewWidth << headerWidth;
747 } else {
748 splitter1Sizes << headerWidth << messageViewerWidth;
749 splitter2Sizes << ftHeight + ffvHeight << messageViewerHeight;
753 mSplitter1->setSizes(splitter1Sizes);
754 mSplitter2->setSizes(splitter2Sizes);
756 if (mFolderViewSplitter) {
757 QList<int> splitterSizes;
758 splitterSizes << ffvHeight << ftHeight;
759 mFolderViewSplitter->setSizes(splitterSizes);
763 // Now add the splitters to the main layout
765 mTopLayout->addWidget(mSplitter1);
767 // Make sure the focus is on the view, and not on the quick search line edit, because otherwise
768 // shortcuts like + or j go to the wrong place.
769 // This would normally be done in the message list itself, but apparently something resets the focus
770 // again, probably all the reparenting we do here.
771 mMessagePane->focusView();
773 // By default hide th unread and size columns on first run.
774 if (kmkernel->firstStart()) {
775 mFolderTreeWidget->folderTreeView()->hideColumn(1);
776 mFolderTreeWidget->folderTreeView()->hideColumn(3);
777 mFolderTreeWidget->folderTreeView()->header()->resizeSection(0, folderViewWidth * 0.8);
780 // Make the copy action work, see disconnect comment above
781 if (mMsgView)
782 connect(mMsgView->copyAction(), &QAction::triggered,
783 mMsgView, &KMReaderWin::slotCopySelectedText);
786 //-----------------------------------------------------------------------------
787 void KMMainWidget::refreshFavoriteFoldersViewProperties()
789 if (mFavoriteCollectionsView) {
790 if (MailCommon::MailCommonSettings::self()->favoriteCollectionViewMode() == MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::IconMode) {
791 mFavoriteCollectionsView->changeViewMode(QListView::IconMode);
792 } else if (MailCommon::MailCommonSettings::self()->favoriteCollectionViewMode() == MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::ListMode) {
793 mFavoriteCollectionsView->changeViewMode(QListView::ListMode);
794 } else {
795 Q_ASSERT(false); // we should never get here in hidden mode
797 mFavoriteCollectionsView->setDropActionMenuEnabled(kmkernel->showPopupAfterDnD());
798 mFavoriteCollectionsView->setWordWrap(true);
799 mFavoriteCollectionsView->updateMode();
803 //-----------------------------------------------------------------------------
804 void KMMainWidget::readConfig()
806 const bool oldLongFolderList = mLongFolderList;
807 const bool oldReaderWindowActive = mReaderWindowActive;
808 const bool oldReaderWindowBelow = mReaderWindowBelow;
809 const bool oldFavoriteFolderView = mEnableFavoriteFolderView;
811 // on startup, the layout is always new and we need to relayout the widgets
812 bool layoutChanged = !mStartupDone;
814 if (mStartupDone) {
815 readPreConfig();
817 layoutChanged = (oldLongFolderList != mLongFolderList) ||
818 (oldReaderWindowActive != mReaderWindowActive) ||
819 (oldReaderWindowBelow != mReaderWindowBelow) ||
820 (oldFavoriteFolderView != mEnableFavoriteFolderView);
822 if (layoutChanged) {
823 deleteWidgets();
824 createWidgets();
825 restoreCollectionFolderViewConfig();
826 Q_EMIT recreateGui();
831 // Read the config of the folder views and the header
832 if (mMsgView) {
833 mMsgView->readConfig();
835 mMessagePane->reloadGlobalConfiguration();
836 mFolderTreeWidget->readConfig();
837 if (mFavoriteCollectionsView) {
838 mFavoriteCollectionsView->readConfig();
840 refreshFavoriteFoldersViewProperties();
844 // area for config group "General"
845 if (!mStartupDone) {
846 // check mail on startup
847 // do it after building the kmmainwin, so that the progressdialog is available
848 QTimer::singleShot(0, this, &KMMainWidget::slotCheckMailOnStartup);
852 if (layoutChanged) {
853 layoutSplitters();
856 updateMessageMenu();
857 updateFileMenu();
858 kmkernel->toggleSystemTray();
859 mAccountActionMenu->setAccountOrder(MailCommon::MailCommonSettings::self()->order());
861 connect(Akonadi::AgentManager::self(), &AgentManager::instanceAdded,
862 this, &KMMainWidget::updateFileMenu);
863 connect(Akonadi::AgentManager::self(), &AgentManager::instanceRemoved,
864 this, &KMMainWidget::updateFileMenu);
867 //-----------------------------------------------------------------------------
868 void KMMainWidget::writeConfig(bool force)
870 // Don't save the sizes of all the widgets when we were never shown.
871 // This can happen in Kontact, where the KMail plugin is automatically
872 // loaded, but not necessarily shown.
873 // This prevents invalid sizes from being saved
874 if (mWasEverShown) {
875 // The height of the header widget can be 0, this happens when the user
876 // did not switch to the header widget onced and the "Welcome to KMail"
877 // HTML widget was shown the whole time
878 int headersHeight = mMessagePane->height();
879 if (headersHeight == 0) {
880 headersHeight = height() / 2;
883 KMailSettings::self()->setSearchAndHeaderHeight(headersHeight);
884 KMailSettings::self()->setSearchAndHeaderWidth(mMessagePane->width());
885 if (mFavoriteCollectionsView) {
886 MailCommon::MailCommonSettings::self()->setFavoriteCollectionViewHeight(mFavoriteCollectionsView->height());
887 KMailSettings::self()->setFolderTreeHeight(mFolderTreeWidget->height());
888 if (!mLongFolderList) {
889 KMailSettings::self()->setFolderViewHeight(mFolderViewSplitter->height());
891 } else if (!mLongFolderList && mFolderTreeWidget) {
892 KMailSettings::self()->setFolderTreeHeight(mFolderTreeWidget->height());
894 if (mFolderTreeWidget) {
895 KMailSettings::self()->setFolderViewWidth(mFolderTreeWidget->width());
896 KSharedConfig::Ptr config = KMKernel::self()->config();
897 KConfigGroup group(config, "CollectionFolderView");
899 ETMViewStateSaver saver;
900 saver.setView(mFolderTreeWidget->folderTreeView());
901 saver.saveState(group);
903 group.writeEntry("HeaderState", mFolderTreeWidget->folderTreeView()->header()->saveState());
904 //Work around from startup folder
905 group.deleteEntry("Selection");
906 #if 0
907 if (!KMailSettings::self()->startSpecificFolderAtStartup()) {
908 group.deleteEntry("Current");
910 #endif
911 group.sync();
914 if (mMsgView) {
915 if (!mReaderWindowBelow) {
916 KMailSettings::self()->setReaderWindowWidth(mMsgView->width());
918 mMsgView->viewer()->writeConfig(force);
919 KMailSettings::self()->setReaderWindowHeight(mMsgView->height());
924 void KMMainWidget::writeReaderConfig()
926 if (mWasEverShown) {
927 if (mMsgView) {
928 mMsgView->viewer()->writeConfig();
933 KMReaderWin *KMMainWidget::messageView() const
935 return mMsgView;
938 CollectionPane *KMMainWidget::messageListPane() const
940 return mMessagePane;
943 //-----------------------------------------------------------------------------
944 void KMMainWidget::deleteWidgets()
946 // Simply delete the top splitter, which always is mSplitter1, regardless
947 // of the layout. This deletes all children.
948 // akonadi action manager is created in createWidgets(), parented to this
949 // so not autocleaned up.
950 delete mAkonadiStandardActionManager;
951 mAkonadiStandardActionManager = Q_NULLPTR;
952 delete mSplitter1;
953 mMsgView = Q_NULLPTR;
954 mSearchAndTree = Q_NULLPTR;
955 mFolderViewSplitter = Q_NULLPTR;
956 mFavoriteCollectionsView = Q_NULLPTR;
957 mSplitter1 = Q_NULLPTR;
958 mSplitter2 = Q_NULLPTR;
959 mFavoritesModel = Q_NULLPTR;
962 //-----------------------------------------------------------------------------
963 void KMMainWidget::createWidgets()
965 // Note that all widgets we create in this function have the parent 'this'.
966 // They will be properly reparented in layoutSplitters()
969 // Create header view and search bar
971 FolderTreeWidget::TreeViewOptions opt = FolderTreeWidget::ShowUnreadCount;
972 opt |= FolderTreeWidget::UseLineEditForFiltering;
973 opt |= FolderTreeWidget::ShowCollectionStatisticAnimation;
974 opt |= FolderTreeWidget::DontKeyFilter;
975 mFolderTreeWidget = new FolderTreeWidget(this, mGUIClient, opt);
977 connect(mFolderTreeWidget->folderTreeView(), SIGNAL(currentChanged(Akonadi::Collection)), this, SLOT(slotFolderChanged(Akonadi::Collection)));
979 connect(mFolderTreeWidget->folderTreeView()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KMMainWidget::updateFolderMenu);
981 connect(mFolderTreeWidget->folderTreeView(), &FolderTreeView::prefereCreateNewTab, this, &KMMainWidget::slotCreateNewTab);
983 mFolderTreeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
984 mMessagePane = new CollectionPane(!KMailSettings::self()->startSpecificFolderAtStartup(), KMKernel::self()->entityTreeModel(),
985 mFolderTreeWidget->folderTreeView()->selectionModel(),
986 this);
987 connect(KMKernel::self()->entityTreeModel(), &Akonadi::EntityTreeModel::collectionFetched, this, &KMMainWidget::slotCollectionFetched);
989 mMessagePane->setXmlGuiClient(mGUIClient);
990 connect(mMessagePane, &MessageList::Pane::messageSelected,
991 this, &KMMainWidget::slotMessageSelected);
992 connect(mMessagePane, &MessageList::Pane::selectionChanged,
993 this, &KMMainWidget::startUpdateMessageActionsTimer);
994 connect(mMessagePane, &CollectionPane::currentTabChanged, this, &KMMainWidget::refreshMessageListSelection);
995 connect(mMessagePane, &MessageList::Pane::messageActivated,
996 this, &KMMainWidget::slotMessageActivated);
997 connect(mMessagePane, &MessageList::Pane::messageStatusChangeRequest,
998 this, &KMMainWidget::slotMessageStatusChangeRequest);
1000 connect(mMessagePane, &MessageList::Pane::statusMessage,
1001 BroadcastStatus::instance(), &KPIM::BroadcastStatus::setStatusMsg);
1004 // Create the reader window
1006 if (mReaderWindowActive) {
1007 mMsgView = new KMReaderWin(this, this, actionCollection(), Q_NULLPTR);
1008 if (mMsgActions) {
1009 mMsgActions->setMessageView(mMsgView);
1011 connect(mMsgView->viewer(), &MessageViewer::Viewer::replaceMsgByUnencryptedVersion,
1012 this, &KMMainWidget::slotReplaceMsgByUnencryptedVersion);
1013 connect(mMsgView->viewer(), &MessageViewer::Viewer::popupMenu,
1014 this, &KMMainWidget::slotMessagePopup);
1015 connect(mMsgView->viewer(), &MessageViewer::Viewer::moveMessageToTrash,
1016 this, &KMMainWidget::slotMoveMessageToTrash);
1017 connect(mMsgView->viewer(), &MessageViewer::Viewer::executeMailAction,
1018 this, &KMMainWidget::slotExecuteMailAction);
1019 if (mShowIntroductionAction) {
1020 mShowIntroductionAction->setEnabled(true);
1022 } else {
1023 if (mMsgActions) {
1024 mMsgActions->setMessageView(Q_NULLPTR);
1026 if (mShowIntroductionAction) {
1027 mShowIntroductionAction->setEnabled(false);
1032 // Create the folder tree
1033 // the "folder tree" consists of a quicksearch input field and the tree itself
1036 mSearchAndTree = new QWidget(this);
1037 QVBoxLayout *vboxlayout = new QVBoxLayout;
1038 vboxlayout->setMargin(0);
1039 mSearchAndTree->setLayout(vboxlayout);
1041 vboxlayout->addWidget(mFolderTreeWidget);
1042 mFolderTreeWidget->filterFolderLineEdit()->hide();
1044 // Create the favorite folder view
1046 mAkonadiStandardActionManager = new Akonadi::StandardMailActionManager(mGUIClient->actionCollection(), this);
1047 connect(mAkonadiStandardActionManager, &Akonadi::StandardMailActionManager::actionStateUpdated, this, &KMMainWidget::slotAkonadiStandardActionUpdated);
1049 mAkonadiStandardActionManager->setCollectionSelectionModel(mFolderTreeWidget->folderTreeView()->selectionModel());
1050 mAkonadiStandardActionManager->setItemSelectionModel(mMessagePane->currentItemSelectionModel());
1052 if (mEnableFavoriteFolderView) {
1054 mFavoriteCollectionsView = new FavoriteCollectionWidget(mGUIClient, this);
1055 refreshFavoriteFoldersViewProperties();
1056 connect(mFavoriteCollectionsView, SIGNAL(currentChanged(Akonadi::Collection)), this, SLOT(slotFolderChanged(Akonadi::Collection)));
1058 mFavoritesModel = new Akonadi::FavoriteCollectionsModel(
1059 mFolderTreeWidget->folderTreeView()->model(),
1060 KMKernel::self()->config()->group("FavoriteCollections"), this);
1062 mFavoriteCollectionsView->setModel(mFavoritesModel);
1064 mAkonadiStandardActionManager->setFavoriteCollectionsModel(mFavoritesModel);
1065 mAkonadiStandardActionManager->setFavoriteSelectionModel(mFavoriteCollectionsView->selectionModel());
1068 //Don't use mMailActionManager->createAllActions() to save memory by not
1069 //creating actions that doesn't make sense.
1070 QList<StandardActionManager::Type> standardActions;
1071 standardActions << StandardActionManager::CreateCollection
1072 << StandardActionManager::CopyCollections
1073 << StandardActionManager::DeleteCollections
1074 << StandardActionManager::SynchronizeCollections
1075 << StandardActionManager::CollectionProperties
1076 << StandardActionManager::CopyItems
1077 << StandardActionManager::Paste
1078 << StandardActionManager::DeleteItems
1079 << StandardActionManager::ManageLocalSubscriptions
1080 << StandardActionManager::CopyCollectionToMenu
1081 << StandardActionManager::CopyItemToMenu
1082 << StandardActionManager::MoveItemToMenu
1083 << StandardActionManager::MoveCollectionToMenu
1084 << StandardActionManager::CutItems
1085 << StandardActionManager::CutCollections
1086 << StandardActionManager::CreateResource
1087 << StandardActionManager::DeleteResources
1088 << StandardActionManager::ResourceProperties
1089 << StandardActionManager::SynchronizeResources
1090 << StandardActionManager::ToggleWorkOffline
1091 << StandardActionManager::SynchronizeCollectionsRecursive;
1093 Q_FOREACH (StandardActionManager::Type standardAction, standardActions) {
1094 mAkonadiStandardActionManager->createAction(standardAction);
1097 if (mEnableFavoriteFolderView) {
1098 QList<StandardActionManager::Type> favoriteActions;
1099 favoriteActions << StandardActionManager::AddToFavoriteCollections
1100 << StandardActionManager::RemoveFromFavoriteCollections
1101 << StandardActionManager::RenameFavoriteCollection
1102 << StandardActionManager::SynchronizeFavoriteCollections;
1103 Q_FOREACH (StandardActionManager::Type favoriteAction, favoriteActions) {
1104 mAkonadiStandardActionManager->createAction(favoriteAction);
1108 QList<StandardMailActionManager::Type> mailActions;
1109 mailActions << StandardMailActionManager::MarkAllMailAsRead
1110 << StandardMailActionManager::MoveToTrash
1111 << StandardMailActionManager::MoveAllToTrash
1112 << StandardMailActionManager::RemoveDuplicates
1113 << StandardMailActionManager::EmptyAllTrash
1114 << StandardMailActionManager::MarkMailAsRead
1115 << StandardMailActionManager::MarkMailAsUnread
1116 << StandardMailActionManager::MarkMailAsImportant
1117 << StandardMailActionManager::MarkMailAsActionItem;
1119 Q_FOREACH (StandardMailActionManager::Type mailAction, mailActions) {
1120 mAkonadiStandardActionManager->createAction(mailAction);
1123 mAkonadiStandardActionManager->interceptAction(Akonadi::StandardActionManager::CollectionProperties);
1124 connect(mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CollectionProperties), &QAction::triggered, mManageShowCollectionProperties, &ManageShowCollectionProperties::slotCollectionProperties);
1127 // Create all kinds of actions
1129 mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::RemoveDuplicates)->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Asterisk));
1130 mAkonadiStandardActionManager->interceptAction(Akonadi::StandardMailActionManager::RemoveDuplicates);
1131 connect(mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::RemoveDuplicates), &QAction::triggered, this, &KMMainWidget::slotRemoveDuplicates);
1134 mCollectionProperties = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CollectionProperties);
1136 connect(kmkernel->folderCollectionMonitor(), &Monitor::collectionRemoved,
1137 this, &KMMainWidget::slotCollectionRemoved);
1138 connect(kmkernel->folderCollectionMonitor(), &Monitor::itemAdded,
1139 this, &KMMainWidget::slotItemAdded);
1140 connect(kmkernel->folderCollectionMonitor(), &Monitor::itemRemoved,
1141 this, &KMMainWidget::slotItemRemoved);
1142 connect(kmkernel->folderCollectionMonitor(), &Monitor::itemMoved,
1143 this, &KMMainWidget::slotItemMoved);
1144 connect(kmkernel->folderCollectionMonitor(), SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>)), SLOT(slotCollectionChanged(Akonadi::Collection,QSet<QByteArray>)));
1146 connect(kmkernel->folderCollectionMonitor(), &Monitor::collectionStatisticsChanged, this, &KMMainWidget::slotCollectionStatisticsChanged);
1150 void KMMainWidget::updateMoveAction(const Akonadi::CollectionStatistics &statistic)
1152 const bool hasUnreadMails = (statistic.unreadCount() > 0);
1153 const bool hasMails = (statistic.count() > 0);
1154 updateMoveAction(hasUnreadMails, hasMails);
1157 void KMMainWidget::updateMoveAction(bool hasUnreadMails, bool hasMails)
1159 const bool enable_goto_unread = hasUnreadMails
1160 || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllFolders)
1161 || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllMarkedFolders);
1162 actionCollection()->action(QStringLiteral("go_next_message"))->setEnabled(hasMails);
1163 actionCollection()->action(QStringLiteral("go_next_unread_message"))->setEnabled(enable_goto_unread);
1164 actionCollection()->action(QStringLiteral("go_prev_message"))->setEnabled(hasMails);
1165 actionCollection()->action(QStringLiteral("go_prev_unread_message"))->setEnabled(enable_goto_unread);
1166 if (mAkonadiStandardActionManager && mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkAllMailAsRead)) {
1167 mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkAllMailAsRead)->setEnabled(hasUnreadMails);
1171 void KMMainWidget::updateAllToTrashAction(int statistics)
1173 bool multiFolder = false;
1174 if (mFolderTreeWidget) {
1175 multiFolder = mFolderTreeWidget->selectedCollections().count() > 1;
1177 if (mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)) {
1178 const bool folderWithContent = mCurrentFolder && !mCurrentFolder->isStructural();
1179 mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)->setEnabled(folderWithContent
1180 && (statistics > 0)
1181 && mCurrentFolder->canDeleteMessages()
1182 && !multiFolder);
1186 void KMMainWidget::slotCollectionStatisticsChanged(Akonadi::Collection::Id id, const Akonadi::CollectionStatistics &statistic)
1188 if (id == CommonKernel->outboxCollectionFolder().id()) {
1189 const bool enableAction = (statistic.count() > 0);
1190 mSendQueued->setEnabled(enableAction);
1191 mSendActionMenu->setEnabled(enableAction);
1192 } else if (mCurrentFolder && (id == mCurrentFolder->collection().id())) {
1193 updateMoveAction(statistic);
1194 updateAllToTrashAction(statistic.count());
1195 mCurrentFolder->setCollection(MailCommon::Util::updatedCollection(mCurrentFolder->collection()));
1199 void KMMainWidget::slotCreateNewTab(bool preferNewTab)
1201 mMessagePane->setPreferEmptyTab(preferNewTab);
1204 void KMMainWidget::slotCollectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &set)
1206 if (mCurrentFolder
1207 && (collection == mCurrentFolder->collection())
1208 && (set.contains("MESSAGEFOLDER") || set.contains("expirationcollectionattribute"))) {
1209 if (set.contains("MESSAGEFOLDER")) {
1210 mMessagePane->resetModelStorage();
1211 } else {
1212 mCurrentFolder->setCollection(collection);
1214 } else if (set.contains("ENTITYDISPLAY") || set.contains("NAME")) {
1215 const QModelIndex idx = Akonadi::EntityTreeModel::modelIndexForCollection(KMKernel::self()->collectionModel(), collection);
1216 if (idx.isValid()) {
1217 const QString text = idx.data().toString();
1218 const QIcon icon = idx.data(Qt::DecorationRole).value<QIcon>();
1219 mMessagePane->updateTabIconText(collection, text, icon);
1224 void KMMainWidget::slotItemAdded(const Akonadi::Item &msg, const Akonadi::Collection &col)
1226 Q_UNUSED(msg);
1227 if (col.isValid()) {
1228 if (col == CommonKernel->outboxCollectionFolder()) {
1229 startUpdateMessageActionsTimer();
1234 void KMMainWidget::slotItemRemoved(const Akonadi::Item &item)
1236 if (item.isValid() && item.parentCollection().isValid() && (item.parentCollection() == CommonKernel->outboxCollectionFolder())) {
1237 startUpdateMessageActionsTimer();
1241 void KMMainWidget::slotItemMoved(const Akonadi::Item &item, const Akonadi::Collection &from, const Akonadi::Collection &to)
1243 if (item.isValid() && ((from.id() == CommonKernel->outboxCollectionFolder().id())
1244 || to.id() == CommonKernel->outboxCollectionFolder().id())) {
1245 startUpdateMessageActionsTimer();
1249 //-------------------------------------------------------------------------
1250 void KMMainWidget::slotFocusQuickSearch()
1252 const QString text = mMsgView ? mMsgView->copyText() : QString();
1253 mMessagePane->focusQuickSearch(text);
1256 //-------------------------------------------------------------------------
1257 bool KMMainWidget::slotSearch()
1259 if (!mSearchWin) {
1260 mSearchWin = new SearchWindow(this, mCurrentFolder ? mCurrentFolder->collection() : Akonadi::Collection());
1261 mSearchWin->setModal(false);
1262 mSearchWin->setObjectName(QStringLiteral("Search"));
1263 } else {
1264 mSearchWin->activateFolder(mCurrentFolder ? mCurrentFolder->collection() : Akonadi::Collection());
1267 mSearchWin->show();
1268 KWindowSystem::activateWindow(mSearchWin->winId());
1269 return true;
1272 //-----------------------------------------------------------------------------
1273 void KMMainWidget::slotHelp()
1275 KHelpClient::invokeHelp();
1278 //-----------------------------------------------------------------------------
1279 void KMMainWidget::slotFilter()
1281 FilterIf->openFilterDialog(true);
1284 void KMMainWidget::slotManageSieveScripts()
1286 if (!kmkernel->askToGoOnline()) {
1287 return;
1289 if (mManageSieveDialog) {
1290 return;
1293 mManageSieveDialog = new KSieveUi::ManageSieveScriptsDialog;
1294 connect(mManageSieveDialog.data(), &KSieveUi::ManageSieveScriptsDialog::finished, this, &KMMainWidget::slotCheckVacation);
1295 mManageSieveDialog->show();
1298 //-----------------------------------------------------------------------------
1299 void KMMainWidget::slotCheckMail()
1301 kmkernel->checkMail();
1304 //-----------------------------------------------------------------------------
1305 void KMMainWidget::slotCheckMailOnStartup()
1307 kmkernel->checkMailOnStartup();
1310 void KMMainWidget::slotCompose()
1312 KMail::Composer *win;
1313 KMime::Message::Ptr msg(new KMime::Message());
1315 bool forceCursorPosition = false;
1316 if (mCurrentFolder) {
1317 MessageHelper::initHeader(msg, KMKernel::self()->identityManager(), mCurrentFolder->identity());
1318 //Laurent: bug 289905
1320 if ( mCurrentFolder->collection().isValid() && mCurrentFolder->putRepliesInSameFolder() ) {
1321 KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Fcc", msg.get(), QString::number( mCurrentFolder->collection().id() ), "utf-8" );
1322 msg->setHeader( header );
1325 TemplateParser::TemplateParser parser(msg, TemplateParser::TemplateParser::NewMessage);
1326 parser.setIdentityManager(KMKernel::self()->identityManager());
1327 parser.process(msg, mCurrentFolder->collection());
1328 win = KMail::makeComposer(msg, false, false, KMail::Composer::New, mCurrentFolder->identity());
1329 win->setCollectionForNewMessage(mCurrentFolder->collection());
1330 forceCursorPosition = parser.cursorPositionWasSet();
1331 } else {
1332 MessageHelper::initHeader(msg, KMKernel::self()->identityManager());
1333 TemplateParser::TemplateParser parser(msg, TemplateParser::TemplateParser::NewMessage);
1334 parser.setIdentityManager(KMKernel::self()->identityManager());
1335 parser.process(KMime::Message::Ptr(), Akonadi::Collection());
1336 win = KMail::makeComposer(msg, false, false, KMail::Composer::New);
1337 forceCursorPosition = parser.cursorPositionWasSet();
1339 if (forceCursorPosition) {
1340 win->setFocusToEditor();
1342 win->show();
1346 //-----------------------------------------------------------------------------
1347 // TODO: do we want the list sorted alphabetically?
1348 void KMMainWidget::slotShowNewFromTemplate()
1350 if (mCurrentFolder) {
1351 const KIdentityManagement::Identity &ident =
1352 kmkernel->identityManager()->identityForUoidOrDefault(mCurrentFolder->identity());
1353 mTemplateFolder = CommonKernel->collectionFromId(ident.templates().toLongLong());
1356 if (!mTemplateFolder.isValid()) {
1357 mTemplateFolder = CommonKernel->templatesCollectionFolder();
1359 if (!mTemplateFolder.isValid()) {
1360 return;
1363 mTemplateMenu->menu()->clear();
1365 Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(mTemplateFolder);
1366 job->fetchScope().setAncestorRetrieval(ItemFetchScope::Parent);
1367 job->fetchScope().fetchFullPayload();
1368 connect(job, &Akonadi::ItemFetchJob::result, this, &KMMainWidget::slotDelayedShowNewFromTemplate);
1371 void KMMainWidget::slotDelayedShowNewFromTemplate(KJob *job)
1373 Akonadi::ItemFetchJob *fetchJob = qobject_cast<Akonadi::ItemFetchJob *>(job);
1375 const Akonadi::Item::List items = fetchJob->items();
1376 const int numberOfItems = items.count();
1377 for (int idx = 0; idx < numberOfItems; ++idx) {
1378 KMime::Message::Ptr msg = MessageCore::Util::message(items.at(idx));
1379 if (msg) {
1380 QString subj = msg->subject()->asUnicodeString();
1381 if (subj.isEmpty()) {
1382 subj = i18n("No Subject");
1385 QAction *templateAction = mTemplateMenu->menu()->addAction(KStringHandler::rsqueeze(subj.replace(QLatin1Char('&'), QStringLiteral("&&"))));
1386 QVariant var;
1387 var.setValue(items.at(idx));
1388 templateAction->setData(var);
1392 // If there are no templates available, add a menu entry which informs
1393 // the user about this.
1394 if (mTemplateMenu->menu()->actions().isEmpty()) {
1395 QAction *noAction = mTemplateMenu->menu()->addAction(
1396 i18n("(no templates)"));
1397 noAction->setEnabled(false);
1401 //-----------------------------------------------------------------------------
1402 void KMMainWidget::slotNewFromTemplate(QAction *action)
1405 if (!mTemplateFolder.isValid()) {
1406 return;
1408 const Akonadi::Item item = action->data().value<Akonadi::Item>();
1409 newFromTemplate(item);
1412 //-----------------------------------------------------------------------------
1413 void KMMainWidget::newFromTemplate(const Akonadi::Item &msg)
1415 if (!msg.isValid()) {
1416 return;
1418 KMCommand *command = new KMUseTemplateCommand(this, msg);
1419 command->start();
1422 //-----------------------------------------------------------------------------
1423 void KMMainWidget::slotPostToML()
1425 if (mCurrentFolder && mCurrentFolder->isMailingListEnabled()) {
1426 if (KMail::Util::mailingListPost(mCurrentFolder)) {
1427 return;
1430 slotCompose();
1433 void KMMainWidget::slotExpireFolder()
1435 if (!mCurrentFolder) {
1436 return;
1438 bool mustDeleteExpirationAttribute = false;
1439 MailCommon::ExpireCollectionAttribute *attr = MailCommon::Util::expirationCollectionAttribute(mCurrentFolder->collection(), mustDeleteExpirationAttribute);
1440 bool canBeExpired = true;
1441 if (!attr->isAutoExpire()) {
1442 canBeExpired = false;
1443 } else if (attr->unreadExpireUnits() == MailCommon::ExpireCollectionAttribute::ExpireNever &&
1444 attr->readExpireUnits() == MailCommon::ExpireCollectionAttribute::ExpireNever) {
1445 canBeExpired = false;
1448 if (!canBeExpired) {
1449 const QString message = i18n("This folder does not have any expiry options set");
1450 KMessageBox::information(this, message);
1451 if (mustDeleteExpirationAttribute) {
1452 delete attr;
1454 return;
1457 if (KMailSettings::self()->warnBeforeExpire()) {
1458 const QString message = i18n("<qt>Are you sure you want to expire the folder <b>%1</b>?</qt>",
1459 mCurrentFolder->name().toHtmlEscaped());
1460 if (KMessageBox::warningContinueCancel(this, message, i18n("Expire Folder"),
1461 KGuiItem(i18n("&Expire")))
1462 != KMessageBox::Continue) {
1463 if (mustDeleteExpirationAttribute) {
1464 delete attr;
1466 return;
1470 MailCommon::Util::expireOldMessages(mCurrentFolder->collection(), true /*immediate*/);
1471 if (mustDeleteExpirationAttribute) {
1472 delete attr;
1476 //-----------------------------------------------------------------------------
1477 void KMMainWidget::slotEmptyFolder()
1479 if (!mCurrentFolder) {
1480 return;
1482 const bool isTrash = CommonKernel->folderIsTrash(mCurrentFolder->collection());
1483 if (KMailSettings::self()->confirmBeforeEmpty()) {
1484 const QString title = (isTrash) ? i18n("Empty Trash") : i18n("Move to Trash");
1485 const QString text = (isTrash) ?
1486 i18n("Are you sure you want to empty the trash folder?") :
1487 i18n("<qt>Are you sure you want to move all messages from "
1488 "folder <b>%1</b> to the trash?</qt>", mCurrentFolder->name().toHtmlEscaped());
1490 if (KMessageBox::warningContinueCancel(this, text, title, KGuiItem(title, QStringLiteral("user-trash")))
1491 != KMessageBox::Continue) {
1492 return;
1495 #ifndef QT_NO_CURSOR
1496 KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy());
1497 #endif
1498 slotSelectAllMessages();
1499 if (isTrash) {
1500 /* Don't ask for confirmation again when deleting, the user has already
1501 confirmed. */
1502 slotDeleteMsg(false);
1503 } else {
1504 slotTrashSelectedMessages();
1507 if (mMsgView) {
1508 mMsgView->clearCache();
1511 if (!isTrash) {
1512 BroadcastStatus::instance()->setStatusMsg(i18n("Moved all messages to the trash"));
1515 updateMessageActions();
1517 // Disable empty trash/move all to trash action - we've just deleted/moved
1518 // all folder contents.
1519 mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)->setEnabled(false);
1522 //-----------------------------------------------------------------------------
1523 void KMMainWidget::slotArchiveFolder()
1525 if (mCurrentFolder && mCurrentFolder->collection().isValid()) {
1526 KMail::ArchiveFolderDialog archiveDialog;
1527 archiveDialog.setFolder(mCurrentFolder->collection());
1528 archiveDialog.exec();
1532 //-----------------------------------------------------------------------------
1533 void KMMainWidget::slotRemoveFolder()
1535 if (!mCurrentFolder) {
1536 return;
1538 if (!mCurrentFolder->collection().isValid()) {
1539 return;
1541 if (mCurrentFolder->isSystemFolder()) {
1542 return;
1544 if (mCurrentFolder->isReadOnly()) {
1545 return;
1548 RemoveCollectionJob *job = new RemoveCollectionJob(this);
1549 connect(job, &RemoveCollectionJob::clearCurrentFolder, this, &KMMainWidget::slotClearCurrentFolder);
1550 job->setMainWidget(this);
1551 job->setCurrentFolder(mCurrentFolder->collection());
1552 job->start();
1555 void KMMainWidget::slotClearCurrentFolder()
1557 mCurrentFolder.clear();
1560 //-----------------------------------------------------------------------------
1561 void KMMainWidget::slotExpireAll()
1563 if (KMailSettings::self()->warnBeforeExpire()) {
1564 const int ret = KMessageBox::warningContinueCancel(KMainWindow::memberList().first(),
1565 i18n("Are you sure you want to expire all old messages?"),
1566 i18n("Expire Old Messages?"), KGuiItem(i18n("Expire")));
1567 if (ret != KMessageBox::Continue) {
1568 return;
1572 kmkernel->expireAllFoldersNow();
1575 //-----------------------------------------------------------------------------
1576 void KMMainWidget::slotOverrideHtmlLoadExt()
1578 if (mHtmlLoadExtGlobalSetting == mFolderHtmlLoadExtPreference) {
1579 int result = KMessageBox::warningContinueCancel(this,
1580 // the warning text is taken from configuredialog.cpp:
1581 i18n("Loading external references in html mail will make you more vulnerable to "
1582 "\"spam\" and may increase the likelihood that your system will be "
1583 "compromised by other present and anticipated security exploits."),
1584 i18n("Security Warning"),
1585 KGuiItem(i18n("Load External References")),
1586 KStandardGuiItem::cancel(),
1587 QStringLiteral("OverrideHtmlLoadExtWarning"), Q_NULLPTR);
1588 if (result == KMessageBox::Cancel) {
1589 mPreferHtmlLoadExtAction->setChecked(false);
1590 return;
1593 mFolderHtmlLoadExtPreference = !mFolderHtmlLoadExtPreference;
1595 if (mMsgView) {
1596 mMsgView->setHtmlLoadExtOverride(mFolderHtmlLoadExtPreference);
1597 mMsgView->update(true);
1601 //-----------------------------------------------------------------------------
1602 void KMMainWidget::slotMessageQueuedOrDrafted()
1604 if (!CommonKernel->folderIsDraftOrOutbox(mCurrentFolder->collection())) {
1605 return;
1607 if (mMsgView) {
1608 mMsgView->update(true);
1612 //-----------------------------------------------------------------------------
1613 void KMMainWidget::slotForwardInlineMsg()
1615 if (!mCurrentFolder) {
1616 return;
1619 const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
1620 if (selectedMessages.isEmpty()) {
1621 return;
1623 KMForwardCommand *command = new KMForwardCommand(
1624 this, selectedMessages, mCurrentFolder->identity()
1627 command->start();
1630 //-----------------------------------------------------------------------------
1631 void KMMainWidget::slotForwardAttachedMessage()
1633 if (!mCurrentFolder) {
1634 return;
1637 const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
1638 if (selectedMessages.isEmpty()) {
1639 return;
1641 KMForwardAttachedCommand *command = new KMForwardAttachedCommand(
1642 this, selectedMessages, mCurrentFolder->identity()
1645 command->start();
1648 //-----------------------------------------------------------------------------
1649 void KMMainWidget::slotUseTemplate()
1651 newFromTemplate(mMessagePane->currentItem());
1654 //-----------------------------------------------------------------------------
1655 void KMMainWidget::slotResendMsg()
1657 const Akonadi::Item msg = mMessagePane->currentItem();
1658 if (!msg.isValid()) {
1659 return;
1661 KMCommand *command = new KMResendMessageCommand(this, msg);
1663 command->start();
1666 //-----------------------------------------------------------------------------
1667 // Message moving and permanent deletion
1670 void KMMainWidget::moveMessageSelected(MessageList::Core::MessageItemSetReference ref, const Akonadi::Collection &dest, bool confirmOnDeletion)
1672 Akonadi::Item::List selectMsg = mMessagePane->itemListFromPersistentSet(ref);
1673 // If this is a deletion, ask for confirmation
1674 if (confirmOnDeletion) {
1675 int ret = KMessageBox::warningContinueCancel(
1676 this,
1677 i18np(
1678 "<qt>Do you really want to delete the selected message?<br />"
1679 "Once deleted, it cannot be restored.</qt>",
1680 "<qt>Do you really want to delete the %1 selected messages?<br />"
1681 "Once deleted, they cannot be restored.</qt>",
1682 selectMsg.count()
1684 selectMsg.count() > 1 ? i18n("Delete Messages") : i18n("Delete Message"),
1685 KStandardGuiItem::del(),
1686 KStandardGuiItem::cancel(),
1687 QStringLiteral("NoConfirmDelete")
1689 if (ret == KMessageBox::Cancel) {
1690 mMessagePane->deletePersistentSet(ref);
1691 return; // user canceled the action
1694 mMessagePane->markMessageItemsAsAboutToBeRemoved(ref, true);
1695 // And stuff them into a KMMoveCommand :)
1696 KMMoveCommand *command = new KMMoveCommand(dest, selectMsg, ref);
1697 QObject::connect(
1698 command, &KMMoveCommand::moveDone,
1699 this, &KMMainWidget::slotMoveMessagesCompleted
1701 command->start();
1703 if (dest.isValid()) {
1704 BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages..."));
1705 } else {
1706 BroadcastStatus::instance()->setStatusMsg(i18n("Deleting messages..."));
1710 void KMMainWidget::slotMoveMessagesCompleted(KMMoveCommand *command)
1712 Q_ASSERT(command);
1713 mMessagePane->markMessageItemsAsAboutToBeRemoved(command->refSet(), false);
1714 mMessagePane->deletePersistentSet(command->refSet());
1715 // Bleah :D
1716 const bool moveWasReallyADelete = !command->destFolder().isValid();
1718 if (command->result() == KMCommand::OK) {
1719 if (moveWasReallyADelete) {
1720 BroadcastStatus::instance()->setStatusMsg(i18n("Messages deleted successfully."));
1721 } else {
1722 BroadcastStatus::instance()->setStatusMsg(i18n("Messages moved successfully."));
1724 } else {
1725 if (moveWasReallyADelete) {
1726 if (command->result() == KMCommand::Failed) {
1727 BroadcastStatus::instance()->setStatusMsg(i18n("Deleting messages failed."));
1728 } else {
1729 BroadcastStatus::instance()->setStatusMsg(i18n("Deleting messages canceled."));
1731 } else {
1732 if (command->result() == KMCommand::Failed) {
1733 BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages failed."));
1734 } else {
1735 BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages canceled."));
1739 // The command will autodelete itself and will also kill the set.
1742 void KMMainWidget::slotDeleteMessages()
1744 slotDeleteMsg(true);
1747 Akonadi::Item::List KMMainWidget::currentSelection() const
1749 Akonadi::Item::List selectMsg;
1750 MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet();
1751 if (ref != -1) {
1752 selectMsg = mMessagePane->itemListFromPersistentSet(ref);
1754 return selectMsg;
1757 void KMMainWidget::slotDeleteMsg(bool confirmDelete)
1759 // Create a persistent message set from the current selection
1760 MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet();
1761 if (ref != -1) {
1762 moveMessageSelected(ref, Akonadi::Collection(), confirmDelete);
1766 void KMMainWidget::slotDeleteThread(bool confirmDelete)
1768 // Create a persistent set from the current thread.
1769 MessageList::Core::MessageItemSetReference ref = mMessagePane->currentThreadAsPersistentSet();
1770 if (ref != -1) {
1771 moveMessageSelected(ref, Akonadi::Collection(), confirmDelete);
1775 FolderSelectionDialog *KMMainWidget::moveOrCopyToDialog()
1777 if (!mMoveOrCopyToDialog) {
1778 FolderSelectionDialog::SelectionFolderOption options = FolderSelectionDialog::HideVirtualFolder;
1779 mMoveOrCopyToDialog = new FolderSelectionDialog(this, options);
1780 mMoveOrCopyToDialog->setModal(true);
1782 return mMoveOrCopyToDialog;
1785 FolderSelectionDialog *KMMainWidget::selectFromAllFoldersDialog()
1787 if (!mSelectFromAllFoldersDialog) {
1788 FolderSelectionDialog::SelectionFolderOptions options = FolderSelectionDialog::None;
1789 options |= FolderSelectionDialog::NotAllowToCreateNewFolder;
1791 mSelectFromAllFoldersDialog = new FolderSelectionDialog(this, options);
1792 mSelectFromAllFoldersDialog->setModal(true);
1794 return mSelectFromAllFoldersDialog;
1797 void KMMainWidget::slotMoveSelectedMessageToFolder()
1799 QPointer<MailCommon::FolderSelectionDialog> dialog(moveOrCopyToDialog());
1800 dialog->setWindowTitle(i18n("Move Messages to Folder"));
1801 if (dialog->exec() && dialog) {
1802 const Akonadi::Collection dest = dialog->selectedCollection();
1803 if (dest.isValid()) {
1804 moveSelectedMessagesToFolder(dest);
1809 void KMMainWidget::moveSelectedMessagesToFolder(const Akonadi::Collection &dest)
1811 MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet();
1812 if (ref != -1) {
1813 //Need to verify if dest == src ??? akonadi do it for us.
1814 moveMessageSelected(ref, dest, false);
1818 void KMMainWidget::copyMessageSelected(const Akonadi::Item::List &selectMsg, const Akonadi::Collection &dest)
1820 if (selectMsg.isEmpty()) {
1821 return;
1823 // And stuff them into a KMCopyCommand :)
1824 KMCommand *command = new KMCopyCommand(dest, selectMsg);
1825 QObject::connect(
1826 command, &KMCommand::completed,
1827 this, &KMMainWidget::slotCopyMessagesCompleted
1829 command->start();
1830 BroadcastStatus::instance()->setStatusMsg(i18n("Copying messages..."));
1833 void KMMainWidget::slotCopyMessagesCompleted(KMCommand *command)
1835 Q_ASSERT(command);
1836 if (command->result() == KMCommand::OK) {
1837 BroadcastStatus::instance()->setStatusMsg(i18n("Messages copied successfully."));
1838 } else {
1839 if (command->result() == KMCommand::Failed) {
1840 BroadcastStatus::instance()->setStatusMsg(i18n("Copying messages failed."));
1841 } else {
1842 BroadcastStatus::instance()->setStatusMsg(i18n("Copying messages canceled."));
1845 // The command will autodelete itself and will also kill the set.
1848 void KMMainWidget::slotCopySelectedMessagesToFolder()
1850 QPointer<MailCommon::FolderSelectionDialog> dialog(moveOrCopyToDialog());
1851 dialog->setWindowTitle(i18n("Copy Messages to Folder"));
1853 if (dialog->exec() && dialog) {
1854 const Akonadi::Collection dest = dialog->selectedCollection();
1855 if (dest.isValid()) {
1856 copySelectedMessagesToFolder(dest);
1861 void KMMainWidget::copySelectedMessagesToFolder(const Akonadi::Collection &dest)
1863 const Akonadi::Item::List lstMsg = mMessagePane->selectionAsMessageItemList();
1864 if (!lstMsg.isEmpty()) {
1865 copyMessageSelected(lstMsg, dest);
1869 //-----------------------------------------------------------------------------
1870 // Message trashing
1872 void KMMainWidget::trashMessageSelected(MessageList::Core::MessageItemSetReference ref)
1874 if (!mCurrentFolder) {
1875 return;
1878 const Akonadi::Item::List select = mMessagePane->itemListFromPersistentSet(ref);
1879 mMessagePane->markMessageItemsAsAboutToBeRemoved(ref, true);
1881 // FIXME: Why we don't use KMMoveCommand( trashFolder(), selectedMessages ); ?
1882 // And stuff them into a KMTrashMsgCommand :)
1883 KMCommand *command = new KMTrashMsgCommand(mCurrentFolder->collection(), select, ref);
1885 QObject::connect(
1886 command, SIGNAL(moveDone(KMMoveCommand*)),
1887 this, SLOT(slotTrashMessagesCompleted(KMMoveCommand*))
1889 command->start();
1890 BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages to trash..."));
1893 void KMMainWidget::slotTrashMessagesCompleted(KMMoveCommand *command)
1895 Q_ASSERT(command);
1896 mMessagePane->markMessageItemsAsAboutToBeRemoved(command->refSet(), false);
1897 mMessagePane->deletePersistentSet(command->refSet());
1898 if (command->result() == KMCommand::OK) {
1899 BroadcastStatus::instance()->setStatusMsg(i18n("Messages moved to trash successfully."));
1900 } else {
1901 if (command->result() == KMCommand::Failed) {
1902 BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages to trash failed."));
1903 } else {
1904 BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages to trash canceled."));
1908 // The command will autodelete itself and will also kill the set.
1911 void KMMainWidget::slotTrashSelectedMessages()
1913 MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet();
1914 if (ref != -1) {
1915 trashMessageSelected(ref);
1919 void KMMainWidget::slotTrashThread()
1921 MessageList::Core::MessageItemSetReference ref = mMessagePane->currentThreadAsPersistentSet();
1922 if (ref != -1) {
1923 trashMessageSelected(ref);
1927 //-----------------------------------------------------------------------------
1928 // Message tag setting for messages
1930 // FIXME: The "selection" version of these functions is in MessageActions.
1931 // We should probably move everything there....
1932 void KMMainWidget::toggleMessageSetTag(const Akonadi::Item::List &select, const Akonadi::Tag &tag)
1934 if (select.isEmpty()) {
1935 return;
1937 KMCommand *command = new KMSetTagCommand(Akonadi::Tag::List() << tag, select, KMSetTagCommand::Toggle);
1938 command->start();
1941 void KMMainWidget::slotSelectMoreMessageTagList()
1943 const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
1944 if (selectedMessages.isEmpty()) {
1945 return;
1948 TagSelectDialog dlg(this, selectedMessages.count(), selectedMessages.first());
1949 dlg.setActionCollection(QList<KActionCollection *>() << actionCollection());
1950 if (dlg.exec()) {
1951 const Akonadi::Tag::List lst = dlg.selectedTag();
1952 KMCommand *command = new KMSetTagCommand(lst, selectedMessages, KMSetTagCommand::CleanExistingAndAddNew);
1953 command->start();
1957 void KMMainWidget::slotUpdateMessageTagList(const Akonadi::Tag &tag)
1959 // Create a persistent set from the current thread.
1960 const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
1961 if (selectedMessages.isEmpty()) {
1962 return;
1964 toggleMessageSetTag(selectedMessages, tag);
1967 void KMMainWidget::refreshMessageListSelection()
1969 mAkonadiStandardActionManager->setItemSelectionModel(mMessagePane->currentItemSelectionModel());
1970 slotMessageSelected(mMessagePane->currentItem());
1973 //-----------------------------------------------------------------------------
1974 // Status setting for threads
1976 // FIXME: The "selection" version of these functions is in MessageActions.
1977 // We should probably move everything there....
1978 void KMMainWidget::setMessageSetStatus(const Akonadi::Item::List &select,
1979 const Akonadi::MessageStatus &status,
1980 bool toggle)
1982 KMCommand *command = new KMSetStatusCommand(status, select, toggle);
1983 command->start();
1986 void KMMainWidget::setCurrentThreadStatus(const Akonadi::MessageStatus &status, bool toggle)
1988 const Akonadi::Item::List select = mMessagePane->currentThreadAsMessageList();
1989 if (select.isEmpty()) {
1990 return;
1992 setMessageSetStatus(select, status, toggle);
1995 void KMMainWidget::slotSetThreadStatusUnread()
1997 setCurrentThreadStatus(MessageStatus::statusRead(), true);
2000 void KMMainWidget::slotSetThreadStatusImportant()
2002 setCurrentThreadStatus(MessageStatus::statusImportant(), true);
2005 void KMMainWidget::slotSetThreadStatusRead()
2007 setCurrentThreadStatus(MessageStatus::statusRead(), false);
2010 void KMMainWidget::slotSetThreadStatusToAct()
2012 setCurrentThreadStatus(MessageStatus::statusToAct(), true);
2015 void KMMainWidget::slotSetThreadStatusWatched()
2017 setCurrentThreadStatus(MessageStatus::statusWatched(), true);
2018 if (mWatchThreadAction->isChecked()) {
2019 mIgnoreThreadAction->setChecked(false);
2023 void KMMainWidget::slotSetThreadStatusIgnored()
2025 setCurrentThreadStatus(MessageStatus::statusIgnored(), true);
2026 if (mIgnoreThreadAction->isChecked()) {
2027 mWatchThreadAction->setChecked(false);
2031 //-----------------------------------------------------------------------------
2032 void KMMainWidget::slotRedirectMessage()
2034 const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2035 if (selectedMessages.isEmpty()) {
2036 return;
2039 KMCommand *command = new KMRedirectCommand(this, selectedMessages);
2040 command->start();
2043 //-----------------------------------------------------------------------------
2044 void KMMainWidget::slotCustomReplyToMsg(const QString &tmpl)
2046 const Akonadi::Item msg = mMessagePane->currentItem();
2047 if (!msg.isValid()) {
2048 return;
2051 const QString text = mMsgView ? mMsgView->copyText() : QString();
2053 qCDebug(KMAIL_LOG) << "Reply with template:" << tmpl;
2055 KMCommand *command = new KMReplyCommand(this,
2056 msg,
2057 MessageComposer::ReplySmart,
2058 text, false,
2059 tmpl);
2060 command->start();
2063 //-----------------------------------------------------------------------------
2064 void KMMainWidget::slotCustomReplyAllToMsg(const QString &tmpl)
2066 const Akonadi::Item msg = mMessagePane->currentItem();
2067 if (!msg.isValid()) {
2068 return;
2071 const QString text = mMsgView ? mMsgView->copyText() : QString();
2073 qCDebug(KMAIL_LOG) << "Reply to All with template:" << tmpl;
2075 KMCommand *command = new KMReplyCommand(this,
2076 msg,
2077 MessageComposer::ReplyAll,
2078 text,
2079 false,
2080 tmpl
2083 command->start();
2086 //-----------------------------------------------------------------------------
2087 void KMMainWidget::slotCustomForwardMsg(const QString &tmpl)
2089 if (!mCurrentFolder) {
2090 return;
2093 const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2094 if (selectedMessages.isEmpty()) {
2095 return;
2098 qCDebug(KMAIL_LOG) << "Forward with template:" << tmpl;
2099 KMForwardCommand *command = new KMForwardCommand(
2100 this, selectedMessages, mCurrentFolder->identity(), tmpl
2103 command->start();
2106 void KMMainWidget::openFilterDialog(const QByteArray &field, const QString &value)
2108 FilterIf->openFilterDialog(false);
2109 FilterIf->createFilter(field, value);
2112 //-----------------------------------------------------------------------------
2113 void KMMainWidget::slotSubjectFilter()
2115 const KMime::Message::Ptr msg = mMessagePane->currentMessage();
2116 if (!msg) {
2117 return;
2120 openFilterDialog("Subject", msg->subject()->asUnicodeString());
2123 //-----------------------------------------------------------------------------
2124 void KMMainWidget::slotFromFilter()
2126 KMime::Message::Ptr msg = mMessagePane->currentMessage();
2127 if (!msg) {
2128 return;
2131 AddrSpecList al = MessageHelper::extractAddrSpecs(msg, "From");
2132 if (al.empty()) {
2133 openFilterDialog("From", msg->from()->asUnicodeString());
2134 } else {
2135 openFilterDialog("From", al.front().asString());
2139 //-----------------------------------------------------------------------------
2140 void KMMainWidget::slotToFilter()
2142 KMime::Message::Ptr msg = mMessagePane->currentMessage();
2143 if (!msg) {
2144 return;
2146 openFilterDialog("To", msg->to()->asUnicodeString());
2149 void KMMainWidget::slotCcFilter()
2151 KMime::Message::Ptr msg = mMessagePane->currentMessage();
2152 if (!msg) {
2153 return;
2155 openFilterDialog("Cc", msg->cc()->asUnicodeString());
2158 void KMMainWidget::slotBandwidth(bool b)
2160 PimCommon::NetworkUtil::self()->setLowBandwidth(b);
2163 //-----------------------------------------------------------------------------
2164 void KMMainWidget::slotUndo()
2166 kmkernel->undoStack()->undo();
2167 updateMessageActions();
2168 updateFolderMenu();
2171 //-----------------------------------------------------------------------------
2172 void KMMainWidget::slotJumpToFolder()
2174 QPointer<MailCommon::FolderSelectionDialog> dialog(selectFromAllFoldersDialog());
2175 dialog->setWindowTitle(i18n("Jump to Folder"));
2176 if (dialog->exec() && dialog) {
2177 Akonadi::Collection collection = dialog->selectedCollection();
2178 if (collection.isValid()) {
2179 slotSelectCollectionFolder(collection);
2184 void KMMainWidget::slotSelectCollectionFolder(const Akonadi::Collection &col)
2186 if (mFolderTreeWidget) {
2187 mFolderTreeWidget->selectCollectionFolder(col);
2188 slotFolderChanged(col);
2192 void KMMainWidget::slotApplyFilters()
2194 const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2195 if (selectedMessages.isEmpty()) {
2196 return;
2198 applyFilters(selectedMessages);
2201 void KMMainWidget::slotApplyFiltersOnFolder()
2203 if (mCurrentFolder && mCurrentFolder->collection().isValid()) {
2204 Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(mCurrentFolder->collection(), this);
2205 connect(job, &Akonadi::ItemFetchJob::result, this, &KMMainWidget::slotFetchItemsForFolderDone);
2209 void KMMainWidget::slotFetchItemsForFolderDone(KJob *job)
2211 Akonadi::ItemFetchJob *fjob = dynamic_cast<Akonadi::ItemFetchJob *>(job);
2212 Q_ASSERT(fjob);
2213 Akonadi::Item::List items = fjob->items();
2214 applyFilters(items);
2217 void KMMainWidget::applyFilters(const Akonadi::Item::List &selectedMessages)
2219 #ifndef QT_NO_CURSOR
2220 KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy());
2221 #endif
2223 MailCommon::FilterManager::instance()->filter(selectedMessages);
2226 //-----------------------------------------------------------------------------
2227 void KMMainWidget::slotCheckVacation()
2229 updateVacationScriptStatus(false);
2230 if (!kmkernel->askToGoOnline()) {
2231 return;
2234 mVacationManager->checkVacation();
2235 connect(mVacationManager, SIGNAL(updateVacationScriptStatus(bool,QString)), SLOT(updateVacationScriptStatus(bool,QString)));
2236 connect(mVacationManager, SIGNAL(editVacation()), SLOT(slotEditVacation()));
2239 void KMMainWidget::slotEditVacation(const QString &serverName)
2241 if (!kmkernel->askToGoOnline()) {
2242 return;
2245 mVacationManager->slotEditVacation(serverName);
2248 //-----------------------------------------------------------------------------
2249 void KMMainWidget::slotDebugSieve()
2251 #if !defined(NDEBUG)
2252 if (mSieveDebugDialog) {
2253 return;
2256 mSieveDebugDialog = new KSieveUi::SieveDebugDialog(this);
2257 mSieveDebugDialog->exec();
2258 delete mSieveDebugDialog;
2259 #endif
2262 void KMMainWidget::slotConfigChanged()
2264 readConfig();
2265 mMsgActions->setupForwardActions(actionCollection());
2266 mMsgActions->setupForwardingActionsList(mGUIClient);
2269 //-----------------------------------------------------------------------------
2270 void KMMainWidget::slotSaveMsg()
2272 const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2273 if (selectedMessages.isEmpty()) {
2274 return;
2276 KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand(this, selectedMessages);
2277 saveCommand->start();
2280 //-----------------------------------------------------------------------------
2281 void KMMainWidget::slotOpenMsg()
2283 KMOpenMsgCommand *openCommand = new KMOpenMsgCommand(this, QUrl(), overrideEncoding(), this);
2285 openCommand->start();
2288 //-----------------------------------------------------------------------------
2289 void KMMainWidget::slotSaveAttachments()
2291 const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
2292 if (selectedMessages.isEmpty()) {
2293 return;
2295 // Avoid re-downloading in the common case that only one message is selected, and the message
2296 // is also displayed in the viewer. For this, create a dummy item without a parent collection / item id,
2297 // so that KMCommand doesn't download it.
2298 KMSaveAttachmentsCommand *saveCommand = Q_NULLPTR;
2299 if (mMsgView && selectedMessages.size() == 1 &&
2300 mMsgView->message().hasPayload<KMime::Message::Ptr>() &&
2301 selectedMessages.first().id() == mMsgView->message().id()) {
2302 Akonadi::Item dummyItem;
2303 dummyItem.setPayload<KMime::Message::Ptr>(mMsgView->message().payload<KMime::Message::Ptr>());
2304 saveCommand = new KMSaveAttachmentsCommand(this, dummyItem, mMsgView->viewer());
2305 } else {
2306 saveCommand = new KMSaveAttachmentsCommand(this, selectedMessages);
2309 saveCommand->start();
2312 void KMMainWidget::slotOnlineStatus()
2314 // KMKernel will Q_EMIT a signal when we toggle the network state that is caught by
2315 // KMMainWidget::slotUpdateOnlineStatus to update our GUI
2316 if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Online) {
2317 // if online; then toggle and set it offline.
2318 kmkernel->stopNetworkJobs();
2319 } else {
2320 kmkernel->resumeNetworkJobs();
2321 slotCheckVacation();
2325 void KMMainWidget::slotUpdateOnlineStatus(KMailSettings::EnumNetworkState::type)
2327 if (!mAkonadiStandardActionManager) {
2328 return;
2330 QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::ToggleWorkOffline);
2331 if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Online) {
2332 action->setText(i18n("Work Offline"));
2333 action->setIcon(QIcon::fromTheme(QStringLiteral("user-offline")));
2334 } else {
2335 action->setText(i18n("Work Online"));
2336 action->setIcon(QIcon::fromTheme(QStringLiteral("user-online")));
2340 //-----------------------------------------------------------------------------
2341 void KMMainWidget::slotSendQueued()
2343 if (kmkernel->msgSender()) {
2344 kmkernel->msgSender()->sendQueued();
2348 //-----------------------------------------------------------------------------
2349 void KMMainWidget::slotSendQueuedVia(MailTransport::Transport *transport)
2351 if (transport) {
2352 if (kmkernel->msgSender()) {
2353 kmkernel->msgSender()->sendQueued(transport->id());
2358 //-----------------------------------------------------------------------------
2359 void KMMainWidget::slotShowBusySplash()
2361 if (mReaderWindowActive) {
2362 mMsgView->displayBusyPage();
2366 void KMMainWidget::showOfflinePage()
2368 if (!mReaderWindowActive) {
2369 return;
2372 mMsgView->displayOfflinePage();
2375 void KMMainWidget::showResourceOfflinePage()
2377 if (!mReaderWindowActive) {
2378 return;
2381 mMsgView->displayResourceOfflinePage();
2384 //-----------------------------------------------------------------------------
2385 void KMMainWidget::slotReplaceMsgByUnencryptedVersion()
2387 qCDebug(KMAIL_LOG);
2388 Akonadi::Item oldMsg = mMessagePane->currentItem();
2389 if (oldMsg.isValid()) {
2390 #if 0
2391 qCDebug(KMAIL_LOG) << "Old message found";
2392 if (oldMsg->hasUnencryptedMsg()) {
2393 qCDebug(KMAIL_LOG) << "Extra unencrypted message found";
2394 KMime::Message *newMsg = oldMsg->unencryptedMsg();
2395 // adjust the message id
2397 QString msgId(oldMsg->msgId());
2398 QString prefix("DecryptedMsg.");
2399 int oldIdx = msgId.indexOf(prefix, 0, Qt::CaseInsensitive);
2400 if (-1 == oldIdx) {
2401 int leftAngle = msgId.lastIndexOf('<');
2402 msgId = msgId.insert((-1 == leftAngle) ? 0 : ++leftAngle, prefix);
2403 } else {
2404 // toggle between "DecryptedMsg." and "DeCryptedMsg."
2405 // to avoid same message id
2406 QCharRef c = msgId[ oldIdx + 2 ];
2407 if ('C' == c) {
2408 c = 'c';
2409 } else {
2410 c = 'C';
2413 newMsg->setMsgId(msgId);
2414 mMsgView->setIdOfLastViewedMessage(msgId);
2416 // insert the unencrypted message
2417 qCDebug(KMAIL_LOG) << "Adding unencrypted message to folder";
2418 mFolder->addMsg(newMsg);
2419 /* Figure out its index in the folder for selecting. This must be count()-1,
2420 * since we append. Be safe and do find, though, just in case. */
2421 int newMsgIdx = mFolder->find(newMsg);
2422 Q_ASSERT(newMsgIdx != -1);
2423 /* we need this unget, to have the message displayed correctly initially */
2424 mFolder->unGetMsg(newMsgIdx);
2425 int idx = mFolder->find(oldMsg);
2426 Q_ASSERT(idx != -1);
2427 /* only select here, so the old one is not un-Gotten before, which would
2428 * render the pointer we hold invalid so that find would fail */
2429 #if 0
2430 // FIXME (Pragma)
2431 mHeaders->setCurrentItemByIndex(newMsgIdx);
2432 #endif
2433 // remove the old one
2434 if (idx != -1) {
2435 qCDebug(KMAIL_LOG) << "Deleting encrypted message";
2436 mFolder->take(idx);
2439 qCDebug(KMAIL_LOG) << "Updating message actions";
2440 updateMessageActions();
2442 qCDebug(KMAIL_LOG) << "Done.";
2443 } else {
2444 qCDebug(KMAIL_LOG) << "NO EXTRA UNENCRYPTED MESSAGE FOUND";
2446 #else
2447 qCDebug(KMAIL_LOG) << "AKONADI PORT: Disabled code in " << Q_FUNC_INFO;
2448 #endif
2449 } else {
2450 qCDebug(KMAIL_LOG) << "PANIC: NO OLD MESSAGE FOUND";
2454 void KMMainWidget::slotFocusOnNextMessage()
2456 mMessagePane->focusNextMessageItem(MessageList::Core::MessageTypeAny, true, false);
2459 void KMMainWidget::slotFocusOnPrevMessage()
2461 mMessagePane->focusPreviousMessageItem(MessageList::Core::MessageTypeAny, true, false);
2464 void KMMainWidget::slotSelectFirstMessage()
2466 mMessagePane->selectFirstMessageItem(MessageList::Core::MessageTypeAny, true);
2469 void KMMainWidget::slotSelectLastMessage()
2471 mMessagePane->selectLastMessageItem(MessageList::Core::MessageTypeAny, true);
2474 void KMMainWidget::slotSelectFocusedMessage()
2476 mMessagePane->selectFocusedMessageItem(true);
2479 void KMMainWidget::slotSelectNextMessage()
2481 mMessagePane->selectNextMessageItem(MessageList::Core::MessageTypeAny,
2482 MessageList::Core::ClearExistingSelection,
2483 true, false);
2486 void KMMainWidget::slotExtendSelectionToNextMessage()
2488 mMessagePane->selectNextMessageItem(
2489 MessageList::Core::MessageTypeAny,
2490 MessageList::Core::GrowOrShrinkExistingSelection,
2491 true, // center item
2492 false // don't loop in folder
2496 void KMMainWidget::slotSelectNextUnreadMessage()
2498 // The looping logic is: "Don't loop" just never loops, "Loop in current folder"
2499 // loops just in current folder, "Loop in all folders" loops in the current folder
2500 // first and then after confirmation jumps to the next folder.
2501 // A bad point here is that if you answer "No, and don't ask me again" to the confirmation
2502 // dialog then you have "Loop in current folder" and "Loop in all folders" that do
2503 // the same thing and no way to get the old behaviour. However, after a consultation on #kontact,
2504 // for bug-to-bug backward compatibility, the masters decided to keep it b0rken :D
2505 // If nobody complains, it stays like it is: if you complain enough maybe the masters will
2506 // decide to reconsider :)
2507 if (!mMessagePane->selectNextMessageItem(
2508 MessageList::Core::MessageTypeUnreadOnly,
2509 MessageList::Core::ClearExistingSelection,
2510 true, // center item
2511 KMailSettings::self()->loopOnGotoUnread() != KMailSettings::EnumLoopOnGotoUnread::DontLoop
2512 )) {
2513 // no next unread message was found in the current folder
2514 if ((KMailSettings::self()->loopOnGotoUnread() ==
2515 KMailSettings::EnumLoopOnGotoUnread::LoopInAllFolders) ||
2516 (KMailSettings::self()->loopOnGotoUnread() ==
2517 KMailSettings::EnumLoopOnGotoUnread::LoopInAllMarkedFolders)) {
2518 mGoToFirstUnreadMessageInSelectedFolder = true;
2519 mFolderTreeWidget->folderTreeView()->selectNextUnreadFolder(true);
2520 mGoToFirstUnreadMessageInSelectedFolder = false;
2525 void KMMainWidget::slotSelectPreviousMessage()
2527 mMessagePane->selectPreviousMessageItem(MessageList::Core::MessageTypeAny,
2528 MessageList::Core::ClearExistingSelection,
2529 true, false);
2532 void KMMainWidget::slotExtendSelectionToPreviousMessage()
2534 mMessagePane->selectPreviousMessageItem(
2535 MessageList::Core::MessageTypeAny,
2536 MessageList::Core::GrowOrShrinkExistingSelection,
2537 true, // center item
2538 false // don't loop in folder
2542 void KMMainWidget::slotSelectPreviousUnreadMessage()
2544 if (!mMessagePane->selectPreviousMessageItem(
2545 MessageList::Core::MessageTypeUnreadOnly,
2546 MessageList::Core::ClearExistingSelection,
2547 true, // center item
2548 KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInCurrentFolder
2549 )) {
2550 // no next unread message was found in the current folder
2551 if ((KMailSettings::self()->loopOnGotoUnread() ==
2552 KMailSettings::EnumLoopOnGotoUnread::LoopInAllFolders) ||
2553 (KMailSettings::self()->loopOnGotoUnread() ==
2554 KMailSettings::EnumLoopOnGotoUnread::LoopInAllMarkedFolders)) {
2555 mGoToFirstUnreadMessageInSelectedFolder = true;
2556 mFolderTreeWidget->folderTreeView()->selectPrevUnreadFolder();
2557 mGoToFirstUnreadMessageInSelectedFolder = false;
2562 void KMMainWidget::slotDisplayCurrentMessage()
2564 if (mMessagePane->currentItem().isValid() && !mMessagePane->searchEditHasFocus()) {
2565 slotMessageActivated(mMessagePane->currentItem());
2569 // Called by double-clicked or 'Enter' in the messagelist -> pop up reader window
2570 void KMMainWidget::slotMessageActivated(const Akonadi::Item &msg)
2572 if (!mCurrentFolder || !msg.isValid()) {
2573 return;
2576 if (CommonKernel->folderIsDraftOrOutbox(mCurrentFolder->collection())) {
2577 mMsgActions->setCurrentMessage(msg);
2578 mMsgActions->editCurrentMessage();
2579 return;
2582 if (CommonKernel->folderIsTemplates(mCurrentFolder->collection())) {
2583 slotUseTemplate();
2584 return;
2587 // Try to fetch the mail, even in offline mode, it might be cached
2588 KMFetchMessageCommand *cmd = new KMFetchMessageCommand(this, msg);
2589 connect(cmd, &KMCommand::completed,
2590 this, &KMMainWidget::slotItemsFetchedForActivation);
2591 cmd->start();
2594 void KMMainWidget::slotItemsFetchedForActivation(KMCommand *command)
2596 KMCommand::Result result = command->result();
2597 if (result != KMCommand::OK) {
2598 qCDebug(KMAIL_LOG) << "Result:" << result;
2599 return;
2602 KMFetchMessageCommand *fetchCmd = qobject_cast<KMFetchMessageCommand *>(command);
2603 const Item msg = fetchCmd->item();
2605 KMReaderMainWin *win = new KMReaderMainWin(mFolderDisplayFormatPreference, mFolderHtmlLoadExtPreference);
2606 const bool useFixedFont = mMsgView ? mMsgView->isFixedFont() :
2607 MessageViewer::MessageViewerSettings::self()->useFixedFont();
2608 win->setUseFixedFont(useFixedFont);
2610 const Akonadi::Collection parentCollection = MailCommon::Util::parentCollectionFromItem(msg);
2611 win->showMessage(overrideEncoding(), msg, parentCollection);
2612 win->show();
2615 void KMMainWidget::slotMessageStatusChangeRequest(const Akonadi::Item &item, const Akonadi::MessageStatus &set, const Akonadi::MessageStatus &clear)
2617 if (!item.isValid()) {
2618 return;
2621 if (clear.toQInt32() != Akonadi::MessageStatus().toQInt32()) {
2622 KMCommand *command = new KMSetStatusCommand(clear, Akonadi::Item::List() << item, true);
2623 command->start();
2626 if (set.toQInt32() != Akonadi::MessageStatus().toQInt32()) {
2627 KMCommand *command = new KMSetStatusCommand(set, Akonadi::Item::List() << item, false);
2628 command->start();
2632 //-----------------------------------------------------------------------------
2633 void KMMainWidget::slotSelectAllMessages()
2635 mMessagePane->selectAll();
2636 updateMessageActions();
2639 void KMMainWidget::slotMessagePopup(const Akonadi::Item &msg, const QUrl &aUrl, const QUrl &imageUrl, const QPoint &aPoint)
2641 updateMessageMenu();
2643 const QString email = KEmailAddress::firstEmailAddress(aUrl.path()).toLower();
2644 if (aUrl.scheme() == QLatin1String("mailto") && !email.isEmpty()) {
2645 Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob(this);
2646 job->setLimit(1);
2647 job->setQuery(Akonadi::ContactSearchJob::Email, email, Akonadi::ContactSearchJob::ExactMatch);
2648 job->setProperty("msg", QVariant::fromValue(msg));
2649 job->setProperty("point", aPoint);
2650 job->setProperty("imageUrl", imageUrl);
2651 job->setProperty("url", aUrl);
2652 connect(job, &Akonadi::ContactSearchJob::result, this, &KMMainWidget::slotContactSearchJobForMessagePopupDone);
2653 } else {
2654 showMessagePopup(msg, aUrl, imageUrl, aPoint, false, false);
2658 void KMMainWidget::slotContactSearchJobForMessagePopupDone(KJob *job)
2660 const Akonadi::ContactSearchJob *searchJob = qobject_cast<Akonadi::ContactSearchJob *>(job);
2661 const bool contactAlreadyExists = !searchJob->contacts().isEmpty();
2663 const Akonadi::Item::List listContact = searchJob->items();
2664 const bool uniqueContactFound = (listContact.count() == 1);
2665 if (uniqueContactFound) {
2666 mMsgView->setContactItem(listContact.first(), searchJob->contacts().at(0));
2667 } else {
2668 mMsgView->clearContactItem();
2670 const Akonadi::Item msg = job->property("msg").value<Akonadi::Item>();
2671 const QPoint aPoint = job->property("point").toPoint();
2672 const QUrl imageUrl = job->property("imageUrl").toUrl();
2673 const QUrl url = job->property("url").toUrl();
2675 showMessagePopup(msg, url, imageUrl, aPoint, contactAlreadyExists, uniqueContactFound);
2678 void KMMainWidget::showMessagePopup(const Akonadi::Item &msg, const QUrl &url, const QUrl &imageUrl, const QPoint &aPoint, bool contactAlreadyExists, bool uniqueContactFound)
2680 QMenu *menu = new QMenu;
2682 bool urlMenuAdded = false;
2684 if (!url.isEmpty()) {
2685 if (url.scheme() == QLatin1String("mailto")) {
2686 // popup on a mailto URL
2687 menu->addAction(mMsgView->mailToComposeAction());
2688 menu->addAction(mMsgView->mailToReplyAction());
2689 menu->addAction(mMsgView->mailToForwardAction());
2691 menu->addSeparator();
2693 if (contactAlreadyExists) {
2694 if (uniqueContactFound) {
2695 menu->addAction(mMsgView->editContactAction());
2696 } else {
2697 menu->addAction(mMsgView->openAddrBookAction());
2699 } else {
2700 menu->addAction(mMsgView->addAddrBookAction());
2701 menu->addAction(mMsgView->addToExistingContactAction());
2703 menu->addSeparator();
2704 menu->addMenu(mMsgView->viewHtmlOption());
2705 menu->addSeparator();
2706 menu->addAction(mMsgView->copyURLAction());
2707 urlMenuAdded = true;
2708 } else if (url.scheme() != QLatin1String("attachment")) {
2709 // popup on a not-mailto URL
2710 menu->addAction(mMsgView->urlOpenAction());
2711 menu->addAction(mMsgView->addBookmarksAction());
2712 menu->addAction(mMsgView->urlSaveAsAction());
2713 menu->addAction(mMsgView->copyURLAction());
2714 menu->addSeparator();
2715 menu->addAction(mMsgView->shareServiceUrlMenu());
2716 menu->addActions(mMsgView->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedUrl));
2717 if (!imageUrl.isEmpty()) {
2718 menu->addSeparator();
2719 menu->addAction(mMsgView->copyImageLocation());
2720 menu->addAction(mMsgView->downloadImageToDiskAction());
2721 menu->addAction(mMsgView->shareImage());
2722 if (mMsgView->adblockEnabled()) {
2723 menu->addSeparator();
2724 menu->addAction(mMsgView->blockImage());
2727 urlMenuAdded = true;
2729 qCDebug(KMAIL_LOG) << "URL is:" << url;
2731 const QString selectedText = mMsgView ? mMsgView->copyText() : QString();
2732 if (mMsgView && !selectedText.isEmpty()) {
2733 if (urlMenuAdded) {
2734 menu->addSeparator();
2736 menu->addAction(mMsgActions->replyMenu());
2737 menu->addSeparator();
2739 menu->addAction(mMsgView->copyAction());
2740 menu->addAction(mMsgView->selectAllAction());
2741 menu->addSeparator();
2742 mMsgActions->addWebShortcutsMenu(menu, selectedText);
2743 menu->addSeparator();
2744 menu->addActions(mMsgView->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedSelection));
2745 if (KPIMTextEdit::TextToSpeech::self()->isReady()) {
2746 menu->addSeparator();
2747 menu->addAction(mMsgView->speakTextAction());
2749 } else if (!urlMenuAdded) {
2750 // popup somewhere else (i.e., not a URL) on the message
2751 if (!mMessagePane->currentMessage()) {
2752 // no messages
2753 delete menu;
2754 return;
2756 Akonadi::Collection parentCol = msg.parentCollection();
2757 if (parentCol.isValid() && CommonKernel->folderIsTemplates(parentCol)) {
2758 menu->addAction(mUseAction);
2759 } else {
2760 menu->addAction(mMsgActions->replyMenu());
2761 menu->addAction(mMsgActions->forwardMenu());
2763 if (parentCol.isValid() && CommonKernel->folderIsSentMailFolder(parentCol)) {
2764 menu->addAction(sendAgainAction());
2765 } else {
2766 menu->addAction(editAction());
2768 menu->addAction(mailingListActionMenu());
2769 menu->addSeparator();
2771 menu->addAction(mCopyActionMenu);
2772 menu->addAction(mMoveActionMenu);
2774 menu->addSeparator();
2776 menu->addAction(mMsgActions->messageStatusMenu());
2777 menu->addSeparator();
2778 if (mMsgView) {
2779 if (!imageUrl.isEmpty()) {
2780 menu->addSeparator();
2781 menu->addAction(mMsgView->copyImageLocation());
2782 menu->addAction(mMsgView->downloadImageToDiskAction());
2783 menu->addAction(mMsgView->shareImage());
2784 menu->addSeparator();
2785 if (mMsgView->adblockEnabled()) {
2786 menu->addAction(mMsgView->blockImage());
2787 menu->addSeparator();
2790 menu->addAction(mMsgView->viewSourceAction());
2791 menu->addAction(mMsgView->toggleFixFontAction());
2792 menu->addAction(mMsgView->toggleMimePartTreeAction());
2794 menu->addSeparator();
2795 if (mMsgActions->printPreviewAction()) {
2796 menu->addAction(mMsgActions->printPreviewAction());
2798 menu->addAction(mMsgActions->printAction());
2799 menu->addAction(mSaveAsAction);
2800 menu->addAction(mSaveAttachmentsAction);
2801 menu->addSeparator();
2802 if (parentCol.isValid() && CommonKernel->folderIsTrash(parentCol)) {
2803 menu->addAction(mDeleteAction);
2804 } else {
2805 menu->addAction(akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash));
2807 menu->addSeparator();
2809 if (mMsgView) {
2810 menu->addActions(mMsgView->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedMessage));
2811 menu->addSeparator();
2812 menu->addAction(mMsgView->saveMessageDisplayFormatAction());
2813 menu->addAction(mMsgView->resetMessageDisplayFormatAction());
2814 menu->addSeparator();
2816 menu->addAction(mMsgActions->annotateAction());
2818 if (mMsgView && mMsgView->adblockEnabled()) {
2819 menu->addSeparator();
2820 menu->addAction(mMsgView->openBlockableItems());
2822 menu->addSeparator();
2823 menu->addAction(mMsgActions->addFollowupReminderAction());
2824 if (kmkernel->allowToDebugBalooSupport()) {
2825 menu->addSeparator();
2826 menu->addAction(mMsgActions->debugBalooAction());
2829 KAcceleratorManager::manage(menu);
2830 menu->exec(aPoint, Q_NULLPTR);
2831 delete menu;
2834 void KMMainWidget::setupActions()
2836 mPluginInterface->setParentWidget(this);
2837 mPluginInterface->createPluginInterface();
2838 mMsgActions = new KMail::MessageActions(actionCollection(), this);
2839 mMsgActions->setMessageView(mMsgView);
2841 //----- File Menu
2842 mSaveAsAction = new QAction(QIcon::fromTheme(QStringLiteral("document-save")), i18n("Save &As..."), this);
2843 actionCollection()->addAction(QStringLiteral("file_save_as"), mSaveAsAction);
2844 connect(mSaveAsAction, &QAction::triggered, this, &KMMainWidget::slotSaveMsg);
2845 actionCollection()->setDefaultShortcut(mSaveAsAction, KStandardShortcut::save().first());
2847 mOpenAction = KStandardAction::open(this, SLOT(slotOpenMsg()),
2848 actionCollection());
2850 mOpenRecentAction = KStandardAction::openRecent(this, SLOT(slotOpenRecentMsg(QUrl)),
2851 actionCollection());
2852 KConfigGroup grp = mConfig->group(QStringLiteral("Recent Files"));
2853 mOpenRecentAction->loadEntries(grp);
2856 QAction *action = new QAction(i18n("&Expire All Folders"), this);
2857 actionCollection()->addAction(QStringLiteral("expire_all_folders"), action);
2858 connect(action, &QAction::triggered, this, &KMMainWidget::slotExpireAll);
2861 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("mail-receive")), i18n("Check &Mail"), this);
2862 actionCollection()->addAction(QStringLiteral("check_mail"), action);
2863 connect(action, &QAction::triggered, this, &KMMainWidget::slotCheckMail);
2864 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_L));
2867 mAccountActionMenu = new KActionMenuAccount(this);
2868 mAccountActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("mail-receive")));
2869 mAccountActionMenu->setText(i18n("Check Mail In"));
2871 mAccountActionMenu->setIconText(i18n("Check Mail"));
2872 mAccountActionMenu->setToolTip(i18n("Check Mail"));
2873 actionCollection()->addAction(QStringLiteral("check_mail_in"), mAccountActionMenu);
2874 connect(mAccountActionMenu, &KActionMenu::triggered, this, &KMMainWidget::slotCheckMail);
2876 mSendQueued = new QAction(QIcon::fromTheme(QStringLiteral("mail-send")), i18n("&Send Queued Messages"), this);
2877 actionCollection()->addAction(QStringLiteral("send_queued"), mSendQueued);
2878 connect(mSendQueued, &QAction::triggered, this, &KMMainWidget::slotSendQueued);
2881 QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::ToggleWorkOffline);
2882 mAkonadiStandardActionManager->interceptAction(Akonadi::StandardActionManager::ToggleWorkOffline);
2883 action->setCheckable(false);
2884 connect(action, &QAction::triggered, this, &KMMainWidget::slotOnlineStatus);
2885 action->setText(i18n("Online status (unknown)"));
2888 mSendActionMenu = new KActionMenuTransport(this);
2889 mSendActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("mail-send-via")));
2890 mSendActionMenu->setText(i18n("Send Queued Messages Via"));
2891 actionCollection()->addAction(QStringLiteral("send_queued_via"), mSendActionMenu);
2893 connect(mSendActionMenu, &KActionMenuTransport::transportSelected, this, &KMMainWidget::slotSendQueuedVia);
2895 //----- Tools menu
2896 if (parent()->inherits("KMMainWin")) {
2897 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("x-office-address-book")), i18n("&Address Book"), this);
2898 actionCollection()->addAction(QStringLiteral("addressbook"), action);
2899 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotAddrBook);
2900 if (QStandardPaths::findExecutable(QStringLiteral("kaddressbook")).isEmpty()) {
2901 action->setEnabled(false);
2906 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("pgp-keys")), i18n("Certificate Manager"), this);
2907 actionCollection()->addAction(QStringLiteral("tools_start_certman"), action);
2908 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotStartCertManager);
2909 // disable action if no certman binary is around
2910 if (QStandardPaths::findExecutable(QStringLiteral("kleopatra")).isEmpty()) {
2911 action->setEnabled(false);
2915 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("pgp-keys")), i18n("GnuPG Log Viewer"), this);
2916 actionCollection()->addAction(QStringLiteral("tools_start_kwatchgnupg"), action);
2917 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotStartWatchGnuPG);
2918 #ifdef Q_OS_WIN32
2919 // not ported yet, underlying infrastructure missing on Windows
2920 const bool usableKWatchGnupg = false;
2921 #else
2922 // disable action if no kwatchgnupg binary is around
2923 bool usableKWatchGnupg = !QStandardPaths::findExecutable(QStringLiteral("kwatchgnupg")).isEmpty();
2924 #endif
2925 action->setEnabled(usableKWatchGnupg);
2928 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("document-import")), i18n("&Import Messages..."), this);
2929 actionCollection()->addAction(QStringLiteral("import"), action);
2930 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotImport);
2931 if (QStandardPaths::findExecutable(QStringLiteral("importwizard")).isEmpty()) {
2932 action->setEnabled(false);
2936 #if !defined(NDEBUG)
2938 QAction *action = new QAction(i18n("&Debug Sieve..."), this);
2939 actionCollection()->addAction(QStringLiteral("tools_debug_sieve"), action);
2940 connect(action, &QAction::triggered, this, &KMMainWidget::slotDebugSieve);
2942 #endif
2945 QAction *action = new QAction(i18n("Filter &Log Viewer..."), this);
2946 actionCollection()->addAction(QStringLiteral("filter_log_viewer"), action);
2947 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotFilterLogViewer);
2950 QAction *action = new QAction(i18n("&Account Wizard..."), this);
2951 actionCollection()->addAction(QStringLiteral("accountWizard"), action);
2952 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotAccountWizard);
2955 QAction *action = new QAction(i18n("&Import from another Email Client..."), this);
2956 actionCollection()->addAction(QStringLiteral("importWizard"), action);
2957 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotImportWizard);
2959 if (KSieveUi::Util::allowOutOfOfficeSettings()) {
2960 QAction *action = new QAction(i18n("Edit \"Out of Office\" Replies..."), this);
2961 actionCollection()->addAction(QStringLiteral("tools_edit_vacation"), action);
2962 connect(action, SIGNAL(triggered(bool)), SLOT(slotEditVacation()));
2966 QAction *action = new QAction(i18n("&Configure Automatic Archiving..."), this);
2967 actionCollection()->addAction(QStringLiteral("tools_automatic_archiving"), action);
2968 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotConfigureAutomaticArchiving);
2972 QAction *action = new QAction(i18n("Delayed Messages..."), this);
2973 actionCollection()->addAction(QStringLiteral("message_delayed"), action);
2974 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotConfigureSendLater);
2978 QAction *action = new QAction(i18n("Followup Reminder Messages..."), this);
2979 actionCollection()->addAction(QStringLiteral("followup_reminder_messages"), action);
2980 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotConfigureFollowupReminder);
2983 // Disable the standard action delete key sortcut.
2984 QAction *const standardDelAction = akonadiStandardAction(Akonadi::StandardActionManager::DeleteItems);
2985 standardDelAction->setShortcut(QKeySequence());
2987 //----- Edit Menu
2989 /* The delete action is nowhere in the gui, by default, so we need to make
2990 * sure it is plugged into the KAccel now, since that won't happen on
2991 * XMLGui construction or manual ->plug(). This is only a problem when run
2992 * as a part, though. */
2993 mDeleteAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action Hard delete, bypassing trash", "&Delete"), this);
2994 actionCollection()->addAction(QStringLiteral("delete"), mDeleteAction);
2995 connect(mDeleteAction, &QAction::triggered, this, &KMMainWidget::slotDeleteMessages);
2996 actionCollection()->setDefaultShortcut(mDeleteAction, QKeySequence(Qt::SHIFT + Qt::Key_Delete));
2998 mTrashThreadAction = new QAction(i18n("M&ove Thread to Trash"), this);
2999 actionCollection()->addAction(QStringLiteral("move_thread_to_trash"), mTrashThreadAction);
3000 actionCollection()->setDefaultShortcut(mTrashThreadAction, QKeySequence(Qt::CTRL + Qt::Key_Delete));
3001 mTrashThreadAction->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
3002 KMail::Util::addQActionHelpText(mTrashThreadAction, i18n("Move thread to trashcan"));
3003 connect(mTrashThreadAction, &QAction::triggered, this, &KMMainWidget::slotTrashThread);
3005 mDeleteThreadAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete T&hread"), this);
3006 actionCollection()->addAction(QStringLiteral("delete_thread"), mDeleteThreadAction);
3007 //Don't use new connect api.
3008 connect(mDeleteThreadAction, SIGNAL(triggered(bool)), this, SLOT(slotDeleteThread()));
3009 actionCollection()->setDefaultShortcut(mDeleteThreadAction, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Delete));
3011 mSearchMessages = new QAction(QIcon::fromTheme(QStringLiteral("edit-find-mail")), i18n("&Find Messages..."), this);
3012 actionCollection()->addAction(QStringLiteral("search_messages"), mSearchMessages);
3013 connect(mSearchMessages, &QAction::triggered, this, &KMMainWidget::slotRequestFullSearchFromQuickSearch);
3014 actionCollection()->setDefaultShortcut(mSearchMessages, QKeySequence(Qt::Key_S));
3017 QAction *action = new QAction(i18n("Select &All Messages"), this);
3018 actionCollection()->addAction(QStringLiteral("mark_all_messages"), action);
3019 connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectAllMessages);
3020 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_A));
3023 //----- Folder Menu
3025 mFolderMailingListPropertiesAction = new QAction(i18n("&Mailing List Management..."), this);
3026 actionCollection()->addAction(QStringLiteral("folder_mailinglist_properties"), mFolderMailingListPropertiesAction);
3027 connect(mFolderMailingListPropertiesAction, &QAction::triggered, mManageShowCollectionProperties, &ManageShowCollectionProperties::slotFolderMailingListProperties);
3028 // mFolderMailingListPropertiesAction->setIcon(QIcon::fromTheme("document-properties-mailing-list"));
3030 mShowFolderShortcutDialogAction = new QAction(QIcon::fromTheme(QStringLiteral("configure-shortcuts")), i18n("&Assign Shortcut..."), this);
3031 actionCollection()->addAction(QStringLiteral("folder_shortcut_command"), mShowFolderShortcutDialogAction);
3032 connect(mShowFolderShortcutDialogAction, &QAction::triggered, mManageShowCollectionProperties,
3033 &ManageShowCollectionProperties::slotShowFolderShortcutDialog);
3034 // FIXME: this action is not currently enabled in the rc file, but even if
3035 // it were there is inconsistency between the action name and action.
3036 // "Expiration Settings" implies that this will lead to a settings dialogue
3037 // and it should be followed by a "...", but slotExpireFolder() performs
3038 // an immediate expiry.
3040 // TODO: expire action should be disabled if there is no content or if
3041 // the folder can't delete messages.
3043 // Leaving the action here for the moment, it and the "Expire" option in the
3044 // folder popup menu should be combined or at least made consistent. Same for
3045 // slotExpireFolder() and FolderViewItem::slotShowExpiryProperties().
3046 mExpireFolderAction = new QAction(i18n("&Expiration Settings"), this);
3047 actionCollection()->addAction(QStringLiteral("expire"), mExpireFolderAction);
3048 connect(mExpireFolderAction, &QAction::triggered, this, &KMMainWidget::slotExpireFolder);
3050 mAkonadiStandardActionManager->interceptAction(Akonadi::StandardMailActionManager::MoveToTrash);
3051 connect(mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveToTrash), &QAction::triggered, this, &KMMainWidget::slotTrashSelectedMessages);
3053 mAkonadiStandardActionManager->interceptAction(Akonadi::StandardMailActionManager::MoveAllToTrash);
3054 connect(mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash), &QAction::triggered, this, &KMMainWidget::slotEmptyFolder);
3056 mAkonadiStandardActionManager->interceptAction(Akonadi::StandardActionManager::DeleteCollections);
3057 connect(mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections), &QAction::triggered, this, &KMMainWidget::slotRemoveFolder);
3059 // ### PORT ME: Add this to the context menu. Not possible right now because
3060 // the context menu uses XMLGUI, and that would add the entry to
3061 // all collection context menus
3062 mArchiveFolderAction = new QAction(i18n("&Archive Folder..."), this);
3063 actionCollection()->addAction(QStringLiteral("archive_folder"), mArchiveFolderAction);
3064 connect(mArchiveFolderAction, &QAction::triggered, this, &KMMainWidget::slotArchiveFolder);
3066 mDisplayMessageFormatMenu = new DisplayMessageFormatActionMenu(this);
3067 connect(mDisplayMessageFormatMenu, &DisplayMessageFormatActionMenu::changeDisplayMessageFormat, this, &KMMainWidget::slotChangeDisplayMessageFormat);
3068 actionCollection()->addAction(QStringLiteral("display_format_message"), mDisplayMessageFormatMenu);
3070 mPreferHtmlLoadExtAction = new KToggleAction(i18n("Load E&xternal References"), this);
3071 actionCollection()->addAction(QStringLiteral("prefer_html_external_refs"), mPreferHtmlLoadExtAction);
3072 connect(mPreferHtmlLoadExtAction, &KToggleAction::triggered, this, &KMMainWidget::slotOverrideHtmlLoadExt);
3075 QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyCollections);
3076 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_C));
3079 QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::Paste);
3080 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_V));
3083 QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyItems);
3084 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::CTRL + Qt::Key_C));
3087 QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CutItems);
3088 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::CTRL + Qt::Key_X));
3092 QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyItemToMenu);
3093 action->setText(i18n("Copy Message To..."));
3094 action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::MoveItemToMenu);
3095 action->setText(i18n("Move Message To..."));
3098 //----- Message Menu
3100 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new")), i18n("&New Message..."), this);
3101 actionCollection()->addAction(QStringLiteral("new_message"), action);
3102 action->setIconText(i18nc("@action:intoolbar New Empty Message", "New"));
3103 connect(action, &QAction::triggered, this, &KMMainWidget::slotCompose);
3104 // do not set a New shortcut if kmail is a component
3105 if (!kmkernel->xmlGuiInstanceName().isEmpty()) {
3106 actionCollection()->setDefaultShortcut(action, KStandardShortcut::openNew().first());
3110 mTemplateMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Message From &Template"),
3111 actionCollection());
3112 mTemplateMenu->setDelayed(true);
3113 actionCollection()->addAction(QStringLiteral("new_from_template"), mTemplateMenu);
3114 connect(mTemplateMenu->menu(), &QMenu::aboutToShow, this,
3115 &KMMainWidget::slotShowNewFromTemplate);
3116 connect(mTemplateMenu->menu(), &QMenu::triggered, this,
3117 &KMMainWidget::slotNewFromTemplate);
3119 mMessageNewList = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new-list")),
3120 i18n("New Message t&o Mailing-List..."),
3121 this);
3122 actionCollection()->addAction(QStringLiteral("post_message"), mMessageNewList);
3123 connect(mMessageNewList, &QAction::triggered,
3124 this, &KMMainWidget::slotPostToML);
3125 actionCollection()->setDefaultShortcut(mMessageNewList, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_N));
3127 mSendAgainAction = new QAction(i18n("Send A&gain..."), this);
3128 actionCollection()->addAction(QStringLiteral("send_again"), mSendAgainAction);
3129 connect(mSendAgainAction, &QAction::triggered, this, &KMMainWidget::slotResendMsg);
3131 //----- Create filter actions
3132 mFilterMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("&Create Filter"), this);
3133 actionCollection()->addAction(QStringLiteral("create_filter"), mFilterMenu);
3134 connect(mFilterMenu, &QAction::triggered, this,
3135 &KMMainWidget::slotFilter);
3137 QAction *action = new QAction(i18n("Filter on &Subject..."), this);
3138 actionCollection()->addAction(QStringLiteral("subject_filter"), action);
3139 connect(action, &QAction::triggered, this, &KMMainWidget::slotSubjectFilter);
3140 mFilterMenu->addAction(action);
3144 QAction *action = new QAction(i18n("Filter on &From..."), this);
3145 actionCollection()->addAction(QStringLiteral("from_filter"), action);
3146 connect(action, &QAction::triggered, this, &KMMainWidget::slotFromFilter);
3147 mFilterMenu->addAction(action);
3150 QAction *action = new QAction(i18n("Filter on &To..."), this);
3151 actionCollection()->addAction(QStringLiteral("to_filter"), action);
3152 connect(action, &QAction::triggered, this, &KMMainWidget::slotToFilter);
3153 mFilterMenu->addAction(action);
3156 QAction *action = new QAction(i18n("Filter on &Cc..."), this);
3157 actionCollection()->addAction(QStringLiteral("cc_filter"), action);
3158 connect(action, &QAction::triggered, this, &KMMainWidget::slotCcFilter);
3159 mFilterMenu->addAction(action);
3161 mFilterMenu->addAction(mMsgActions->listFilterAction());
3163 mUseAction = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New Message From &Template"), this);
3164 actionCollection()->addAction(QStringLiteral("use_template"), mUseAction);
3165 connect(mUseAction, &QAction::triggered, this, &KMMainWidget::slotUseTemplate);
3166 actionCollection()->setDefaultShortcut(mUseAction, QKeySequence(Qt::SHIFT + Qt::Key_N));
3168 //----- "Mark Thread" submenu
3169 mThreadStatusMenu = new KActionMenu(i18n("Mark &Thread"), this);
3170 actionCollection()->addAction(QStringLiteral("thread_status"), mThreadStatusMenu);
3172 mMarkThreadAsReadAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-mark-read")), i18n("Mark Thread as &Read"), this);
3173 actionCollection()->addAction(QStringLiteral("thread_read"), mMarkThreadAsReadAction);
3174 connect(mMarkThreadAsReadAction, &QAction::triggered, this, &KMMainWidget::slotSetThreadStatusRead);
3175 KMail::Util::addQActionHelpText(mMarkThreadAsReadAction, i18n("Mark all messages in the selected thread as read"));
3176 mThreadStatusMenu->addAction(mMarkThreadAsReadAction);
3178 mMarkThreadAsUnreadAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-mark-unread")), i18n("Mark Thread as &Unread"), this);
3179 actionCollection()->addAction(QStringLiteral("thread_unread"), mMarkThreadAsUnreadAction);
3180 connect(mMarkThreadAsUnreadAction, &QAction::triggered, this, &KMMainWidget::slotSetThreadStatusUnread);
3181 KMail::Util::addQActionHelpText(mMarkThreadAsUnreadAction, i18n("Mark all messages in the selected thread as unread"));
3182 mThreadStatusMenu->addAction(mMarkThreadAsUnreadAction);
3184 mThreadStatusMenu->addSeparator();
3186 //----- "Mark Thread" toggle actions
3187 mToggleThreadImportantAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-mark-important")), i18n("Mark Thread as &Important"), this);
3188 actionCollection()->addAction(QStringLiteral("thread_flag"), mToggleThreadImportantAction);
3189 connect(mToggleThreadImportantAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusImportant);
3190 mToggleThreadImportantAction->setCheckedState(KGuiItem(i18n("Remove &Important Thread Mark")));
3191 mThreadStatusMenu->addAction(mToggleThreadImportantAction);
3193 mToggleThreadToActAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-mark-task")), i18n("Mark Thread as &Action Item"), this);
3194 actionCollection()->addAction(QStringLiteral("thread_toact"), mToggleThreadToActAction);
3195 connect(mToggleThreadToActAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusToAct);
3196 mToggleThreadToActAction->setCheckedState(KGuiItem(i18n("Remove &Action Item Thread Mark")));
3197 mThreadStatusMenu->addAction(mToggleThreadToActAction);
3199 //------- "Watch and ignore thread" actions
3200 mWatchThreadAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-thread-watch")), i18n("&Watch Thread"), this);
3201 actionCollection()->addAction(QStringLiteral("thread_watched"), mWatchThreadAction);
3202 connect(mWatchThreadAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusWatched);
3204 mIgnoreThreadAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-thread-ignored")), i18n("&Ignore Thread"), this);
3205 actionCollection()->addAction(QStringLiteral("thread_ignored"), mIgnoreThreadAction);
3206 connect(mIgnoreThreadAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusIgnored);
3208 mThreadStatusMenu->addSeparator();
3209 mThreadStatusMenu->addAction(mWatchThreadAction);
3210 mThreadStatusMenu->addAction(mIgnoreThreadAction);
3212 mSaveAttachmentsAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("Save A&ttachments..."), this);
3213 actionCollection()->addAction(QStringLiteral("file_save_attachments"), mSaveAttachmentsAction);
3214 connect(mSaveAttachmentsAction, &QAction::triggered, this, &KMMainWidget::slotSaveAttachments);
3216 mMoveActionMenu = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::MoveItemToMenu);
3218 mCopyActionMenu = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyItemToMenu);
3220 mApplyAllFiltersAction =
3221 new QAction(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("Appl&y All Filters"), this);
3222 actionCollection()->addAction(QStringLiteral("apply_filters"), mApplyAllFiltersAction);
3223 connect(mApplyAllFiltersAction, &QAction::triggered,
3224 this, &KMMainWidget::slotApplyFilters);
3225 actionCollection()->setDefaultShortcut(mApplyAllFiltersAction, QKeySequence(Qt::CTRL + Qt::Key_J));
3227 mApplyFilterActionsMenu = new KActionMenu(i18n("A&pply Filter"), this);
3228 actionCollection()->addAction(QStringLiteral("apply_filter_actions"), mApplyFilterActionsMenu);
3231 QAction *action = new QAction(i18nc("View->", "&Expand Thread / Group"), this);
3232 actionCollection()->addAction(QStringLiteral("expand_thread"), action);
3233 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Period));
3234 KMail::Util::addQActionHelpText(action, i18n("Expand the current thread or group"));
3235 connect(action, &QAction::triggered, this, &KMMainWidget::slotExpandThread);
3238 QAction *action = new QAction(i18nc("View->", "&Collapse Thread / Group"), this);
3239 actionCollection()->addAction(QStringLiteral("collapse_thread"), action);
3240 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Comma));
3241 KMail::Util::addQActionHelpText(action, i18n("Collapse the current thread or group"));
3242 connect(action, &QAction::triggered, this, &KMMainWidget::slotCollapseThread);
3245 QAction *action = new QAction(i18nc("View->", "Ex&pand All Threads"), this);
3246 actionCollection()->addAction(QStringLiteral("expand_all_threads"), action);
3247 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Period));
3248 KMail::Util::addQActionHelpText(action, i18n("Expand all threads in the current folder"));
3249 connect(action, &QAction::triggered, this, &KMMainWidget::slotExpandAllThreads);
3252 QAction *action = new QAction(i18nc("View->", "C&ollapse All Threads"), this);
3253 actionCollection()->addAction(QStringLiteral("collapse_all_threads"), action);
3254 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Comma));
3255 KMail::Util::addQActionHelpText(action, i18n("Collapse all threads in the current folder"));
3256 connect(action, &QAction::triggered, this, &KMMainWidget::slotCollapseAllThreads);
3259 QAction *dukeOfMonmoth = new QAction(i18n("&Display Message"), this);
3260 actionCollection()->addAction(QStringLiteral("display_message"), dukeOfMonmoth);
3261 connect(dukeOfMonmoth, &QAction::triggered, this, &KMMainWidget::slotDisplayCurrentMessage);
3262 QList<QKeySequence> shortcuts;
3263 shortcuts << QKeySequence(Qt::Key_Enter) << QKeySequence(Qt::Key_Return);
3264 actionCollection()->setDefaultShortcuts(dukeOfMonmoth, shortcuts);
3266 //----- Go Menu
3268 QAction *action = new QAction(i18n("&Next Message"), this);
3269 actionCollection()->addAction(QStringLiteral("go_next_message"), action);
3270 actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("N; Right")));
3271 KMail::Util::addQActionHelpText(action, i18n("Go to the next message"));
3272 connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectNextMessage);
3275 QAction *action = new QAction(i18n("Next &Unread Message"), this);
3276 actionCollection()->addAction(QStringLiteral("go_next_unread_message"), action);
3277 actionCollection()->setDefaultShortcuts(action, QList<QKeySequence>() << QKeySequence(Qt::Key_Plus) << QKeySequence(Qt::Key_Plus + Qt::KeypadModifier));
3278 if (QApplication::isRightToLeft()) {
3279 action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
3280 } else {
3281 action->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
3283 action->setIconText(i18nc("@action:inmenu Goto next unread message", "Next"));
3284 KMail::Util::addQActionHelpText(action, i18n("Go to the next unread message"));
3285 connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectNextUnreadMessage);
3288 QAction *action = new QAction(i18n("&Previous Message"), this);
3289 actionCollection()->addAction(QStringLiteral("go_prev_message"), action);
3290 KMail::Util::addQActionHelpText(action, i18n("Go to the previous message"));
3291 actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("P; Left")));
3292 connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectPreviousMessage);
3295 QAction *action = new QAction(i18n("Previous Unread &Message"), this);
3296 actionCollection()->addAction(QStringLiteral("go_prev_unread_message"), action);
3297 actionCollection()->setDefaultShortcuts(action, QList<QKeySequence>() << QKeySequence(Qt::Key_Minus) << QKeySequence(Qt::Key_Minus + Qt::KeypadModifier));
3298 if (QApplication::isRightToLeft()) {
3299 action->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
3300 } else {
3301 action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
3303 action->setIconText(i18nc("@action:inmenu Goto previous unread message.", "Previous"));
3304 KMail::Util::addQActionHelpText(action, i18n("Go to the previous unread message"));
3305 connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectPreviousUnreadMessage);
3308 QAction *action = new QAction(i18n("Next Unread &Folder"), this);
3309 actionCollection()->addAction(QStringLiteral("go_next_unread_folder"), action);
3310 connect(action, &QAction::triggered, this, &KMMainWidget::slotNextUnreadFolder);
3311 actionCollection()->setDefaultShortcuts(action, QList<QKeySequence>() << QKeySequence(Qt::ALT + Qt::Key_Plus) << QKeySequence(Qt::ALT + Qt::Key_Plus + Qt::KeypadModifier));
3312 KMail::Util::addQActionHelpText(action, i18n("Go to the next folder with unread messages"));
3315 QAction *action = new QAction(i18n("Previous Unread F&older"), this);
3316 actionCollection()->addAction(QStringLiteral("go_prev_unread_folder"), action);
3317 actionCollection()->setDefaultShortcuts(action, QList<QKeySequence>() << QKeySequence(Qt::ALT + Qt::Key_Minus) << QKeySequence(Qt::ALT + Qt::Key_Minus + Qt::KeypadModifier));
3318 KMail::Util::addQActionHelpText(action, i18n("Go to the previous folder with unread messages"));
3319 connect(action, &QAction::triggered, this, &KMMainWidget::slotPrevUnreadFolder);
3322 QAction *action = new QAction(i18nc("Go->", "Next Unread &Text"), this);
3323 actionCollection()->addAction(QStringLiteral("go_next_unread_text"), action);
3324 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Space));
3325 KMail::Util::addQActionHelpText(action, i18n("Go to the next unread text"));
3326 action->setWhatsThis(i18n("Scroll down current message. "
3327 "If at end of current message, "
3328 "go to next unread message."));
3329 connect(action, &QAction::triggered, this, &KMMainWidget::slotReadOn);
3332 //----- Settings Menu
3334 QAction *action = new QAction(i18n("Configure &Filters..."), this);
3335 action->setMenuRole(QAction::NoRole); // do not move to application menu on OS X
3336 actionCollection()->addAction(QStringLiteral("filter"), action);
3337 connect(action, &QAction::triggered, this, &KMMainWidget::slotFilter);
3340 QAction *action = new QAction(i18n("Manage &Sieve Scripts..."), this);
3341 actionCollection()->addAction(QStringLiteral("sieveFilters"), action);
3342 connect(action, &QAction::triggered, this, &KMMainWidget::slotManageSieveScripts);
3345 mShowIntroductionAction = new QAction(QIcon::fromTheme(QStringLiteral("kmail")), i18n("KMail &Introduction"), this);
3346 actionCollection()->addAction(QStringLiteral("help_kmail_welcomepage"), mShowIntroductionAction);
3347 KMail::Util::addQActionHelpText(mShowIntroductionAction, i18n("Display KMail's Welcome Page"));
3348 connect(mShowIntroductionAction, &QAction::triggered, this, &KMMainWidget::slotIntro);
3349 mShowIntroductionAction->setEnabled(mMsgView != Q_NULLPTR);
3352 // ----- Standard Actions
3354 // KStandardAction::configureNotifications(this, SLOT(slotEditNotifications()), actionCollection());
3356 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("preferences-desktop-notification")),
3357 i18n("Configure &Notifications..."), this);
3358 action->setMenuRole(QAction::NoRole); // do not move to application menu on OS X
3359 actionCollection()->addAction(QStringLiteral("kmail_configure_notifications"), action);
3360 connect(action, &QAction::triggered, this, &KMMainWidget::slotEditNotifications);
3364 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("&Configure KMail..."), this);
3365 action->setMenuRole(QAction::PreferencesRole); // this one should move to the application menu on OS X
3366 actionCollection()->addAction(QStringLiteral("kmail_configure_kmail"), action);
3367 connect(action, &QAction::triggered, kmkernel, &KMKernel::slotShowConfigurationDialog);
3371 mExpireConfigAction = new QAction(i18n("Expire..."), this);
3372 actionCollection()->addAction(QStringLiteral("expire_settings"), mExpireConfigAction);
3373 connect(mExpireConfigAction, &QAction::triggered, mManageShowCollectionProperties, &ManageShowCollectionProperties::slotShowExpiryProperties);
3377 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("Add Favorite Folder..."), this);
3378 actionCollection()->addAction(QStringLiteral("add_favorite_folder"), action);
3379 connect(action, &QAction::triggered, this, &KMMainWidget::slotAddFavoriteFolder);
3383 mServerSideSubscription = new QAction(QIcon::fromTheme(QStringLiteral("folder-bookmarks")), i18n("Serverside Subscription..."), this);
3384 actionCollection()->addAction(QStringLiteral("serverside_subscription"), mServerSideSubscription);
3385 connect(mServerSideSubscription, &QAction::triggered, this, &KMMainWidget::slotServerSideSubscription);
3389 mApplyFiltersOnFolder = new QAction(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("Appl&y All Filters On Folder"), this);
3390 actionCollection()->addAction(QStringLiteral("apply_filters_on_folder"), mApplyFiltersOnFolder);
3391 connect(mApplyFiltersOnFolder, &QAction::triggered,
3392 this, &KMMainWidget::slotApplyFiltersOnFolder);
3397 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("kmail")), i18n("Import/Export KMail Data..."), this);
3398 actionCollection()->addAction(QStringLiteral("kmail_export_data"), action);
3399 connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotExportData);
3403 QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("New AddressBook Contact..."), this);
3404 actionCollection()->addAction(QStringLiteral("kmail_new_addressbook_contact"), action);
3405 connect(action, &QAction::triggered, this, &KMMainWidget::slotCreateAddressBookContact);
3409 actionCollection()->addAction(KStandardAction::Undo, QStringLiteral("kmail_undo"), this, SLOT(slotUndo()));
3411 menutimer = new QTimer(this);
3412 menutimer->setObjectName(QStringLiteral("menutimer"));
3413 menutimer->setSingleShot(true);
3414 connect(menutimer, &QTimer::timeout, this, &KMMainWidget::updateMessageActionsDelayed);
3415 connect(kmkernel->undoStack(),
3416 &KMail::UndoStack::undoStackChanged, this, &KMMainWidget::slotUpdateUndo);
3418 updateMessageActions();
3419 updateFolderMenu();
3420 mTagActionManager = new KMail::TagActionManager(this, actionCollection(), mMsgActions,
3421 mGUIClient);
3422 mFolderShortcutActionManager = new KMail::FolderShortcutActionManager(this, actionCollection());
3425 QAction *action = new QAction(i18n("Copy Message to Folder"), this);
3426 actionCollection()->addAction(QStringLiteral("copy_message_to_folder"), action);
3427 connect(action, &QAction::triggered,
3428 this, &KMMainWidget::slotCopySelectedMessagesToFolder);
3429 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_C));
3432 QAction *action = new QAction(i18n("Jump to Folder..."), this);
3433 actionCollection()->addAction(QStringLiteral("jump_to_folder"), action);
3434 connect(action, &QAction::triggered,
3435 this, &KMMainWidget::slotJumpToFolder);
3436 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_J));
3439 QAction *action = new QAction(i18n("Abort Current Operation"), this);
3440 actionCollection()->addAction(QStringLiteral("cancel"), action);
3441 connect(action, &QAction::triggered,
3442 ProgressManager::instance(), &KPIM::ProgressManager::slotAbortAll);
3443 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Escape));
3446 QAction *action = new QAction(i18n("Focus on Next Folder"), this);
3447 actionCollection()->addAction(QStringLiteral("inc_current_folder"), action);
3448 connect(action, &QAction::triggered,
3449 mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusNextFolder);
3450 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Right));
3453 QAction *action = new QAction(i18n("Focus on Previous Folder"), this);
3454 actionCollection()->addAction(QStringLiteral("dec_current_folder"), action);
3455 connect(action, &QAction::triggered,
3456 mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusPrevFolder);
3457 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Left));
3460 QAction *action = new QAction(i18n("Select Folder with Focus"), this);
3461 actionCollection()->addAction(QStringLiteral("select_current_folder"), action);
3463 connect(action, &QAction::triggered,
3464 mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotSelectFocusFolder);
3465 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Space));
3468 QAction *action = new QAction(i18n("Focus on First Folder"), this);
3469 actionCollection()->addAction(QStringLiteral("focus_first_folder"), action);
3470 connect(action, &QAction::triggered,
3471 mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusFirstFolder);
3472 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Home));
3475 QAction *action = new QAction(i18n("Focus on Last Folder"), this);
3476 actionCollection()->addAction(QStringLiteral("focus_last_folder"), action);
3477 connect(action, &QAction::triggered,
3478 mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusLastFolder);
3479 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_End));
3482 QAction *action = new QAction(i18n("Focus on Next Message"), this);
3483 actionCollection()->addAction(QStringLiteral("inc_current_message"), action);
3484 connect(action, &QAction::triggered,
3485 this, &KMMainWidget::slotFocusOnNextMessage);
3486 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Right));
3489 QAction *action = new QAction(i18n("Focus on Previous Message"), this);
3490 actionCollection()->addAction(QStringLiteral("dec_current_message"), action);
3491 connect(action, &QAction::triggered,
3492 this, &KMMainWidget::slotFocusOnPrevMessage);
3493 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Left));
3496 QAction *action = new QAction(i18n("Select First Message"), this);
3497 actionCollection()->addAction(QStringLiteral("select_first_message"), action);
3498 connect(action, &QAction::triggered,
3499 this, &KMMainWidget::slotSelectFirstMessage);
3500 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Home));
3503 QAction *action = new QAction(i18n("Select Last Message"), this);
3504 actionCollection()->addAction(QStringLiteral("select_last_message"), action);
3505 connect(action, &QAction::triggered,
3506 this, &KMMainWidget::slotSelectLastMessage);
3507 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_End));
3510 QAction *action = new QAction(i18n("Select Message with Focus"), this);
3511 actionCollection()->addAction(QStringLiteral("select_current_message"), action);
3512 connect(action, &QAction::triggered,
3513 this, &KMMainWidget::slotSelectFocusedMessage);
3514 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Space));
3518 mQuickSearchAction = new QAction(i18n("Set Focus to Quick Search"), this);
3519 //If change shortcut change Panel::setQuickSearchClickMessage(...) message
3520 actionCollection()->setDefaultShortcut(mQuickSearchAction, QKeySequence(Qt::ALT + Qt::Key_Q));
3521 actionCollection()->addAction(QStringLiteral("focus_to_quickseach"), mQuickSearchAction);
3522 connect(mQuickSearchAction, &QAction::triggered,
3523 this, &KMMainWidget::slotFocusQuickSearch);
3524 updateQuickSearchLineText();
3527 QAction *action = new QAction(i18n("Extend Selection to Previous Message"), this);
3528 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::Key_Left));
3529 actionCollection()->addAction(QStringLiteral("previous_message"), action);
3530 connect(action, &QAction::triggered,
3531 this, &KMMainWidget::slotExtendSelectionToPreviousMessage);
3534 QAction *action = new QAction(i18n("Extend Selection to Next Message"), this);
3535 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::Key_Right));
3536 actionCollection()->addAction(QStringLiteral("next_message"), action);
3537 connect(action, &QAction::triggered,
3538 this, &KMMainWidget::slotExtendSelectionToNextMessage);
3542 mMoveMsgToFolderAction = new QAction(i18n("Move Message to Folder"), this);
3543 actionCollection()->setDefaultShortcut(mMoveMsgToFolderAction, QKeySequence(Qt::Key_M));
3544 actionCollection()->addAction(QStringLiteral("move_message_to_folder"), mMoveMsgToFolderAction);
3545 connect(mMoveMsgToFolderAction, &QAction::triggered,
3546 this, &KMMainWidget::slotMoveSelectedMessageToFolder);
3549 mArchiveAction = new QAction(i18nc("@action", "Archive"), this);
3550 actionCollection()->addAction(QStringLiteral("archive_mails"), mArchiveAction);
3551 connect(mArchiveAction, &QAction::triggered, this, &KMMainWidget::slotArchiveMails);
3553 mLowBandwithAction = new KToggleAction(i18n("Low Bandwidth"), this);
3554 actionCollection()->addAction(QStringLiteral("low_bandwidth"), mLowBandwithAction);
3555 connect(mLowBandwithAction, &KToggleAction::triggered, this, &KMMainWidget::slotBandwidth);
3557 mMarkAllMessageAsReadAndInAllSubFolder = new QAction(i18n("Mark All Messages As Read in This Folder and All its Subfolder"), this);
3558 mMarkAllMessageAsReadAndInAllSubFolder->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-read")));
3559 actionCollection()->addAction(QStringLiteral("markallmessagereadcurentfolderandsubfolder"), mMarkAllMessageAsReadAndInAllSubFolder);
3560 connect(mMarkAllMessageAsReadAndInAllSubFolder, &KToggleAction::triggered, this, &KMMainWidget::slotMarkAllMessageAsReadInCurrentFolderAndSubfolder);
3562 mRemoveDuplicateRecursiveAction = new QAction(i18n("Remove Duplicates in This Folder and All its Subfolder"), this);
3563 actionCollection()->addAction(QStringLiteral("remove_duplicate_recursive"), mRemoveDuplicateRecursiveAction);
3564 connect(mRemoveDuplicateRecursiveAction, &KToggleAction::triggered, this, &KMMainWidget::slotRemoveDuplicateRecursive);
3568 void KMMainWidget::slotAddFavoriteFolder()
3570 if (!mFavoritesModel) {
3571 return;
3573 QPointer<MailCommon::FolderSelectionDialog> dialog(selectFromAllFoldersDialog());
3574 dialog->setWindowTitle(i18n("Add Favorite Folder"));
3575 if (dialog->exec() && dialog) {
3576 const Akonadi::Collection collection = dialog->selectedCollection();
3577 if (collection.isValid()) {
3578 mFavoritesModel->addCollection(collection);
3583 //-----------------------------------------------------------------------------
3584 void KMMainWidget::slotEditNotifications()
3586 KMail::KMKnotify notifyDlg(this);
3587 notifyDlg.exec();
3590 //-----------------------------------------------------------------------------
3591 void KMMainWidget::slotReadOn()
3593 if (!mMsgView) {
3594 return;
3596 if (!mMsgView->viewer()->atBottom()) {
3597 mMsgView->viewer()->slotJumpDown();
3598 return;
3600 slotSelectNextUnreadMessage();
3603 void KMMainWidget::slotNextUnreadFolder()
3605 if (!mFolderTreeWidget) {
3606 return;
3608 mGoToFirstUnreadMessageInSelectedFolder = true;
3609 mFolderTreeWidget->folderTreeView()->selectNextUnreadFolder();
3610 mGoToFirstUnreadMessageInSelectedFolder = false;
3613 void KMMainWidget::slotPrevUnreadFolder()
3615 if (!mFolderTreeWidget) {
3616 return;
3618 mGoToFirstUnreadMessageInSelectedFolder = true;
3619 mFolderTreeWidget->folderTreeView()->selectPrevUnreadFolder();
3620 mGoToFirstUnreadMessageInSelectedFolder = false;
3623 void KMMainWidget::slotExpandThread()
3625 mMessagePane->setCurrentThreadExpanded(true);
3628 void KMMainWidget::slotCollapseThread()
3630 mMessagePane->setCurrentThreadExpanded(false);
3633 void KMMainWidget::slotExpandAllThreads()
3635 // TODO: Make this asynchronous ? (if there is enough demand)
3636 #ifndef QT_NO_CURSOR
3637 KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy());
3638 #endif
3639 mMessagePane->setAllThreadsExpanded(true);
3642 void KMMainWidget::slotCollapseAllThreads()
3644 // TODO: Make this asynchronous ? (if there is enough demand)
3645 #ifndef QT_NO_CURSOR
3646 KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy());
3647 #endif
3648 mMessagePane->setAllThreadsExpanded(false);
3651 //-----------------------------------------------------------------------------
3652 void KMMainWidget::updateMessageMenu()
3654 updateMessageActions();
3657 void KMMainWidget::startUpdateMessageActionsTimer()
3659 // FIXME: This delay effectively CAN make the actions to be in an incoherent state
3660 // Maybe we should mark actions as "dirty" here and check it in every action handler...
3661 updateMessageActions(true);
3663 menutimer->stop();
3664 menutimer->start(500);
3667 void KMMainWidget::updateMessageActions(bool fast)
3669 Akonadi::Item::List selectedItems;
3670 Akonadi::Item::List selectedVisibleItems;
3671 bool allSelectedBelongToSameThread = false;
3672 if (mCurrentFolder && mCurrentFolder->isValid() &&
3673 mMessagePane->getSelectionStats(selectedItems, selectedVisibleItems, &allSelectedBelongToSameThread)
3675 mMsgActions->setCurrentMessage(mMessagePane->currentItem(), selectedVisibleItems);
3676 } else {
3677 mMsgActions->setCurrentMessage(Akonadi::Item());
3680 if (!fast) {
3681 updateMessageActionsDelayed();
3686 void KMMainWidget::updateMessageActionsDelayed()
3688 int count;
3689 Akonadi::Item::List selectedItems;
3690 Akonadi::Item::List selectedVisibleItems;
3691 bool allSelectedBelongToSameThread = false;
3692 Akonadi::Item currentMessage;
3693 if (mCurrentFolder && mCurrentFolder->isValid() &&
3694 mMessagePane->getSelectionStats(selectedItems, selectedVisibleItems, &allSelectedBelongToSameThread)
3696 count = selectedItems.count();
3698 currentMessage = mMessagePane->currentItem();
3700 } else {
3701 count = 0;
3702 currentMessage = Akonadi::Item();
3705 mApplyFiltersOnFolder->setEnabled(mCurrentFolder && mCurrentFolder->isValid());
3708 // Here we have:
3710 // - A list of selected messages stored in selectedSernums.
3711 // The selected messages might contain some invisible ones as a selected
3712 // collapsed node "includes" all the children in the selection.
3713 // - A list of selected AND visible messages stored in selectedVisibleSernums.
3714 // This list does not contain children of selected and collapsed nodes.
3716 // Now, actions can operate on:
3717 // - Any set of messages
3718 // These are called "mass actions" and are enabled whenever we have a message selected.
3719 // In fact we should differentiate between actions that operate on visible selection
3720 // and those that operate on the selection as a whole (without considering visibility)...
3721 // - A single thread
3722 // These are called "thread actions" and are enabled whenever all the selected messages belong
3723 // to the same thread. If the selection doesn't cover the whole thread then the action
3724 // will act on the whole thread anyway (thus will silently extend the selection)
3725 // - A single message
3726 // And we have two sub-cases:
3727 // - The selection must contain exactly one message
3728 // These actions can't ignore the hidden messages and thus must be disabled if
3729 // the selection contains any.
3730 // - The selection must contain exactly one visible message
3731 // These actions will ignore the hidden message and thus can be enabled if
3732 // the selection contains any.
3735 bool readOnly = mCurrentFolder && mCurrentFolder->isValid() && (mCurrentFolder->rights() & Akonadi::Collection::ReadOnly);
3736 // can we apply strictly single message actions ? (this is false if the whole selection contains more than one message)
3737 bool single_actions = count == 1;
3738 // can we apply loosely single message actions ? (this is false if the VISIBLE selection contains more than one message)
3739 bool singleVisibleMessageSelected = selectedVisibleItems.count() == 1;
3740 // can we apply "mass" actions to the selection ? (this is actually always true if the selection is non-empty)
3741 bool mass_actions = count >= 1;
3742 // does the selection identify a single thread ?
3743 bool thread_actions = mass_actions && allSelectedBelongToSameThread && mMessagePane->isThreaded();
3744 // can we apply flags to the selected messages ?
3745 bool flags_available = KMailSettings::self()->allowLocalFlags() || !(mCurrentFolder && mCurrentFolder->isValid() ? readOnly : true);
3747 mThreadStatusMenu->setEnabled(thread_actions);
3748 // these need to be handled individually, the user might have them
3749 // in the toolbar
3750 mWatchThreadAction->setEnabled(thread_actions && flags_available);
3751 mIgnoreThreadAction->setEnabled(thread_actions && flags_available);
3752 mMarkThreadAsReadAction->setEnabled(thread_actions);
3753 mMarkThreadAsUnreadAction->setEnabled(thread_actions);
3754 mToggleThreadToActAction->setEnabled(thread_actions && flags_available);
3755 mToggleThreadImportantAction->setEnabled(thread_actions && flags_available);
3756 bool canDeleteMessages = mCurrentFolder && mCurrentFolder->isValid() && (mCurrentFolder->rights() & Akonadi::Collection::CanDeleteItem);
3758 mTrashThreadAction->setEnabled(thread_actions && canDeleteMessages);
3759 mDeleteThreadAction->setEnabled(thread_actions && canDeleteMessages);
3760 if (messageView() && messageView()->viewer() && messageView()->viewer()->headerStylePlugin()) {
3761 messageView()->viewer()->headerStylePlugin()->headerStyle()->setReadOnlyMessage(!canDeleteMessages);
3764 if (currentMessage.isValid()) {
3765 MessageStatus status;
3766 status.setStatusFromFlags(currentMessage.flags());
3767 mTagActionManager->updateActionStates(count, mMessagePane->currentItem());
3768 if (thread_actions) {
3769 mToggleThreadToActAction->setChecked(status.isToAct());
3770 mToggleThreadImportantAction->setChecked(status.isImportant());
3771 mWatchThreadAction->setChecked(status.isWatched());
3772 mIgnoreThreadAction->setChecked(status.isIgnored());
3776 mMoveActionMenu->setEnabled(mass_actions && canDeleteMessages);
3777 if (mMoveMsgToFolderAction) {
3778 mMoveMsgToFolderAction->setEnabled(mass_actions && canDeleteMessages);
3780 //mCopyActionMenu->setEnabled( mass_actions );
3782 mDeleteAction->setEnabled(mass_actions && canDeleteMessages);
3784 mExpireConfigAction->setEnabled(canDeleteMessages);
3786 if (mMsgView) {
3787 mMsgView->findInMessageAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentFolder->collection()));
3789 mMsgActions->forwardInlineAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentFolder->collection()));
3790 mMsgActions->forwardAttachedAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentFolder->collection()));
3791 mMsgActions->forwardMenu()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentFolder->collection()));
3793 mMsgActions->editAction()->setEnabled(single_actions);
3794 mUseAction->setEnabled(single_actions && CommonKernel->folderIsTemplates(mCurrentFolder->collection()));
3795 filterMenu()->setEnabled(single_actions);
3796 mMsgActions->redirectAction()->setEnabled(/*single_actions &&*/mass_actions && !CommonKernel->folderIsTemplates(mCurrentFolder->collection()));
3798 if (mMsgActions->customTemplatesMenu()) {
3799 mMsgActions->customTemplatesMenu()->forwardActionMenu()->setEnabled(mass_actions);
3800 mMsgActions->customTemplatesMenu()->replyActionMenu()->setEnabled(single_actions);
3801 mMsgActions->customTemplatesMenu()->replyAllActionMenu()->setEnabled(single_actions);
3804 // "Print" will act on the current message: it will ignore any hidden selection
3805 mMsgActions->printAction()->setEnabled(singleVisibleMessageSelected);
3806 // "Print preview" will act on the current message: it will ignore any hidden selection
3807 QAction *printPreviewAction = mMsgActions->printPreviewAction();
3808 if (printPreviewAction) {
3809 printPreviewAction->setEnabled(singleVisibleMessageSelected);
3812 // "View Source" will act on the current message: it will ignore any hidden selection
3813 if (mMsgView) {
3814 mMsgView->viewSourceAction()->setEnabled(singleVisibleMessageSelected);
3816 MessageStatus status;
3817 status.setStatusFromFlags(currentMessage.flags());
3819 QList< QAction *> actionList;
3820 bool statusSendAgain = single_actions && ((currentMessage.isValid() && status.isSent()) || (currentMessage.isValid() && CommonKernel->folderIsSentMailFolder(mCurrentFolder->collection())));
3821 if (statusSendAgain) {
3822 actionList << mSendAgainAction;
3823 } else if (single_actions) {
3824 actionList << messageActions()->editAction();
3826 actionList << mSaveAttachmentsAction;
3827 if (mCurrentFolder && FolderArchive::FolderArchiveUtil::resourceSupportArchiving(mCurrentFolder->collection().resource())) {
3828 actionList << mArchiveAction;
3830 mGUIClient->unplugActionList(QStringLiteral("messagelist_actionlist"));
3831 mGUIClient->plugActionList(QStringLiteral("messagelist_actionlist"), actionList);
3832 mSendAgainAction->setEnabled(statusSendAgain);
3834 mSaveAsAction->setEnabled(mass_actions);
3836 if ((mCurrentFolder && mCurrentFolder->isValid())) {
3837 updateMoveAction(mCurrentFolder->statistics());
3838 } else {
3839 updateMoveAction(false, false);
3842 const qint64 nbMsgOutboxCollection = MailCommon::Util::updatedCollection(CommonKernel->outboxCollectionFolder()).statistics().count();
3844 mSendQueued->setEnabled(nbMsgOutboxCollection > 0);
3845 mSendActionMenu->setEnabled(nbMsgOutboxCollection > 0);
3847 const bool newPostToMailingList = mCurrentFolder && mCurrentFolder->isMailingListEnabled();
3848 mMessageNewList->setEnabled(newPostToMailingList);
3850 slotUpdateOnlineStatus(static_cast<GlobalSettingsBase::EnumNetworkState::type>(KMailSettings::self()->networkState()));
3851 if (action(QStringLiteral("kmail_undo"))) {
3852 action(QStringLiteral("kmail_undo"))->setEnabled(kmkernel->undoStack()->size() > 0);
3855 // Enable / disable all filters.
3856 foreach (QAction *filterAction, mFilterMenuActions) {
3857 filterAction->setEnabled(count > 0);
3860 mApplyAllFiltersAction->setEnabled(count);
3861 mApplyFilterActionsMenu->setEnabled(count);
3864 void KMMainWidget::slotAkonadiStandardActionUpdated()
3866 bool multiFolder = false;
3867 if (mFolderTreeWidget) {
3868 multiFolder = mFolderTreeWidget->selectedCollections().count() > 1;
3870 if (mCollectionProperties) {
3871 if (mCurrentFolder && mCurrentFolder->collection().isValid()) {
3872 const Akonadi::AgentInstance instance =
3873 Akonadi::AgentManager::self()->instance(mCurrentFolder->collection().resource());
3875 mCollectionProperties->setEnabled(!multiFolder &&
3876 !mCurrentFolder->isStructural() &&
3877 (instance.status() != Akonadi::AgentInstance::Broken));
3878 } else {
3879 mCollectionProperties->setEnabled(false);
3881 QList< QAction * > collectionProperties;
3882 if (mCollectionProperties->isEnabled()) {
3883 collectionProperties << mCollectionProperties;
3885 mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_collectionproperties_actionlist"));
3886 mGUIClient->plugActionList(QStringLiteral("akonadi_collection_collectionproperties_actionlist"), collectionProperties);
3890 const bool folderWithContent = mCurrentFolder && !mCurrentFolder->isStructural();
3892 if (mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections)) {
3894 mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections)->setEnabled(mCurrentFolder
3895 && !multiFolder
3896 && (mCurrentFolder->collection().rights() & Collection::CanDeleteCollection)
3897 && !mCurrentFolder->isSystemFolder()
3898 && folderWithContent);
3901 if (mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)) {
3902 mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)->setEnabled(folderWithContent
3903 && (mCurrentFolder->count() > 0)
3904 && mCurrentFolder->canDeleteMessages()
3905 && !multiFolder);
3906 mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)->setText((mCurrentFolder && CommonKernel->folderIsTrash(mCurrentFolder->collection())) ? i18n("E&mpty Trash") : i18n("&Move All Messages to Trash"));
3909 QList< QAction * > addToFavorite;
3910 QAction *actionAddToFavoriteCollections = akonadiStandardAction(Akonadi::StandardActionManager::AddToFavoriteCollections);
3911 if (actionAddToFavoriteCollections) {
3912 if (mEnableFavoriteFolderView && actionAddToFavoriteCollections->isEnabled()) {
3913 addToFavorite << actionAddToFavoriteCollections;
3915 mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_add_to_favorites_actionlist"));
3916 mGUIClient->plugActionList(QStringLiteral("akonadi_collection_add_to_favorites_actionlist"), addToFavorite);
3919 QList< QAction * > syncActionList;
3920 QAction *actionSync = akonadiStandardAction(Akonadi::StandardActionManager::SynchronizeCollections);
3921 if (actionSync && actionSync->isEnabled()) {
3922 syncActionList << actionSync;
3924 actionSync = akonadiStandardAction(Akonadi::StandardActionManager::SynchronizeCollectionsRecursive);
3925 if (actionSync && actionSync->isEnabled()) {
3926 syncActionList << actionSync;
3928 mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_sync_actionlist"));
3929 mGUIClient->plugActionList(QStringLiteral("akonadi_collection_sync_actionlist"), syncActionList);
3931 QList< QAction * > actionList;
3933 QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CreateCollection);
3934 if (action && action->isEnabled()) {
3935 actionList << action;
3938 action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::MoveCollectionToMenu);
3939 if (action && action->isEnabled()) {
3940 actionList << action;
3943 action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyCollectionToMenu);
3944 if (action && action->isEnabled()) {
3945 actionList << action;
3947 mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_move_copy_menu_actionlist"));
3948 mGUIClient->plugActionList(QStringLiteral("akonadi_collection_move_copy_menu_actionlist"), actionList);
3952 void KMMainWidget::updateHtmlMenuEntry()
3954 if (mDisplayMessageFormatMenu && mPreferHtmlLoadExtAction) {
3955 bool multiFolder = false;
3956 if (mFolderTreeWidget) {
3957 multiFolder = mFolderTreeWidget->selectedCollections().count() > 1;
3959 // the visual ones only make sense if we are showing a message list
3960 const bool enabledAction = (mFolderTreeWidget &&
3961 mFolderTreeWidget->folderTreeView()->currentFolder().isValid() &&
3962 !multiFolder);
3964 mDisplayMessageFormatMenu->setEnabled(enabledAction);
3965 const bool isEnabled = (mFolderTreeWidget &&
3966 mFolderTreeWidget->folderTreeView()->currentFolder().isValid() &&
3967 !multiFolder);
3968 const bool useHtml = (mFolderDisplayFormatPreference == MessageViewer::Viewer::Html || (mHtmlGlobalSetting && mFolderDisplayFormatPreference == MessageViewer::Viewer::UseGlobalSetting));
3969 mPreferHtmlLoadExtAction->setEnabled(isEnabled && useHtml);
3971 mDisplayMessageFormatMenu->setDisplayMessageFormat(mFolderDisplayFormatPreference);
3973 mPreferHtmlLoadExtAction->setChecked(!multiFolder && (mHtmlLoadExtGlobalSetting ? !mFolderHtmlLoadExtPreference : mFolderHtmlLoadExtPreference));
3977 //-----------------------------------------------------------------------------
3978 void KMMainWidget::updateFolderMenu()
3980 if (!CommonKernel->outboxCollectionFolder().isValid()) {
3981 QTimer::singleShot(1000, this, &KMMainWidget::updateFolderMenu);
3982 return;
3985 const bool folderWithContent = mCurrentFolder && !mCurrentFolder->isStructural();
3986 bool multiFolder = false;
3987 if (mFolderTreeWidget) {
3988 multiFolder = mFolderTreeWidget->selectedCollections().count() > 1;
3990 mFolderMailingListPropertiesAction->setEnabled(folderWithContent &&
3991 !multiFolder &&
3992 !mCurrentFolder->isSystemFolder());
3994 QList< QAction * > actionlist;
3995 if (mCurrentFolder && mCurrentFolder->collection().id() == CommonKernel->outboxCollectionFolder().id() && (mCurrentFolder->collection()).statistics().count() > 0) {
3996 qCDebug(KMAIL_LOG) << "Enabling send queued";
3997 mSendQueued->setEnabled(true);
3998 actionlist << mSendQueued;
4000 // if ( mCurrentFolder && mCurrentFolder->collection().id() != CommonKernel->trashCollectionFolder().id() ) {
4001 // actionlist << mTrashAction;
4002 // }
4003 mGUIClient->unplugActionList(QStringLiteral("outbox_folder_actionlist"));
4004 mGUIClient->plugActionList(QStringLiteral("outbox_folder_actionlist"), actionlist);
4005 actionlist.clear();
4007 const bool isASearchFolder = mCurrentFolder && mCurrentFolder->collection().resource() == QLatin1String("akonadi_search_resource");
4008 if (isASearchFolder) {
4009 mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections)->setText(i18n("&Delete Search"));
4012 mArchiveFolderAction->setEnabled(mCurrentFolder && !multiFolder && folderWithContent);
4014 bool isInTrashFolder = (mCurrentFolder && CommonKernel->folderIsTrash(mCurrentFolder->collection()));
4015 QAction *moveToTrash = akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash);
4016 akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash)->setText(isInTrashFolder ? i18nc("@action Hard delete, bypassing trash", "&Delete") : i18n("&Move to Trash"));
4017 akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash)->setIcon(isInTrashFolder ? QIcon::fromTheme(QStringLiteral("edit-delete")) : QIcon::fromTheme(QStringLiteral("user-trash")));
4018 //Use same text as in Text property. Change it in kf5
4019 moveToTrash->setToolTip(isInTrashFolder ? i18nc("@action Hard delete, bypassing trash", "&Delete") : i18n("&Move to Trash"));
4021 mTrashThreadAction->setIcon(isInTrashFolder ? QIcon::fromTheme(QStringLiteral("edit-delete")) : QIcon::fromTheme(QStringLiteral("user-trash")));
4022 mTrashThreadAction->setText(isInTrashFolder ? i18n("Delete T&hread") : i18n("M&ove Thread to Trash"));
4024 mSearchMessages->setText((mCurrentFolder && mCurrentFolder->collection().resource() == QLatin1String("akonadi_search_resource")) ? i18n("Edit Search...") : i18n("&Find Messages..."));
4026 mExpireConfigAction->setEnabled(mCurrentFolder &&
4027 !mCurrentFolder->isStructural() &&
4028 !multiFolder &&
4029 mCurrentFolder->canDeleteMessages() &&
4030 folderWithContent &&
4031 !MailCommon::Util::isVirtualCollection(mCurrentFolder->collection()));
4033 updateHtmlMenuEntry();
4035 mShowFolderShortcutDialogAction->setEnabled(!multiFolder && folderWithContent);
4037 actionlist << akonadiStandardAction(Akonadi::StandardActionManager::ManageLocalSubscriptions);
4038 bool imapFolderIsOnline = false;
4039 if (mCurrentFolder && PimCommon::Util::isImapFolder(mCurrentFolder->collection(), imapFolderIsOnline)) {
4040 if (imapFolderIsOnline) {
4041 actionlist << mServerSideSubscription;
4045 mGUIClient->unplugActionList(QStringLiteral("collectionview_actionlist"));
4046 mGUIClient->plugActionList(QStringLiteral("collectionview_actionlist"), actionlist);
4050 //-----------------------------------------------------------------------------
4051 void KMMainWidget::slotIntro()
4053 if (!mMsgView) {
4054 return;
4057 mMsgView->clear(true);
4059 // hide widgets that are in the way:
4060 if (mMessagePane && mLongFolderList) {
4061 mMessagePane->hide();
4063 mMsgView->displayAboutPage();
4065 mCurrentFolder.clear();
4068 void KMMainWidget::slotShowStartupFolder()
4070 connect(MailCommon::FilterManager::instance(), &FilterManager::filtersChanged,
4071 this, &KMMainWidget::initializeFilterActions);
4072 // Plug various action lists. This can't be done in the constructor, as that is called before
4073 // the main window or Kontact calls createGUI().
4074 // This function however is called with a single shot timer.
4075 checkAkonadiServerManagerState();
4076 mFolderShortcutActionManager->createActions();
4077 mTagActionManager->createActions();
4078 messageActions()->setupForwardingActionsList(mGUIClient);
4079 initializePluginActions();
4081 QString newFeaturesMD5 = KMReaderWin::newFeaturesMD5();
4082 if (kmkernel->firstStart() ||
4083 KMailSettings::self()->previousNewFeaturesMD5() != newFeaturesMD5) {
4084 KMailSettings::self()->setPreviousNewFeaturesMD5(newFeaturesMD5);
4085 slotIntro();
4086 return;
4090 void KMMainWidget::checkAkonadiServerManagerState()
4092 Akonadi::ServerManager::State state = Akonadi::ServerManager::self()->state();
4093 if (state == Akonadi::ServerManager::Running) {
4094 initializeFilterActions();
4095 } else {
4096 connect(Akonadi::ServerManager::self(), &ServerManager::stateChanged,
4097 this, &KMMainWidget::slotServerStateChanged);
4101 void KMMainWidget::slotServerStateChanged(Akonadi::ServerManager::State state)
4103 if (state == Akonadi::ServerManager::Running) {
4104 initializeFilterActions();
4105 disconnect(Akonadi::ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State)));
4109 QList<KActionCollection *> KMMainWidget::actionCollections() const
4111 return QList<KActionCollection *>() << actionCollection();
4114 //-----------------------------------------------------------------------------
4115 void KMMainWidget::slotUpdateUndo()
4117 if (actionCollection()->action(QStringLiteral("kmail_undo"))) {
4118 QAction *act = actionCollection()->action(QStringLiteral("kmail_undo"));
4119 act->setEnabled(!kmkernel->undoStack()->isEmpty());
4120 const QString infoStr = kmkernel->undoStack()->undoInfo();
4121 if (infoStr.isEmpty()) {
4122 act->setText(i18n("&Undo"));
4123 } else {
4124 act->setText(i18n("&Undo: \"%1\"", kmkernel->undoStack()->undoInfo()));
4129 //-----------------------------------------------------------------------------
4130 void KMMainWidget::clearFilterActions()
4132 if (!mFilterTBarActions.isEmpty())
4133 if (mGUIClient->factory()) {
4134 mGUIClient->unplugActionList(QStringLiteral("toolbar_filter_actions"));
4137 if (!mFilterMenuActions.isEmpty())
4138 if (mGUIClient->factory()) {
4139 mGUIClient->unplugActionList(QStringLiteral("menu_filter_actions"));
4142 foreach (QAction *a, mFilterMenuActions) {
4143 actionCollection()->removeAction(a);
4146 mApplyFilterActionsMenu->menu()->clear();
4147 mFilterTBarActions.clear();
4148 mFilterMenuActions.clear();
4150 qDeleteAll(mFilterCommands);
4151 mFilterCommands.clear();
4154 void KMMainWidget::initializePluginActions()
4156 mPluginInterface->initializePluginActions(QStringLiteral("kmail"), mGUIClient);
4159 //-----------------------------------------------------------------------------
4160 void KMMainWidget::initializeFilterActions()
4162 clearFilterActions();
4163 mApplyFilterActionsMenu->menu()->addAction(mApplyAllFiltersAction);
4164 bool addedSeparator = false;
4166 foreach (MailFilter *filter, MailCommon::FilterManager::instance()->filters()) {
4167 if (!filter->isEmpty() && filter->configureShortcut() && filter->isEnabled()) {
4168 QString filterName = QStringLiteral("Filter %1").arg(filter->name());
4169 QString normalizedName = filterName.replace(QLatin1Char(' '), QLatin1Char('_'));
4170 if (action(normalizedName)) {
4171 continue;
4173 KMMetaFilterActionCommand *filterCommand = new KMMetaFilterActionCommand(filter->identifier(), this);
4174 mFilterCommands.append(filterCommand);
4175 QString displayText = i18n("Filter %1", filter->name());
4176 QString icon = filter->icon();
4177 if (icon.isEmpty()) {
4178 icon = QStringLiteral("system-run");
4180 QAction *filterAction = new QAction(QIcon::fromTheme(icon), displayText, actionCollection());
4181 filterAction->setIconText(filter->toolbarName());
4183 // The shortcut configuration is done in the filter dialog.
4184 // The shortcut set in the shortcut dialog would not be saved back to
4185 // the filter settings correctly.
4186 actionCollection()->setShortcutsConfigurable(filterAction, false);
4187 actionCollection()->addAction(normalizedName,
4188 filterAction);
4189 connect(filterAction, &QAction::triggered,
4190 filterCommand, &KMMetaFilterActionCommand::start);
4191 actionCollection()->setDefaultShortcut(filterAction, filter->shortcut());
4192 if (!addedSeparator) {
4193 QAction *a = mApplyFilterActionsMenu->menu()->addSeparator();
4194 mFilterMenuActions.append(a);
4195 addedSeparator = true;
4197 mApplyFilterActionsMenu->menu()->addAction(filterAction);
4198 mFilterMenuActions.append(filterAction);
4199 if (filter->configureToolbar()) {
4200 mFilterTBarActions.append(filterAction);
4204 if (!mFilterMenuActions.isEmpty() && mGUIClient->factory()) {
4205 mGUIClient->plugActionList(QStringLiteral("menu_filter_actions"), mFilterMenuActions);
4207 if (!mFilterTBarActions.isEmpty() && mGUIClient->factory()) {
4208 mFilterTBarActions.prepend(mToolbarActionSeparator);
4209 mGUIClient->plugActionList(QStringLiteral("toolbar_filter_actions"), mFilterTBarActions);
4212 // Our filters have changed, now enable/disable them
4213 updateMessageActions();
4216 void KMMainWidget::updateFileMenu()
4218 const bool isEmpty = MailCommon::Util::agentInstances().isEmpty();
4219 actionCollection()->action(QStringLiteral("check_mail"))->setEnabled(!isEmpty);
4220 actionCollection()->action(QStringLiteral("check_mail_in"))->setEnabled(!isEmpty);
4223 //-----------------------------------------------------------------------------
4224 const KMMainWidget::PtrList *KMMainWidget::mainWidgetList()
4226 // better safe than sorry; check whether the global static has already been destroyed
4227 if (theMainWidgetList.isDestroyed()) {
4228 return Q_NULLPTR;
4230 return theMainWidgetList;
4233 QSharedPointer<FolderCollection> KMMainWidget::currentFolder() const
4235 return mCurrentFolder;
4238 //-----------------------------------------------------------------------------
4239 QString KMMainWidget::overrideEncoding() const
4241 if (mMsgView) {
4242 return mMsgView->overrideEncoding();
4243 } else {
4244 return MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding();
4248 void KMMainWidget::showEvent(QShowEvent *event)
4250 QWidget::showEvent(event);
4251 mWasEverShown = true;
4254 void KMMainWidget::slotRequestFullSearchFromQuickSearch()
4256 // First, open the search window. If we are currently on a search folder,
4257 // the search associated with that will be loaded.
4258 if (!slotSearch()) {
4259 return;
4262 assert(mSearchWin);
4264 // Now we look at the current state of the quick search, and if there's
4265 // something in there, we add the criteria to the existing search for
4266 // the search folder, if applicable, or make a new one from it.
4267 SearchPattern pattern;
4268 const QString searchString = mMessagePane->currentFilterSearchString();
4269 if (!searchString.isEmpty()) {
4270 MessageList::Core::QuickSearchLine::SearchOptions options = mMessagePane->currentOptions();
4271 QByteArray searchStringVal;
4272 if (options & MessageList::Core::QuickSearchLine::SearchEveryWhere) {
4273 searchStringVal = "<message>";
4274 } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstSubject) {
4275 searchStringVal = "subject";
4276 } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstBody) {
4277 searchStringVal = "<body>";
4278 } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstFrom) {
4279 searchStringVal = "from";
4280 } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstBcc) {
4281 searchStringVal = "bcc";
4282 } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstTo) {
4283 searchStringVal = "to";
4284 } else {
4285 searchStringVal = "<message>";
4287 pattern.append(SearchRule::createInstance(searchStringVal, SearchRule::FuncContains, searchString));
4288 QList<MessageStatus> statusList = mMessagePane->currentFilterStatus();
4289 Q_FOREACH (MessageStatus status, statusList) {
4290 if (status.hasAttachment()) {
4291 pattern.append(SearchRule::createInstance(searchStringVal, SearchRule::FuncHasAttachment, searchString));
4292 status.setHasAttachment(false);
4294 if (!status.isOfUnknownStatus()) {
4295 pattern.append(SearchRule::Ptr(new SearchRuleStatus(status)));
4299 if (!pattern.isEmpty()) {
4300 mSearchWin->addRulesToSearchPattern(pattern);
4304 void KMMainWidget::updateVacationScriptStatus(bool active, const QString &serverName)
4306 mVacationScriptIndicator->setVacationScriptActive(active, serverName);
4307 mVacationIndicatorActive = mVacationScriptIndicator->hasVacationScriptActive();
4310 QWidget *KMMainWidget::vacationScriptIndicator() const
4312 return mVacationScriptIndicator;
4315 void KMMainWidget::updateVacationScriptStatus()
4317 updateVacationScriptStatus(mVacationIndicatorActive);
4320 KMail::TagActionManager *KMMainWidget::tagActionManager() const
4322 return mTagActionManager;
4325 KMail::FolderShortcutActionManager *KMMainWidget::folderShortcutActionManager() const
4327 return mFolderShortcutActionManager;
4330 void KMMainWidget::slotMessageSelected(const Akonadi::Item &item)
4332 delete mShowBusySplashTimer;
4333 mShowBusySplashTimer = Q_NULLPTR;
4334 if (mMsgView) {
4335 // The current selection was cleared, so we'll remove the previously
4336 // selected message from the preview pane
4337 if (!item.isValid()) {
4338 mMsgView->clear();
4339 } else {
4340 mShowBusySplashTimer = new QTimer(this);
4341 mShowBusySplashTimer->setSingleShot(true);
4342 connect(mShowBusySplashTimer, &QTimer::timeout, this, &KMMainWidget::slotShowBusySplash);
4343 mShowBusySplashTimer->start(1000);
4345 Akonadi::ItemFetchJob *itemFetchJob = MessageViewer::Viewer::createFetchJob(item);
4346 if (mCurrentFolder) {
4347 const QString resource = mCurrentFolder->collection().resource();
4348 itemFetchJob->setProperty("_resource", QVariant::fromValue(resource));
4349 connect(itemFetchJob, &ItemFetchJob::itemsReceived,
4350 this, &KMMainWidget::itemsReceived);
4351 connect(itemFetchJob, &Akonadi::ItemFetchJob::result, this, &KMMainWidget::itemsFetchDone);
4357 void KMMainWidget::itemsReceived(const Akonadi::Item::List &list)
4359 Q_ASSERT(list.size() == 1);
4360 delete mShowBusySplashTimer;
4361 mShowBusySplashTimer = Q_NULLPTR;
4363 if (!mMsgView) {
4364 return;
4367 Item item = list.first();
4369 if (mMessagePane) {
4370 mMessagePane->show();
4372 if (mMessagePane->currentItem() != item) {
4373 // The user has selected another email already, so don't render this one.
4374 // Mark it as read, though, if the user settings say so.
4375 if (MessageViewer::MessageViewerSettings::self()->delayedMarkAsRead() &&
4376 MessageViewer::MessageViewerSettings::self()->delayedMarkTime() == 0) {
4377 item.setFlag(Akonadi::MessageFlags::Seen);
4378 Akonadi::ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob(item, this);
4379 modifyJob->disableRevisionCheck();
4380 modifyJob->setIgnorePayload(true);
4382 return;
4386 Akonadi::Item copyItem(item);
4387 if (mCurrentFolder) {
4388 copyItem.setParentCollection(mCurrentFolder->collection());
4391 mMsgView->setMessage(copyItem);
4392 // reset HTML override to the folder setting
4393 mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference);
4394 mMsgView->setHtmlLoadExtOverride(mFolderHtmlLoadExtPreference);
4395 mMsgView->setDecryptMessageOverwrite(false);
4396 mMsgActions->setCurrentMessage(copyItem);
4399 void KMMainWidget::itemsFetchDone(KJob *job)
4401 delete mShowBusySplashTimer;
4402 mShowBusySplashTimer = Q_NULLPTR;
4403 if (job->error()) {
4404 // Unfortunately job->error() is Job::Unknown in many cases.
4405 // (see JobPrivate::handleResponse in akonadi/job.cpp)
4406 // So we show the "offline" page after checking the resource status.
4407 qCDebug(KMAIL_LOG) << job->error() << job->errorString();
4409 const QString resource = job->property("_resource").toString();
4410 const Akonadi::AgentInstance agentInstance = Akonadi::AgentManager::self()->instance(resource);
4411 if (!agentInstance.isOnline()) {
4412 // The resource is offline
4413 if (mMsgView) {
4414 mMsgView->viewer()->enableMessageDisplay();
4415 mMsgView->clear(true);
4417 mMessagePane->show();
4419 if (mMsgView) {
4420 if (kmkernel->isOffline()) {
4421 showOfflinePage();
4422 } else {
4423 showResourceOfflinePage();
4426 } else {
4427 // Some other error
4428 BroadcastStatus::instance()->setStatusMsg(job->errorString());
4433 QAction *KMMainWidget::akonadiStandardAction(Akonadi::StandardActionManager::Type type)
4435 return mAkonadiStandardActionManager->action(type);
4438 QAction *KMMainWidget::akonadiStandardAction(Akonadi::StandardMailActionManager::Type type)
4440 return mAkonadiStandardActionManager->action(type);
4443 void KMMainWidget::slotRemoveDuplicates()
4445 RemoveDuplicateMailJob *job = new RemoveDuplicateMailJob(mFolderTreeWidget->folderTreeView()->selectionModel(), this, this);
4446 job->start();
4449 void KMMainWidget::slotServerSideSubscription()
4451 if (!mCurrentFolder) {
4452 return;
4454 PimCommon::ManageServerSideSubscriptionJob *job = new PimCommon::ManageServerSideSubscriptionJob(this);
4455 job->setCurrentCollection(mCurrentFolder->collection());
4456 job->setParentWidget(this);
4457 job->start();
4460 void KMMainWidget::savePaneSelection()
4462 if (mMessagePane) {
4463 mMessagePane->saveCurrentSelection();
4467 void KMMainWidget::updatePaneTagComboBox()
4469 if (mMessagePane) {
4470 mMessagePane->updateTagComboBox();
4474 void KMMainWidget::slotCreateAddressBookContact()
4476 CreateNewContactJob *job = new CreateNewContactJob(this, this);
4477 job->start();
4480 void KMMainWidget::slotOpenRecentMsg(const QUrl &url)
4482 KMOpenMsgCommand *openCommand = new KMOpenMsgCommand(this, url, overrideEncoding(), this);
4483 openCommand->start();
4486 void KMMainWidget::addRecentFile(const QUrl &mUrl)
4488 mOpenRecentAction->addUrl(mUrl);
4489 KConfigGroup grp = mConfig->group(QStringLiteral("Recent Files"));
4490 mOpenRecentAction->saveEntries(grp);
4491 grp.sync();
4494 void KMMainWidget::slotMoveMessageToTrash()
4496 if (messageView() && messageView()->viewer() && mCurrentFolder) {
4497 KMTrashMsgCommand *command = new KMTrashMsgCommand(mCurrentFolder->collection(), messageView()->viewer()->messageItem(), -1);
4498 command->start();
4502 void KMMainWidget::slotArchiveMails()
4504 if (mCurrentFolder && mCurrentFolder->collection().isValid()) {
4505 const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList();
4506 KMKernel::self()->folderArchiveManager()->setArchiveItems(selectedMessages, mCurrentFolder->collection().resource());
4510 void KMMainWidget::updateQuickSearchLineText()
4512 //If change change shortcut
4513 mMessagePane->setQuickSearchClickMessage(i18nc("Show shortcut for focus quick search. Don't change it", "Search...<%1>", mQuickSearchAction->shortcut().toString()));
4516 void KMMainWidget::slotChangeDisplayMessageFormat(MessageViewer::Viewer::DisplayFormatMessage format)
4518 if (format == MessageViewer::Viewer::Html) {
4519 const int result = KMessageBox::warningContinueCancel(this,
4520 // the warning text is taken from configuredialog.cpp:
4521 i18n("Use of HTML in mail will make you more vulnerable to "
4522 "\"spam\" and may increase the likelihood that your system will be "
4523 "compromised by other present and anticipated security exploits."),
4524 i18n("Security Warning"),
4525 KGuiItem(i18n("Use HTML")),
4526 KStandardGuiItem::cancel(),
4527 QStringLiteral("OverrideHtmlWarning"), Q_NULLPTR);
4528 if (result == KMessageBox::Cancel) {
4529 mDisplayMessageFormatMenu->setDisplayMessageFormat(MessageViewer::Viewer::Text);
4530 return;
4533 mFolderDisplayFormatPreference = format;
4535 //Update mPrefererHtmlLoadExtAction
4536 const bool useHtml = (mFolderDisplayFormatPreference == MessageViewer::Viewer::Html || (mHtmlGlobalSetting && mFolderDisplayFormatPreference == MessageViewer::Viewer::UseGlobalSetting));
4537 mPreferHtmlLoadExtAction->setEnabled(useHtml);
4539 if (mMsgView) {
4540 mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference);
4541 mMsgView->update(true);
4543 writeFolderConfig();
4546 void KMMainWidget::populateMessageListStatusFilterCombo()
4548 mMessagePane->populateStatusFilterCombo();
4551 void KMMainWidget::slotCollectionRemoved(const Akonadi::Collection &col)
4553 if (mFavoritesModel) {
4554 mFavoritesModel->removeCollection(col);
4558 void KMMainWidget::slotMarkAllMessageAsReadInCurrentFolderAndSubfolder()
4560 if (mCurrentFolder && mCurrentFolder->collection().isValid()) {
4561 MarkAllMessagesAsReadInFolderAndSubFolderJob *job = new MarkAllMessagesAsReadInFolderAndSubFolderJob(this);
4562 job->setTopLevelCollection(mCurrentFolder->collection());
4563 job->start();
4567 void KMMainWidget::slotRemoveDuplicateRecursive()
4569 if (mCurrentFolder && mCurrentFolder->collection().isValid()) {
4570 RemoveDuplicateMessageInFolderAndSubFolderJob *job = new RemoveDuplicateMessageInFolderAndSubFolderJob(this);
4571 job->setTopLevelCollection(mCurrentFolder->collection());
4572 job->start();
4576 void KMMainWidget::slotUpdateConfig()
4578 readFolderConfig();
4579 updateHtmlMenuEntry();
4582 void KMMainWidget::slotExecuteMailAction(MessageViewer::Viewer::MailAction action)
4584 switch (action) {
4585 case MessageViewer::Viewer::Trash:
4586 slotMoveMessageToTrash();
4587 break;
4588 case MessageViewer::Viewer::Reply:
4589 replyCurrentMessageCommand(MessageComposer::ReplySmart);
4590 break;
4591 case MessageViewer::Viewer::ReplyToAll:
4592 replyCurrentMessageCommand(MessageComposer::ReplyAll);
4593 break;
4594 case MessageViewer::Viewer::Forward:
4595 slotRedirectCurrentMessage();
4596 break;
4597 case MessageViewer::Viewer::NewMessage:
4598 slotCompose();
4599 break;
4600 case MessageViewer::Viewer::Print:
4601 printCurrentMessage(false);
4602 break;
4603 case MessageViewer::Viewer::PrintPreview:
4604 printCurrentMessage(true);
4605 break;
4609 void KMMainWidget::printCurrentMessage(bool preview)
4611 bool result = false;
4612 if (messageView() && messageView()->viewer()) {
4613 if (MessageViewer::MessageViewerSettings::self()->printSelectedText()) {
4614 result = messageView()->printSelectedText(preview);
4617 if (!result) {
4618 const bool useFixedFont = MessageViewer::MessageViewerSettings::self()->useFixedFont();
4619 const QString overrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding();
4621 const Akonadi::Item currentItem = messageView()->viewer()->messageItem();
4622 KMPrintCommand *command =
4623 new KMPrintCommand(this, currentItem,
4624 messageView()->viewer()->headerStylePlugin(),
4625 messageView()->viewer()->displayFormatMessageOverwrite(), messageView()->viewer()->htmlLoadExternal(),
4626 useFixedFont, overrideEncoding);
4627 command->setPrintPreview(preview);
4628 command->start();
4632 void KMMainWidget::slotRedirectCurrentMessage()
4634 if (messageView() && messageView()->viewer()) {
4635 const Akonadi::Item currentItem = messageView()->viewer()->messageItem();
4636 if (!currentItem.hasPayload<KMime::Message::Ptr>()) {
4637 return;
4639 KMCommand *command = new KMRedirectCommand(this, currentItem);
4640 command->start();
4644 void KMMainWidget::replyCurrentMessageCommand(MessageComposer::ReplyStrategy strategy)
4646 if (messageView() && messageView()->viewer()) {
4647 Akonadi::Item currentItem = messageView()->viewer()->messageItem();
4648 if (!currentItem.hasPayload<KMime::Message::Ptr>()) {
4649 return;
4651 const QString text = messageView()->copyText();
4652 KMCommand *command = new KMReplyCommand(this, currentItem, strategy, text);
4653 command->start();