diff --git a/kmail/kmmainwidget.cpp b/kmail/kmmainwidget.cpp index b450dcd0de..5f7aed7f16 100644 --- a/kmail/kmmainwidget.cpp +++ b/kmail/kmmainwidget.cpp @@ -1,4562 +1,4563 @@ /* This file is part of KMail, the KDE mail client. Copyright (c) 2002 Don Sanders Copyright (c) 2009-2015 Montel Laurent Based on the work of Stefan Taferner KMail is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. KMail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // KMail includes #include "kmreadermainwin.h" #include "editor/composer.h" #include "searchdialog/searchwindow.h" #include "antispam-virus/antispamwizard.h" #include "widgets/vacationscriptindicatorwidget.h" #include "undostack.h" #include "kmcommands.h" #include "kmmainwin.h" #include "kmsystemtray.h" #include #include "MailCommon/FolderSelectionDialog" #include "MailCommon/FolderTreeWidget" #include "util.h" #include "mailcommon/mailutil.h" #include "mailcommon/mailkernel.h" #include "dialog/archivefolderdialog.h" #include "settings/kmailsettings.h" #include "MailCommon/FolderTreeView" #include "tag/tagactionmanager.h" #include "foldershortcutactionmanager.h" #include "widgets/collectionpane.h" #include "manageshowcollectionproperties.h" #include "widgets/kactionmenutransport.h" #include "widgets/kactionmenuaccount.h" #include "mailcommon/searchrulestatus.h" #include "PimCommon/NetworkUtil" #include "kpimtextedit/texttospeech.h" #include "job/markallmessagesasreadinfolderandsubfolderjob.h" #if !defined(NDEBUG) #include using KSieveUi::SieveDebugDialog; #endif #include "collectionpage/collectionmaintenancepage.h" #include "collectionpage/collectionquotapage.h" #include "collectionpage/collectiontemplatespage.h" #include "collectionpage/collectionshortcutpage.h" #include "collectionpage/collectionviewpage.h" #include "collectionpage/collectionmailinglistpage.h" #include "tag/tagselectdialog.h" #include "job/createnewcontactjob.h" #include "folderarchive/folderarchiveutil.h" #include "folderarchive/folderarchivemanager.h" #include "PimCommon/CollectionAclPage" #include "PimCommon/PimUtil" #include "MailCommon/CollectionGeneralPage" #include "MailCommon/CollectionExpiryPage" #include "MailCommon/ExpireCollectionAttribute" #include "MailCommon/FilterManager" #include "MailCommon/MailFilter" #include "MailCommon/FavoriteCollectionWidget" #include "MailCommon/FolderTreeWidget" #include "MailCommon/FolderTreeView" #include "mailcommonsettings_base.h" #include "kmmainwidget.h" // Other PIM includes #include "kdepim-version.h" #include "messageviewer/messageviewersettings.h" #include "messageviewer/viewer.h" #include "messageviewer/attachmentstrategy.h" #ifndef QT_NO_CURSOR #include "Libkdepim/KCursorSaver" #endif #include "MessageComposer/MessageSender" #include "MessageComposer/MessageHelper" #include "TemplateParser/TemplateParser" #include "MessageCore/MessageCoreSettings" #include "MessageCore/MailingList" #include "messagecore/messagehelpers.h" #include "dialog/kmknotify.h" #include "widgets/displaymessageformatactionmenu.h" #include "ksieveui/vacationmanager.h" #include "kmlaunchexternalcomponent.h" // LIBKDEPIM includes #include "libkdepim/progressmanager.h" #include "libkdepim/broadcaststatus.h" // KDEPIMLIBS includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDELIBS includes #include #include #include #include #include #include #include #include "kmail_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Qt includes #include #include #include #include #include #include #include #include #include #include // System includes #include #include #include #include "PimCommon/ManageServerSideSubscriptionJob" #include #include using namespace KMime; using namespace Akonadi; using namespace MailCommon; using KPIM::ProgressManager; using KPIM::BroadcastStatus; using KMail::SearchWindow; using KMail::AntiSpamWizard; using KMime::Types::AddrSpecList; using MessageViewer::AttachmentStrategy; Q_GLOBAL_STATIC(KMMainWidget::PtrList, theMainWidgetList) //----------------------------------------------------------------------------- KMMainWidget::KMMainWidget(QWidget *parent, KXMLGUIClient *aGUIClient, KActionCollection *actionCollection, KSharedConfig::Ptr config) : QWidget(parent), mMoveMsgToFolderAction(Q_NULLPTR), mCollectionProperties(Q_NULLPTR), mFavoriteCollectionsView(Q_NULLPTR), mMsgView(Q_NULLPTR), mSplitter1(Q_NULLPTR), mSplitter2(Q_NULLPTR), mFolderViewSplitter(Q_NULLPTR), mArchiveFolderAction(Q_NULLPTR), mShowBusySplashTimer(Q_NULLPTR), mMsgActions(Q_NULLPTR), mCurrentFolder(Q_NULLPTR), mVacationIndicatorActive(false), mGoToFirstUnreadMessageInSelectedFolder(false), mDisplayMessageFormatMenu(Q_NULLPTR), mFolderDisplayFormatPreference(MessageViewer::Viewer::UseGlobalSetting), mSearchMessages(Q_NULLPTR), mManageShowCollectionProperties(new ManageShowCollectionProperties(this, this)), mShowIntroductionAction(Q_NULLPTR), mMarkAllMessageAsReadAndInAllSubFolder(Q_NULLPTR), mAccountActionMenu(Q_NULLPTR) { mLaunchExternalComponent = new KMLaunchExternalComponent(this, this); // must be the first line of the constructor: mStartupDone = false; mWasEverShown = false; mReaderWindowActive = true; mReaderWindowBelow = true; mFolderHtmlLoadExtPreference = false; mDestructed = false; mActionCollection = actionCollection; mTopLayout = new QVBoxLayout(this); mTopLayout->setMargin(0); mConfig = config; mGUIClient = aGUIClient; mFolderTreeWidget = Q_NULLPTR; mPreferHtmlLoadExtAction = Q_NULLPTR; Akonadi::ControlGui::widgetNeedsAkonadi(this); mFavoritesModel = Q_NULLPTR; mVacationManager = new KSieveUi::VacationManager(this); mToolbarActionSeparator = new QAction(this); mToolbarActionSeparator->setSeparator(true); theMainWidgetList->append(this); readPreConfig(); createWidgets(); setupActions(); readConfig(); if (!kmkernel->isOffline()) { //kmail is set to online mode, make sure the agents are also online kmkernel->setAccountStatus(true); } QTimer::singleShot(0, this, &KMMainWidget::slotShowStartupFolder); connect(kmkernel, &KMKernel::startCheckMail, this, &KMMainWidget::slotStartCheckMail); connect(kmkernel, &KMKernel::endCheckMail, this, &KMMainWidget::slotEndCheckMail); connect(kmkernel, &KMKernel::configChanged, this, &KMMainWidget::slotConfigChanged); connect(kmkernel, &KMKernel::onlineStatusChanged, this, &KMMainWidget::slotUpdateOnlineStatus); connect(mTagActionManager, &KMail::TagActionManager::tagActionTriggered, this, &KMMainWidget::slotUpdateMessageTagList); connect(mTagActionManager, &KMail::TagActionManager::tagMoreActionClicked, this, &KMMainWidget::slotSelectMoreMessageTagList); kmkernel->toggleSystemTray(); { // make sure the pages are registered only once, since there can be multiple instances of KMMainWidget static bool pagesRegistered = false; if (!pagesRegistered) { Akonadi::CollectionPropertiesDialog::registerPage(new PimCommon::CollectionAclPageFactory); Akonadi::CollectionPropertiesDialog::registerPage(new MailCommon::CollectionGeneralPageFactory); Akonadi::CollectionPropertiesDialog::registerPage(new CollectionMaintenancePageFactory); Akonadi::CollectionPropertiesDialog::registerPage(new CollectionQuotaPageFactory); Akonadi::CollectionPropertiesDialog::registerPage(new CollectionTemplatesPageFactory); Akonadi::CollectionPropertiesDialog::registerPage(new MailCommon::CollectionExpiryPageFactory); Akonadi::CollectionPropertiesDialog::registerPage(new CollectionViewPageFactory); Akonadi::CollectionPropertiesDialog::registerPage(new CollectionMailingListPageFactory); Akonadi::CollectionPropertiesDialog::registerPage(new CollectionShortcutPageFactory); pagesRegistered = true; } } KMainWindow *mainWin = dynamic_cast(window()); QStatusBar *sb = mainWin ? mainWin->statusBar() : Q_NULLPTR; mVacationScriptIndicator = new KMail::VacationScriptIndicatorWidget(sb); mVacationScriptIndicator->hide(); connect(mVacationScriptIndicator, &KMail::VacationScriptIndicatorWidget::clicked, this, &KMMainWidget::slotEditVacation); if (KSieveUi::Util::checkOutOfOfficeOnStartup()) { QTimer::singleShot(0, this, &KMMainWidget::slotCheckVacation); } connect(mFolderTreeWidget->folderTreeView()->model(), &QAbstractItemModel::modelReset, this, &KMMainWidget::restoreCollectionFolderViewConfig); restoreCollectionFolderViewConfig(); if (kmkernel->firstStart()) { if (MailCommon::Util::foundMailer()) { if (KMessageBox::questionYesNo(this, i18n("Another mailer was found on system. Do you want to import data from it?")) == KMessageBox::Yes) { const QString path = QStandardPaths::findExecutable(QStringLiteral("importwizard")); if (!QProcess::startDetached(path)) { KMessageBox::error(this, i18n("Could not start the import wizard. " "Please check your installation."), i18n("Unable to start import wizard")); } } else { mLaunchExternalComponent->slotAccountWizard(); } } else { mLaunchExternalComponent->slotAccountWizard(); } } // must be the last line of the constructor: mStartupDone = true; mCheckMailTimer.setInterval(3 * 1000); mCheckMailTimer.setSingleShot(true); connect(&mCheckMailTimer, &QTimer::timeout, this, &KMMainWidget::slotUpdateActionsAfterMailChecking); } void KMMainWidget::restoreCollectionFolderViewConfig() { ETMViewStateSaver *saver = new ETMViewStateSaver; saver->setView(mFolderTreeWidget->folderTreeView()); const KConfigGroup cfg(KMKernel::self()->config(), "CollectionFolderView"); mFolderTreeWidget->restoreHeaderState(cfg.readEntry("HeaderState", QByteArray())); saver->restoreState(cfg); //Restore startup folder Akonadi::Collection::Id id = -1; if (mCurrentFolder && mCurrentFolder->collection().isValid()) { id = mCurrentFolder->collection().id(); } if (id == -1) { if (KMailSettings::self()->startSpecificFolderAtStartup()) { Akonadi::Collection::Id startupFolder = KMailSettings::self()->startupFolder(); if (startupFolder > 0) { saver->restoreCurrentItem(QStringLiteral("c%1").arg(startupFolder)); } } } else { saver->restoreCurrentItem(QStringLiteral("c%1").arg(id)); } } //----------------------------------------------------------------------------- //The kernel may have already been deleted when this method is called, //perform all cleanup that requires the kernel in destruct() KMMainWidget::~KMMainWidget() { theMainWidgetList->removeAll(this); qDeleteAll(mFilterCommands); destruct(); } //----------------------------------------------------------------------------- //This method performs all cleanup that requires the kernel to exist. void KMMainWidget::destruct() { if (mDestructed) { return; } if (mSearchWin) { mSearchWin->close(); } disconnect(mFolderTreeWidget->folderTreeView()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KMMainWidget::updateFolderMenu); writeConfig(false); /* don't force kmkernel sync when close BUG: 289287 */ writeFolderConfig(); deleteWidgets(); mCurrentFolder.clear(); delete mMoveOrCopyToDialog; delete mSelectFromAllFoldersDialog; disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)), this, Q_NULLPTR); disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemRemoved(Akonadi::Item)), this, Q_NULLPTR); disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)), this, Q_NULLPTR); disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(collectionChanged(Akonadi::Collection,QSet)), this, Q_NULLPTR); disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), this, Q_NULLPTR); mDestructed = true; } void KMMainWidget::slotStartCheckMail() { if (mCheckMailTimer.isActive()) { mCheckMailTimer.stop(); } } void KMMainWidget::slotEndCheckMail() { if (!mCheckMailTimer.isActive()) { mCheckMailTimer.start(); } } void KMMainWidget::slotUpdateActionsAfterMailChecking() { const bool sendOnAll = KMailSettings::self()->sendOnCheck() == KMailSettings::EnumSendOnCheck::SendOnAllChecks; const bool sendOnManual = KMailSettings::self()->sendOnCheck() == KMailSettings::EnumSendOnCheck::SendOnManualChecks; if (!kmkernel->isOffline() && (sendOnAll || sendOnManual)) { slotSendQueued(); } // update folder menus in case some mail got filtered to trash/current folder // and we can enable "empty trash/move all to trash" action etc. updateFolderMenu(); } void KMMainWidget::slotCollectionFetched(int collectionId) { // Called when a collection is fetched for the first time by the ETM. // This is the right time to update the caption (which still says "Loading...") // and to update the actions that depend on the number of mails in the folder. if (mCurrentFolder && collectionId == mCurrentFolder->collection().id()) { mCurrentFolder->setCollection(MailCommon::Util::updatedCollection(mCurrentFolder->collection())); updateMessageActions(); updateFolderMenu(); } // We call this for any collection, it could be one of our parents... if (mCurrentFolder) { Q_EMIT captionChangeRequest(MailCommon::Util::fullCollectionPath(mCurrentFolder->collection())); } } void KMMainWidget::slotFolderChanged(const Akonadi::Collection &collection) { folderSelected(collection); if (collection.cachePolicy().syncOnDemand()) { AgentManager::self()->synchronizeCollection(collection, false); } mMsgActions->setCurrentMessage(Akonadi::Item()); Q_EMIT captionChangeRequest(MailCommon::Util::fullCollectionPath(collection)); } void KMMainWidget::folderSelected(const Akonadi::Collection &col) { // This is connected to the MainFolderView signal triggering when a folder is selected if (mGoToFirstUnreadMessageInSelectedFolder) { // the default action has been overridden from outside mPreSelectionMode = MessageList::Core::PreSelectFirstUnreadCentered; } else { // use the default action switch (KMailSettings::self()->actionEnterFolder()) { case KMailSettings::EnumActionEnterFolder::SelectFirstUnread: mPreSelectionMode = MessageList::Core::PreSelectFirstUnreadCentered; break; case KMailSettings::EnumActionEnterFolder::SelectLastSelected: mPreSelectionMode = MessageList::Core::PreSelectLastSelected; break; case KMailSettings::EnumActionEnterFolder::SelectNewest: mPreSelectionMode = MessageList::Core::PreSelectNewestCentered; break; case KMailSettings::EnumActionEnterFolder::SelectOldest: mPreSelectionMode = MessageList::Core::PreSelectOldestCentered; break; default: mPreSelectionMode = MessageList::Core::PreSelectNone; break; } } mGoToFirstUnreadMessageInSelectedFolder = false; #ifndef QT_NO_CURSOR KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy()); #endif if (mMsgView) { mMsgView->clear(true); } const bool newFolder = mCurrentFolder && (mCurrentFolder->collection() != col); // Delete any pending timer, if needed it will be recreated below delete mShowBusySplashTimer; mShowBusySplashTimer = Q_NULLPTR; if (newFolder) { // We're changing folder: write configuration for the old one writeFolderConfig(); } mCurrentFolder = FolderCollection::forCollection(col); readFolderConfig(); if (mMsgView) { mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference); mMsgView->setHtmlLoadExtOverride(mFolderHtmlLoadExtPreference); } if (!mCurrentFolder->isValid() && (mMessagePane->count() < 2)) { slotIntro(); } updateMessageActions(); updateFolderMenu(); // The message pane uses the selection model of the folder view to load the correct aggregation model and theme // settings. At this point the selection model hasn't been updated yet to the user's new choice, so it would load // the old folder settings instead. QTimer::singleShot(0, this, &KMMainWidget::slotShowSelectedFolderInPane); } void KMMainWidget::slotShowSelectedFolderInPane() { if (mCurrentFolder && mCurrentFolder->collection().isValid()) { mMessagePane->setCurrentFolder(mCurrentFolder->collection(), false, mPreSelectionMode); } } void KMMainWidget::clearViewer() { if (mMsgView) { mMsgView->clear(true); mMsgView->displayAboutPage(); } } //----------------------------------------------------------------------------- void KMMainWidget::readPreConfig() { mLongFolderList = KMailSettings::self()->folderList() == KMailSettings::EnumFolderList::longlist; mReaderWindowActive = KMailSettings::self()->readerWindowMode() != KMailSettings::EnumReaderWindowMode::hide; mReaderWindowBelow = KMailSettings::self()->readerWindowMode() == KMailSettings::EnumReaderWindowMode::below; mHtmlGlobalSetting = MessageViewer::MessageViewerSettings::self()->htmlMail(); mHtmlLoadExtGlobalSetting = MessageViewer::MessageViewerSettings::self()->htmlLoadExternal(); mEnableFavoriteFolderView = (MailCommon::MailCommonSettings::self()->favoriteCollectionViewMode() != MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::HiddenMode); mEnableFolderQuickSearch = KMailSettings::self()->enableFolderQuickSearch(); readFolderConfig(); updateHtmlMenuEntry(); if (mMsgView) { mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference); mMsgView->update(true); } } //----------------------------------------------------------------------------- void KMMainWidget::readFolderConfig() { if (!mCurrentFolder || !mCurrentFolder->isValid()) { return; } KSharedConfig::Ptr config = KMKernel::self()->config(); KConfigGroup group(config, MailCommon::FolderCollection::configGroupName(mCurrentFolder->collection())); if (group.hasKey("htmlMailOverride")) { const bool useHtml = group.readEntry("htmlMailOverride", false); mFolderDisplayFormatPreference = useHtml ? MessageViewer::Viewer::Html : MessageViewer::Viewer::Text; group.deleteEntry("htmlMailOverride"); group.sync(); } else { mFolderDisplayFormatPreference = static_cast(group.readEntry("displayFormatOverride", static_cast(MessageViewer::Viewer::UseGlobalSetting))); } mFolderHtmlLoadExtPreference = group.readEntry("htmlLoadExternalOverride", false); } //----------------------------------------------------------------------------- void KMMainWidget::writeFolderConfig() { if (mCurrentFolder && mCurrentFolder->isValid()) { KSharedConfig::Ptr config = KMKernel::self()->config(); KConfigGroup group(config, MailCommon::FolderCollection::configGroupName(mCurrentFolder->collection())); group.writeEntry("htmlLoadExternalOverride", mFolderHtmlLoadExtPreference); if (mFolderDisplayFormatPreference == MessageViewer::Viewer::UseGlobalSetting) { group.deleteEntry("displayFormatOverride"); } else { group.writeEntry("displayFormatOverride", static_cast(mFolderDisplayFormatPreference)); } } } //----------------------------------------------------------------------------- void KMMainWidget::layoutSplitters() { // This function can only be called when the old splitters are already deleted Q_ASSERT(!mSplitter1); Q_ASSERT(!mSplitter2); // For some reason, this is necessary here so that the copy action still // works after changing the folder layout. if (mMsgView) disconnect(mMsgView->copyAction(), &QAction::triggered, mMsgView, &KMReaderWin::slotCopySelectedText); // If long folder list is enabled, the splitters are: // Splitter 1: FolderView vs (HeaderAndSearch vs MessageViewer) // Splitter 2: HeaderAndSearch vs MessageViewer // // If long folder list is disabled, the splitters are: // Splitter 1: (FolderView vs HeaderAndSearch) vs MessageViewer // Splitter 2: FolderView vs HeaderAndSearch // The folder view is both the folder tree and the favorite folder view, if // enabled const bool readerWindowAtSide = !mReaderWindowBelow && mReaderWindowActive; const bool readerWindowBelow = mReaderWindowBelow && mReaderWindowActive; mSplitter1 = new QSplitter(this); mSplitter2 = new QSplitter(mSplitter1); QWidget *folderTreeWidget = mSearchAndTree; if (mFavoriteCollectionsView) { mFolderViewSplitter = new QSplitter(Qt::Vertical); //mFolderViewSplitter->setChildrenCollapsible( false ); mFolderViewSplitter->addWidget(mFavoriteCollectionsView); mFavoriteCollectionsView->setParent(mFolderViewSplitter); mFolderViewSplitter->addWidget(mSearchAndTree); folderTreeWidget = mFolderViewSplitter; } if (mLongFolderList) { // add folder tree mSplitter1->setOrientation(Qt::Horizontal); mSplitter1->addWidget(folderTreeWidget); // and the rest to the right mSplitter1->addWidget(mSplitter2); // add the message list to the right or below if (readerWindowAtSide) { mSplitter2->setOrientation(Qt::Horizontal); } else { mSplitter2->setOrientation(Qt::Vertical); } mSplitter2->addWidget(mMessagePane); // add the preview window, if there is one if (mMsgView) { mSplitter2->addWidget(mMsgView); } } else { // short folder list if (mReaderWindowBelow) { mSplitter1->setOrientation(Qt::Vertical); mSplitter2->setOrientation(Qt::Horizontal); } else { // at side or none mSplitter1->setOrientation(Qt::Horizontal); mSplitter2->setOrientation(Qt::Vertical); } mSplitter1->addWidget(mSplitter2); // add folder tree mSplitter2->addWidget(folderTreeWidget); // add message list to splitter 2 mSplitter2->addWidget(mMessagePane); // add the preview window, if there is one if (mMsgView) { mSplitter1->addWidget(mMsgView); } } // // Set splitter properties // mSplitter1->setObjectName(QStringLiteral("splitter1")); //mSplitter1->setChildrenCollapsible( false ); mSplitter2->setObjectName(QStringLiteral("splitter2")); //mSplitter2->setChildrenCollapsible( false ); // // Set the stretch factors // mSplitter1->setStretchFactor(0, 0); mSplitter2->setStretchFactor(0, 0); mSplitter1->setStretchFactor(1, 1); mSplitter2->setStretchFactor(1, 1); if (mFavoriteCollectionsView) { mFolderViewSplitter->setStretchFactor(0, 0); mFolderViewSplitter->setStretchFactor(1, 1); } // Because the reader windows's width increases a tiny bit after each // restart in short folder list mode with message window at side, disable // the stretching as a workaround here if (readerWindowAtSide && !mLongFolderList) { mSplitter1->setStretchFactor(0, 1); mSplitter1->setStretchFactor(1, 0); } // // Set the sizes of the splitters to the values stored in the config // QList splitter1Sizes; QList splitter2Sizes; const int folderViewWidth = KMailSettings::self()->folderViewWidth(); int ftHeight = KMailSettings::self()->folderTreeHeight(); int headerHeight = KMailSettings::self()->searchAndHeaderHeight(); const int messageViewerWidth = KMailSettings::self()->readerWindowWidth(); int headerWidth = KMailSettings::self()->searchAndHeaderWidth(); int messageViewerHeight = KMailSettings::self()->readerWindowHeight(); int ffvHeight = mFolderViewSplitter ? MailCommon::MailCommonSettings::self()->favoriteCollectionViewHeight() : 0; // If the message viewer was hidden before, make sure it is not zero height if (messageViewerHeight < 10 && readerWindowBelow) { headerHeight /= 2; messageViewerHeight = headerHeight; } if (mLongFolderList) { if (!readerWindowAtSide) { splitter1Sizes << folderViewWidth << headerWidth; splitter2Sizes << headerHeight << messageViewerHeight; } else { splitter1Sizes << folderViewWidth << (headerWidth + messageViewerWidth); splitter2Sizes << headerWidth << messageViewerWidth; } } else { if (!readerWindowAtSide) { splitter1Sizes << headerHeight << messageViewerHeight; splitter2Sizes << folderViewWidth << headerWidth; } else { splitter1Sizes << headerWidth << messageViewerWidth; splitter2Sizes << ftHeight + ffvHeight << messageViewerHeight; } } mSplitter1->setSizes(splitter1Sizes); mSplitter2->setSizes(splitter2Sizes); if (mFolderViewSplitter) { QList splitterSizes; splitterSizes << ffvHeight << ftHeight; mFolderViewSplitter->setSizes(splitterSizes); } // // Now add the splitters to the main layout // mTopLayout->addWidget(mSplitter1); // Make sure the focus is on the view, and not on the quick search line edit, because otherwise // shortcuts like + or j go to the wrong place. // This would normally be done in the message list itself, but apparently something resets the focus // again, probably all the reparenting we do here. mMessagePane->focusView(); // By default hide th unread and size columns on first run. if (kmkernel->firstStart()) { mFolderTreeWidget->folderTreeView()->hideColumn(1); mFolderTreeWidget->folderTreeView()->hideColumn(3); mFolderTreeWidget->folderTreeView()->header()->resizeSection(0, folderViewWidth * 0.8); } // Make the copy action work, see disconnect comment above if (mMsgView) connect(mMsgView->copyAction(), &QAction::triggered, mMsgView, &KMReaderWin::slotCopySelectedText); } //----------------------------------------------------------------------------- void KMMainWidget::refreshFavoriteFoldersViewProperties() { if (mFavoriteCollectionsView) { if (MailCommon::MailCommonSettings::self()->favoriteCollectionViewMode() == MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::IconMode) { mFavoriteCollectionsView->changeViewMode(QListView::IconMode); } else if (MailCommon::MailCommonSettings::self()->favoriteCollectionViewMode() == MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::ListMode) { mFavoriteCollectionsView->changeViewMode(QListView::ListMode); } else { Q_ASSERT(false); // we should never get here in hidden mode } mFavoriteCollectionsView->setDropActionMenuEnabled(kmkernel->showPopupAfterDnD()); mFavoriteCollectionsView->setWordWrap(true); mFavoriteCollectionsView->updateMode(); } } //----------------------------------------------------------------------------- void KMMainWidget::readConfig() { const bool oldLongFolderList = mLongFolderList; const bool oldReaderWindowActive = mReaderWindowActive; const bool oldReaderWindowBelow = mReaderWindowBelow; const bool oldFavoriteFolderView = mEnableFavoriteFolderView; const bool oldFolderQuickSearch = mEnableFolderQuickSearch; // on startup, the layout is always new and we need to relayout the widgets bool layoutChanged = !mStartupDone; if (mStartupDone) { readPreConfig(); layoutChanged = (oldLongFolderList != mLongFolderList) || (oldReaderWindowActive != mReaderWindowActive) || (oldReaderWindowBelow != mReaderWindowBelow) || (oldFavoriteFolderView != mEnableFavoriteFolderView); if (layoutChanged) { deleteWidgets(); createWidgets(); restoreCollectionFolderViewConfig(); Q_EMIT recreateGui(); } else if (oldFolderQuickSearch != mEnableFolderQuickSearch) { if (mEnableFolderQuickSearch) { mFolderTreeWidget->filterFolderLineEdit()->show(); } else { mFolderTreeWidget->filterFolderLineEdit()->hide(); } } } { // Read the config of the folder views and the header if (mMsgView) { mMsgView->readConfig(); } mMessagePane->reloadGlobalConfiguration(); mFolderTreeWidget->readConfig(); if (mFavoriteCollectionsView) { mFavoriteCollectionsView->readConfig(); } refreshFavoriteFoldersViewProperties(); } { // area for config group "General" if (!mStartupDone) { // check mail on startup // do it after building the kmmainwin, so that the progressdialog is available QTimer::singleShot(0, this, &KMMainWidget::slotCheckMailOnStartup); } } if (layoutChanged) { layoutSplitters(); } updateMessageMenu(); updateFileMenu(); kmkernel->toggleSystemTray(); mAccountActionMenu->setAccountOrder(MailCommon::MailCommonSettings::self()->order()); connect(Akonadi::AgentManager::self(), &AgentManager::instanceAdded, this, &KMMainWidget::updateFileMenu); connect(Akonadi::AgentManager::self(), &AgentManager::instanceRemoved, this, &KMMainWidget::updateFileMenu); } //----------------------------------------------------------------------------- void KMMainWidget::writeConfig(bool force) { // Don't save the sizes of all the widgets when we were never shown. // This can happen in Kontact, where the KMail plugin is automatically // loaded, but not necessarily shown. // This prevents invalid sizes from being saved if (mWasEverShown) { // The height of the header widget can be 0, this happens when the user // did not switch to the header widget onced and the "Welcome to KMail" // HTML widget was shown the whole time int headersHeight = mMessagePane->height(); if (headersHeight == 0) { headersHeight = height() / 2; } KMailSettings::self()->setSearchAndHeaderHeight(headersHeight); KMailSettings::self()->setSearchAndHeaderWidth(mMessagePane->width()); if (mFavoriteCollectionsView) { MailCommon::MailCommonSettings::self()->setFavoriteCollectionViewHeight(mFavoriteCollectionsView->height()); KMailSettings::self()->setFolderTreeHeight(mFolderTreeWidget->height()); if (!mLongFolderList) { KMailSettings::self()->setFolderViewHeight(mFolderViewSplitter->height()); } } else if (!mLongFolderList && mFolderTreeWidget) { KMailSettings::self()->setFolderTreeHeight(mFolderTreeWidget->height()); } if (mFolderTreeWidget) { KMailSettings::self()->setFolderViewWidth(mFolderTreeWidget->width()); KSharedConfig::Ptr config = KMKernel::self()->config(); KConfigGroup group(config, "CollectionFolderView"); ETMViewStateSaver saver; saver.setView(mFolderTreeWidget->folderTreeView()); saver.saveState(group); group.writeEntry("HeaderState", mFolderTreeWidget->folderTreeView()->header()->saveState()); //Work around from startup folder group.deleteEntry("Selection"); #if 0 if (!KMailSettings::self()->startSpecificFolderAtStartup()) { group.deleteEntry("Current"); } #endif group.sync(); } if (mMsgView) { if (!mReaderWindowBelow) { KMailSettings::self()->setReaderWindowWidth(mMsgView->width()); } mMsgView->viewer()->writeConfig(force); KMailSettings::self()->setReaderWindowHeight(mMsgView->height()); } } } void KMMainWidget::writeReaderConfig() { if (mWasEverShown) { if (mMsgView) { mMsgView->viewer()->writeConfig(); } } } KMReaderWin *KMMainWidget::messageView() const { return mMsgView; } CollectionPane *KMMainWidget::messageListPane() const { return mMessagePane; } //----------------------------------------------------------------------------- void KMMainWidget::deleteWidgets() { // Simply delete the top splitter, which always is mSplitter1, regardless // of the layout. This deletes all children. // akonadi action manager is created in createWidgets(), parented to this // so not autocleaned up. delete mAkonadiStandardActionManager; mAkonadiStandardActionManager = Q_NULLPTR; delete mSplitter1; mMsgView = Q_NULLPTR; mSearchAndTree = Q_NULLPTR; mFolderViewSplitter = Q_NULLPTR; mFavoriteCollectionsView = Q_NULLPTR; mSplitter1 = Q_NULLPTR; mSplitter2 = Q_NULLPTR; mFavoritesModel = Q_NULLPTR; } //----------------------------------------------------------------------------- void KMMainWidget::createWidgets() { // Note that all widgets we create in this function have the parent 'this'. // They will be properly reparented in layoutSplitters() // // Create header view and search bar // FolderTreeWidget::TreeViewOptions opt = FolderTreeWidget::ShowUnreadCount; opt |= FolderTreeWidget::UseLineEditForFiltering; opt |= FolderTreeWidget::ShowCollectionStatisticAnimation; opt |= FolderTreeWidget::DontKeyFilter; mFolderTreeWidget = new FolderTreeWidget(this, mGUIClient, opt); connect(mFolderTreeWidget->folderTreeView(), SIGNAL(currentChanged(Akonadi::Collection)), this, SLOT(slotFolderChanged(Akonadi::Collection))); connect(mFolderTreeWidget->folderTreeView()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KMMainWidget::updateFolderMenu); connect(mFolderTreeWidget->folderTreeView(), &FolderTreeView::prefereCreateNewTab, this, &KMMainWidget::slotCreateNewTab); mFolderTreeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); mMessagePane = new CollectionPane(!KMailSettings::self()->startSpecificFolderAtStartup(), KMKernel::self()->entityTreeModel(), mFolderTreeWidget->folderTreeView()->selectionModel(), this); connect(KMKernel::self()->entityTreeModel(), &Akonadi::EntityTreeModel::collectionFetched, this, &KMMainWidget::slotCollectionFetched); - mMessagePane->hide();// hide the search bar + mMessagePane->show(); mMessagePane->setXmlGuiClient(mGUIClient); connect(mMessagePane, &MessageList::Pane::messageSelected, this, &KMMainWidget::slotMessageSelected); connect(mMessagePane, &MessageList::Pane::selectionChanged, this, &KMMainWidget::startUpdateMessageActionsTimer); connect(mMessagePane, &CollectionPane::currentTabChanged, this, &KMMainWidget::refreshMessageListSelection); connect(mMessagePane, &MessageList::Pane::messageActivated, this, &KMMainWidget::slotMessageActivated); connect(mMessagePane, &MessageList::Pane::messageStatusChangeRequest, this, &KMMainWidget::slotMessageStatusChangeRequest); connect(mMessagePane, &MessageList::Pane::statusMessage, BroadcastStatus::instance(), &KPIM::BroadcastStatus::setStatusMsg); // // Create the reader window // if (mReaderWindowActive) { mMsgView = new KMReaderWin(this, this, actionCollection(), Q_NULLPTR); if (mMsgActions) { mMsgActions->setMessageView(mMsgView); } connect(mMsgView->viewer(), &MessageViewer::Viewer::replaceMsgByUnencryptedVersion, this, &KMMainWidget::slotReplaceMsgByUnencryptedVersion); connect(mMsgView->viewer(), &MessageViewer::Viewer::popupMenu, this, &KMMainWidget::slotMessagePopup); connect(mMsgView->viewer(), &MessageViewer::Viewer::moveMessageToTrash, this, &KMMainWidget::slotMoveMessageToTrash); if (mShowIntroductionAction) { mShowIntroductionAction->setEnabled(true); } } else { if (mMsgActions) { mMsgActions->setMessageView(Q_NULLPTR); } if (mShowIntroductionAction) { mShowIntroductionAction->setEnabled(false); } } // // Create the folder tree // the "folder tree" consists of a quicksearch input field and the tree itself // mSearchAndTree = new QWidget(this); QVBoxLayout *vboxlayout = new QVBoxLayout; vboxlayout->setMargin(0); mSearchAndTree->setLayout(vboxlayout); vboxlayout->addWidget(mFolderTreeWidget); if (!KMailSettings::self()->enableFolderQuickSearch()) { mFolderTreeWidget->filterFolderLineEdit()->hide(); } // // Create the favorite folder view // mAkonadiStandardActionManager = new Akonadi::StandardMailActionManager(mGUIClient->actionCollection(), this); connect(mAkonadiStandardActionManager, &Akonadi::StandardMailActionManager::actionStateUpdated, this, &KMMainWidget::slotAkonadiStandardActionUpdated); mAkonadiStandardActionManager->setCollectionSelectionModel(mFolderTreeWidget->folderTreeView()->selectionModel()); mAkonadiStandardActionManager->setItemSelectionModel(mMessagePane->currentItemSelectionModel()); if (mEnableFavoriteFolderView) { mFavoriteCollectionsView = new FavoriteCollectionWidget(mGUIClient, this); refreshFavoriteFoldersViewProperties(); connect(mFavoriteCollectionsView, SIGNAL(currentChanged(Akonadi::Collection)), this, SLOT(slotFolderChanged(Akonadi::Collection))); mFavoritesModel = new Akonadi::FavoriteCollectionsModel( mFolderTreeWidget->folderTreeView()->model(), KMKernel::self()->config()->group("FavoriteCollections"), this); mFavoriteCollectionsView->setModel(mFavoritesModel); mAkonadiStandardActionManager->setFavoriteCollectionsModel(mFavoritesModel); mAkonadiStandardActionManager->setFavoriteSelectionModel(mFavoriteCollectionsView->selectionModel()); } //Don't use mMailActionManager->createAllActions() to save memory by not //creating actions that doesn't make sense. QList standardActions; standardActions << StandardActionManager::CreateCollection << StandardActionManager::CopyCollections << StandardActionManager::DeleteCollections << StandardActionManager::SynchronizeCollections << StandardActionManager::CollectionProperties << StandardActionManager::CopyItems << StandardActionManager::Paste << StandardActionManager::DeleteItems << StandardActionManager::ManageLocalSubscriptions << StandardActionManager::CopyCollectionToMenu << StandardActionManager::CopyItemToMenu << StandardActionManager::MoveItemToMenu << StandardActionManager::MoveCollectionToMenu << StandardActionManager::CutItems << StandardActionManager::CutCollections << StandardActionManager::CreateResource << StandardActionManager::DeleteResources << StandardActionManager::ResourceProperties << StandardActionManager::SynchronizeResources << StandardActionManager::ToggleWorkOffline << StandardActionManager::SynchronizeCollectionsRecursive; Q_FOREACH (StandardActionManager::Type standardAction, standardActions) { mAkonadiStandardActionManager->createAction(standardAction); } if (mEnableFavoriteFolderView) { QList favoriteActions; favoriteActions << StandardActionManager::AddToFavoriteCollections << StandardActionManager::RemoveFromFavoriteCollections << StandardActionManager::RenameFavoriteCollection << StandardActionManager::SynchronizeFavoriteCollections; Q_FOREACH (StandardActionManager::Type favoriteAction, favoriteActions) { mAkonadiStandardActionManager->createAction(favoriteAction); } } QList mailActions; mailActions << StandardMailActionManager::MarkAllMailAsRead << StandardMailActionManager::MoveToTrash << StandardMailActionManager::MoveAllToTrash << StandardMailActionManager::RemoveDuplicates << StandardMailActionManager::EmptyAllTrash << StandardMailActionManager::MarkMailAsRead << StandardMailActionManager::MarkMailAsUnread << StandardMailActionManager::MarkMailAsImportant << StandardMailActionManager::MarkMailAsActionItem; Q_FOREACH (StandardMailActionManager::Type mailAction, mailActions) { mAkonadiStandardActionManager->createAction(mailAction); } mAkonadiStandardActionManager->interceptAction(Akonadi::StandardActionManager::CollectionProperties); connect(mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CollectionProperties), &QAction::triggered, mManageShowCollectionProperties, &ManageShowCollectionProperties::slotCollectionProperties); // // Create all kinds of actions // mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::RemoveDuplicates)->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Asterisk)); mAkonadiStandardActionManager->interceptAction(Akonadi::StandardMailActionManager::RemoveDuplicates); connect(mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::RemoveDuplicates), &QAction::triggered, this, &KMMainWidget::slotRemoveDuplicates); { mCollectionProperties = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CollectionProperties); } connect(kmkernel->folderCollectionMonitor(), &Monitor::collectionRemoved, this, &KMMainWidget::slotCollectionRemoved); connect(kmkernel->folderCollectionMonitor(), &Monitor::itemAdded, this, &KMMainWidget::slotItemAdded); connect(kmkernel->folderCollectionMonitor(), &Monitor::itemRemoved, this, &KMMainWidget::slotItemRemoved); connect(kmkernel->folderCollectionMonitor(), &Monitor::itemMoved, this, &KMMainWidget::slotItemMoved); connect(kmkernel->folderCollectionMonitor(), SIGNAL(collectionChanged(Akonadi::Collection,QSet)), SLOT(slotCollectionChanged(Akonadi::Collection,QSet))); connect(kmkernel->folderCollectionMonitor(), &Monitor::collectionStatisticsChanged, this, &KMMainWidget::slotCollectionStatisticsChanged); } void KMMainWidget::updateMoveAction(const Akonadi::CollectionStatistics &statistic) { const bool hasUnreadMails = (statistic.unreadCount() > 0); const bool hasMails = (statistic.count() > 0); updateMoveAction(hasUnreadMails, hasMails); } void KMMainWidget::updateMoveAction(bool hasUnreadMails, bool hasMails) { const bool enable_goto_unread = hasUnreadMails || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllFolders) || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllMarkedFolders); actionCollection()->action(QStringLiteral("go_next_message"))->setEnabled(hasMails); actionCollection()->action(QStringLiteral("go_next_unread_message"))->setEnabled(enable_goto_unread); actionCollection()->action(QStringLiteral("go_prev_message"))->setEnabled(hasMails); actionCollection()->action(QStringLiteral("go_prev_unread_message"))->setEnabled(enable_goto_unread); if (mAkonadiStandardActionManager && mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkAllMailAsRead)) { mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkAllMailAsRead)->setEnabled(hasUnreadMails); } } void KMMainWidget::updateAllToTrashAction(int statistics) { bool multiFolder = false; if (mFolderTreeWidget) { multiFolder = mFolderTreeWidget->selectedCollections().count() > 1; } if (mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)) { const bool folderWithContent = mCurrentFolder && !mCurrentFolder->isStructural(); mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)->setEnabled(folderWithContent && (statistics > 0) && mCurrentFolder->canDeleteMessages() && !multiFolder); } } void KMMainWidget::slotCollectionStatisticsChanged(Akonadi::Collection::Id id, const Akonadi::CollectionStatistics &statistic) { if (id == CommonKernel->outboxCollectionFolder().id()) { const bool enableAction = (statistic.count() > 0); mSendQueued->setEnabled(enableAction); mSendActionMenu->setEnabled(enableAction); } else if (mCurrentFolder && (id == mCurrentFolder->collection().id())) { updateMoveAction(statistic); updateAllToTrashAction(statistic.count()); mCurrentFolder->setCollection(MailCommon::Util::updatedCollection(mCurrentFolder->collection())); } } void KMMainWidget::slotCreateNewTab(bool preferNewTab) { mMessagePane->setPreferEmptyTab(preferNewTab); } void KMMainWidget::slotCollectionChanged(const Akonadi::Collection &collection, const QSet &set) { if (mCurrentFolder && (collection == mCurrentFolder->collection()) && (set.contains("MESSAGEFOLDER") || set.contains("expirationcollectionattribute"))) { if (set.contains("MESSAGEFOLDER")) { mMessagePane->resetModelStorage(); } else { mCurrentFolder->setCollection(collection); } } else if (set.contains("ENTITYDISPLAY") || set.contains("NAME")) { const QModelIndex idx = Akonadi::EntityTreeModel::modelIndexForCollection(KMKernel::self()->collectionModel(), collection); if (idx.isValid()) { const QString text = idx.data().toString(); const QIcon icon = idx.data(Qt::DecorationRole).value(); mMessagePane->updateTabIconText(collection, text, icon); } } } void KMMainWidget::slotItemAdded(const Akonadi::Item &msg, const Akonadi::Collection &col) { Q_UNUSED(msg); if (col.isValid()) { if (col == CommonKernel->outboxCollectionFolder()) { startUpdateMessageActionsTimer(); } } } void KMMainWidget::slotItemRemoved(const Akonadi::Item &item) { if (item.isValid() && item.parentCollection().isValid() && (item.parentCollection() == CommonKernel->outboxCollectionFolder())) { startUpdateMessageActionsTimer(); } } void KMMainWidget::slotItemMoved(const Akonadi::Item &item, const Akonadi::Collection &from, const Akonadi::Collection &to) { if (item.isValid() && ((from.id() == CommonKernel->outboxCollectionFolder().id()) || to.id() == CommonKernel->outboxCollectionFolder().id())) { startUpdateMessageActionsTimer(); } } //------------------------------------------------------------------------- void KMMainWidget::slotFocusQuickSearch() { const QString text = mMsgView ? mMsgView->copyText() : QString(); mMessagePane->focusQuickSearch(text); } //------------------------------------------------------------------------- bool KMMainWidget::slotSearch() { if (!mSearchWin) { mSearchWin = new SearchWindow(this, mCurrentFolder ? mCurrentFolder->collection() : Akonadi::Collection()); mSearchWin->setModal(false); mSearchWin->setObjectName(QStringLiteral("Search")); } else { mSearchWin->activateFolder(mCurrentFolder ? mCurrentFolder->collection() : Akonadi::Collection()); } mSearchWin->show(); KWindowSystem::activateWindow(mSearchWin->winId()); return true; } //----------------------------------------------------------------------------- void KMMainWidget::slotHelp() { KHelpClient::invokeHelp(); } //----------------------------------------------------------------------------- void KMMainWidget::slotFilter() { FilterIf->openFilterDialog(true); } void KMMainWidget::slotManageSieveScripts() { if (!kmkernel->askToGoOnline()) { return; } if (mManageSieveDialog) { return; } mManageSieveDialog = new KSieveUi::ManageSieveScriptsDialog; connect(mManageSieveDialog.data(), &KSieveUi::ManageSieveScriptsDialog::finished, this, &KMMainWidget::slotCheckVacation); mManageSieveDialog->show(); } //----------------------------------------------------------------------------- void KMMainWidget::slotCheckMail() { kmkernel->checkMail(); } //----------------------------------------------------------------------------- void KMMainWidget::slotCheckMailOnStartup() { kmkernel->checkMailOnStartup(); } void KMMainWidget::slotCompose() { KMail::Composer *win; KMime::Message::Ptr msg(new KMime::Message()); bool forceCursorPosition = false; if (mCurrentFolder) { MessageHelper::initHeader(msg, KMKernel::self()->identityManager(), mCurrentFolder->identity()); //Laurent: bug 289905 /* if ( mCurrentFolder->collection().isValid() && mCurrentFolder->putRepliesInSameFolder() ) { KMime::Headers::Generic *header = new KMime::Headers::Generic( "X-KMail-Fcc", msg.get(), QString::number( mCurrentFolder->collection().id() ), "utf-8" ); msg->setHeader( header ); } */ TemplateParser::TemplateParser parser(msg, TemplateParser::TemplateParser::NewMessage); parser.setIdentityManager(KMKernel::self()->identityManager()); parser.process(msg, mCurrentFolder->collection()); win = KMail::makeComposer(msg, false, false, KMail::Composer::New, mCurrentFolder->identity()); win->setCollectionForNewMessage(mCurrentFolder->collection()); forceCursorPosition = parser.cursorPositionWasSet(); } else { MessageHelper::initHeader(msg, KMKernel::self()->identityManager()); TemplateParser::TemplateParser parser(msg, TemplateParser::TemplateParser::NewMessage); parser.setIdentityManager(KMKernel::self()->identityManager()); parser.process(KMime::Message::Ptr(), Akonadi::Collection()); win = KMail::makeComposer(msg, false, false, KMail::Composer::New); forceCursorPosition = parser.cursorPositionWasSet(); } if (forceCursorPosition) { win->setFocusToEditor(); } win->show(); } //----------------------------------------------------------------------------- // TODO: do we want the list sorted alphabetically? void KMMainWidget::slotShowNewFromTemplate() { if (mCurrentFolder) { const KIdentityManagement::Identity &ident = kmkernel->identityManager()->identityForUoidOrDefault(mCurrentFolder->identity()); mTemplateFolder = CommonKernel->collectionFromId(ident.templates().toLongLong()); } if (!mTemplateFolder.isValid()) { mTemplateFolder = CommonKernel->templatesCollectionFolder(); } if (!mTemplateFolder.isValid()) { return; } mTemplateMenu->menu()->clear(); Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(mTemplateFolder); job->fetchScope().setAncestorRetrieval(ItemFetchScope::Parent); job->fetchScope().fetchFullPayload(); connect(job, &Akonadi::ItemFetchJob::result, this, &KMMainWidget::slotDelayedShowNewFromTemplate); } void KMMainWidget::slotDelayedShowNewFromTemplate(KJob *job) { Akonadi::ItemFetchJob *fetchJob = qobject_cast(job); const Akonadi::Item::List items = fetchJob->items(); const int numberOfItems = items.count(); for (int idx = 0; idx < numberOfItems; ++idx) { KMime::Message::Ptr msg = MessageCore::Util::message(items.at(idx)); if (msg) { QString subj = msg->subject()->asUnicodeString(); if (subj.isEmpty()) { subj = i18n("No Subject"); } QAction *templateAction = mTemplateMenu->menu()->addAction(KStringHandler::rsqueeze(subj.replace(QLatin1Char('&'), QStringLiteral("&&")))); QVariant var; var.setValue(items.at(idx)); templateAction->setData(var); } } // If there are no templates available, add a menu entry which informs // the user about this. if (mTemplateMenu->menu()->actions().isEmpty()) { QAction *noAction = mTemplateMenu->menu()->addAction( i18n("(no templates)")); noAction->setEnabled(false); } } //----------------------------------------------------------------------------- void KMMainWidget::slotNewFromTemplate(QAction *action) { if (!mTemplateFolder.isValid()) { return; } const Akonadi::Item item = action->data().value(); newFromTemplate(item); } //----------------------------------------------------------------------------- void KMMainWidget::newFromTemplate(const Akonadi::Item &msg) { if (!msg.isValid()) { return; } KMCommand *command = new KMUseTemplateCommand(this, msg); command->start(); } //----------------------------------------------------------------------------- void KMMainWidget::slotPostToML() { if (mCurrentFolder && mCurrentFolder->isMailingListEnabled()) { if (KMail::Util::mailingListPost(mCurrentFolder)) { return; } } slotCompose(); } void KMMainWidget::slotExpireFolder() { if (!mCurrentFolder) { return; } bool mustDeleteExpirationAttribute = false; MailCommon::ExpireCollectionAttribute *attr = MailCommon::Util::expirationCollectionAttribute(mCurrentFolder->collection(), mustDeleteExpirationAttribute); bool canBeExpired = true; if (!attr->isAutoExpire()) { canBeExpired = false; } else if (attr->unreadExpireUnits() == MailCommon::ExpireCollectionAttribute::ExpireNever && attr->readExpireUnits() == MailCommon::ExpireCollectionAttribute::ExpireNever) { canBeExpired = false; } if (!canBeExpired) { const QString message = i18n("This folder does not have any expiry options set"); KMessageBox::information(this, message); if (mustDeleteExpirationAttribute) { delete attr; } return; } if (KMailSettings::self()->warnBeforeExpire()) { const QString message = i18n("Are you sure you want to expire the folder %1?", mCurrentFolder->name().toHtmlEscaped()); if (KMessageBox::warningContinueCancel(this, message, i18n("Expire Folder"), KGuiItem(i18n("&Expire"))) != KMessageBox::Continue) { if (mustDeleteExpirationAttribute) { delete attr; } return; } } MailCommon::Util::expireOldMessages(mCurrentFolder->collection(), true /*immediate*/); if (mustDeleteExpirationAttribute) { delete attr; } } //----------------------------------------------------------------------------- void KMMainWidget::slotEmptyFolder() { if (!mCurrentFolder) { return; } const bool isTrash = CommonKernel->folderIsTrash(mCurrentFolder->collection()); if (KMailSettings::self()->confirmBeforeEmpty()) { const QString title = (isTrash) ? i18n("Empty Trash") : i18n("Move to Trash"); const QString text = (isTrash) ? i18n("Are you sure you want to empty the trash folder?") : i18n("Are you sure you want to move all messages from " "folder %1 to the trash?", mCurrentFolder->name().toHtmlEscaped()); if (KMessageBox::warningContinueCancel(this, text, title, KGuiItem(title, QStringLiteral("user-trash"))) != KMessageBox::Continue) { return; } } #ifndef QT_NO_CURSOR KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy()); #endif slotSelectAllMessages(); if (isTrash) { /* Don't ask for confirmation again when deleting, the user has already confirmed. */ slotDeleteMsg(false); } else { slotTrashSelectedMessages(); } if (mMsgView) { mMsgView->clearCache(); } if (!isTrash) { BroadcastStatus::instance()->setStatusMsg(i18n("Moved all messages to the trash")); } updateMessageActions(); // Disable empty trash/move all to trash action - we've just deleted/moved // all folder contents. mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)->setEnabled(false); } //----------------------------------------------------------------------------- void KMMainWidget::slotArchiveFolder() { if (mCurrentFolder && mCurrentFolder->collection().isValid()) { KMail::ArchiveFolderDialog archiveDialog; archiveDialog.setFolder(mCurrentFolder->collection()); archiveDialog.exec(); } } //----------------------------------------------------------------------------- void KMMainWidget::slotRemoveFolder() { if (!mCurrentFolder) { return; } if (!mCurrentFolder->collection().isValid()) { return; } if (mCurrentFolder->isSystemFolder()) { return; } if (mCurrentFolder->isReadOnly()) { return; } RemoveCollectionJob *job = new RemoveCollectionJob(this); connect(job, &RemoveCollectionJob::clearCurrentFolder, this, &KMMainWidget::slotClearCurrentFolder); job->setMainWidget(this); job->setCurrentFolder(mCurrentFolder->collection()); job->start(); } void KMMainWidget::slotClearCurrentFolder() { mCurrentFolder.clear(); } //----------------------------------------------------------------------------- void KMMainWidget::slotExpireAll() { if (KMailSettings::self()->warnBeforeExpire()) { const int ret = KMessageBox::warningContinueCancel(KMainWindow::memberList().first(), i18n("Are you sure you want to expire all old messages?"), i18n("Expire Old Messages?"), KGuiItem(i18n("Expire"))); if (ret != KMessageBox::Continue) { return; } } kmkernel->expireAllFoldersNow(); } //----------------------------------------------------------------------------- void KMMainWidget::slotOverrideHtmlLoadExt() { if (mHtmlLoadExtGlobalSetting == mFolderHtmlLoadExtPreference) { int result = KMessageBox::warningContinueCancel(this, // the warning text is taken from configuredialog.cpp: i18n("Loading external references in html mail will make you more vulnerable to " "\"spam\" and may increase the likelihood that your system will be " "compromised by other present and anticipated security exploits."), i18n("Security Warning"), KGuiItem(i18n("Load External References")), KStandardGuiItem::cancel(), QStringLiteral("OverrideHtmlLoadExtWarning"), Q_NULLPTR); if (result == KMessageBox::Cancel) { mPreferHtmlLoadExtAction->setChecked(false); return; } } mFolderHtmlLoadExtPreference = !mFolderHtmlLoadExtPreference; if (mMsgView) { mMsgView->setHtmlLoadExtOverride(mFolderHtmlLoadExtPreference); mMsgView->update(true); } } //----------------------------------------------------------------------------- void KMMainWidget::slotMessageQueuedOrDrafted() { if (!CommonKernel->folderIsDraftOrOutbox(mCurrentFolder->collection())) { return; } if (mMsgView) { mMsgView->update(true); } } //----------------------------------------------------------------------------- void KMMainWidget::slotForwardInlineMsg() { if (!mCurrentFolder) { return; } const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } KMForwardCommand *command = new KMForwardCommand( this, selectedMessages, mCurrentFolder->identity() ); command->start(); } //----------------------------------------------------------------------------- void KMMainWidget::slotForwardAttachedMsg() { if (!mCurrentFolder) { return; } const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } KMForwardAttachedCommand *command = new KMForwardAttachedCommand( this, selectedMessages, mCurrentFolder->identity() ); command->start(); } //----------------------------------------------------------------------------- void KMMainWidget::slotUseTemplate() { newFromTemplate(mMessagePane->currentItem()); } //----------------------------------------------------------------------------- void KMMainWidget::slotResendMsg() { const Akonadi::Item msg = mMessagePane->currentItem(); if (!msg.isValid()) { return; } KMCommand *command = new KMResendMessageCommand(this, msg); command->start(); } //----------------------------------------------------------------------------- // Message moving and permanent deletion // void KMMainWidget::moveMessageSelected(MessageList::Core::MessageItemSetReference ref, const Akonadi::Collection &dest, bool confirmOnDeletion) { Akonadi::Item::List selectMsg = mMessagePane->itemListFromPersistentSet(ref); // If this is a deletion, ask for confirmation if (confirmOnDeletion) { int ret = KMessageBox::warningContinueCancel( this, i18np( "Do you really want to delete the selected message?
" "Once deleted, it cannot be restored.
", "Do you really want to delete the %1 selected messages?
" "Once deleted, they cannot be restored.
", selectMsg.count() ), selectMsg.count() > 1 ? i18n("Delete Messages") : i18n("Delete Message"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QStringLiteral("NoConfirmDelete") ); if (ret == KMessageBox::Cancel) { mMessagePane->deletePersistentSet(ref); return; // user canceled the action } } mMessagePane->markMessageItemsAsAboutToBeRemoved(ref, true); // And stuff them into a KMMoveCommand :) KMMoveCommand *command = new KMMoveCommand(dest, selectMsg, ref); QObject::connect( command, &KMMoveCommand::moveDone, this, &KMMainWidget::slotMoveMessagesCompleted ); command->start(); if (dest.isValid()) { BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages...")); } else { BroadcastStatus::instance()->setStatusMsg(i18n("Deleting messages...")); } } void KMMainWidget::slotMoveMessagesCompleted(KMMoveCommand *command) { Q_ASSERT(command); mMessagePane->markMessageItemsAsAboutToBeRemoved(command->refSet(), false); mMessagePane->deletePersistentSet(command->refSet()); // Bleah :D const bool moveWasReallyADelete = !command->destFolder().isValid(); if (command->result() == KMCommand::OK) { if (moveWasReallyADelete) { BroadcastStatus::instance()->setStatusMsg(i18n("Messages deleted successfully.")); } else { BroadcastStatus::instance()->setStatusMsg(i18n("Messages moved successfully.")); } } else { if (moveWasReallyADelete) { if (command->result() == KMCommand::Failed) { BroadcastStatus::instance()->setStatusMsg(i18n("Deleting messages failed.")); } else { BroadcastStatus::instance()->setStatusMsg(i18n("Deleting messages canceled.")); } } else { if (command->result() == KMCommand::Failed) { BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages failed.")); } else { BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages canceled.")); } } } // The command will autodelete itself and will also kill the set. } void KMMainWidget::slotDeleteMessages() { slotDeleteMsg(true); } void KMMainWidget::slotDeleteMsg(bool confirmDelete) { // Create a persistent message set from the current selection MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet(); if (ref != -1) { moveMessageSelected(ref, Akonadi::Collection(), confirmDelete); } } void KMMainWidget::slotDeleteThread(bool confirmDelete) { // Create a persistent set from the current thread. MessageList::Core::MessageItemSetReference ref = mMessagePane->currentThreadAsPersistentSet(); if (ref != -1) { moveMessageSelected(ref, Akonadi::Collection(), confirmDelete); } } FolderSelectionDialog *KMMainWidget::moveOrCopyToDialog() { if (!mMoveOrCopyToDialog) { FolderSelectionDialog::SelectionFolderOption options = FolderSelectionDialog::HideVirtualFolder; mMoveOrCopyToDialog = new FolderSelectionDialog(this, options); mMoveOrCopyToDialog->setModal(true); } return mMoveOrCopyToDialog; } FolderSelectionDialog *KMMainWidget::selectFromAllFoldersDialog() { if (!mSelectFromAllFoldersDialog) { FolderSelectionDialog::SelectionFolderOptions options = FolderSelectionDialog::None; options |= FolderSelectionDialog::NotAllowToCreateNewFolder; mSelectFromAllFoldersDialog = new FolderSelectionDialog(this, options); mSelectFromAllFoldersDialog->setModal(true); } return mSelectFromAllFoldersDialog; } void KMMainWidget::slotMoveSelectedMessageToFolder() { QPointer dialog(moveOrCopyToDialog()); dialog->setWindowTitle(i18n("Move Messages to Folder")); if (dialog->exec() && dialog) { const Akonadi::Collection dest = dialog->selectedCollection(); if (dest.isValid()) { moveSelectedMessagesToFolder(dest); } } } void KMMainWidget::moveSelectedMessagesToFolder(const Akonadi::Collection &dest) { MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet(); if (ref != -1) { //Need to verify if dest == src ??? akonadi do it for us. moveMessageSelected(ref, dest, false); } } void KMMainWidget::copyMessageSelected(const Akonadi::Item::List &selectMsg, const Akonadi::Collection &dest) { if (selectMsg.isEmpty()) { return; } // And stuff them into a KMCopyCommand :) KMCommand *command = new KMCopyCommand(dest, selectMsg); QObject::connect( command, &KMCommand::completed, this, &KMMainWidget::slotCopyMessagesCompleted ); command->start(); BroadcastStatus::instance()->setStatusMsg(i18n("Copying messages...")); } void KMMainWidget::slotCopyMessagesCompleted(KMCommand *command) { Q_ASSERT(command); if (command->result() == KMCommand::OK) { BroadcastStatus::instance()->setStatusMsg(i18n("Messages copied successfully.")); } else { if (command->result() == KMCommand::Failed) { BroadcastStatus::instance()->setStatusMsg(i18n("Copying messages failed.")); } else { BroadcastStatus::instance()->setStatusMsg(i18n("Copying messages canceled.")); } } // The command will autodelete itself and will also kill the set. } void KMMainWidget::slotCopySelectedMessagesToFolder() { QPointer dialog(moveOrCopyToDialog()); dialog->setWindowTitle(i18n("Copy Messages to Folder")); if (dialog->exec() && dialog) { const Akonadi::Collection dest = dialog->selectedCollection(); if (dest.isValid()) { copySelectedMessagesToFolder(dest); } } } void KMMainWidget::copySelectedMessagesToFolder(const Akonadi::Collection &dest) { const Akonadi::Item::List lstMsg = mMessagePane->selectionAsMessageItemList(); if (!lstMsg.isEmpty()) { copyMessageSelected(lstMsg, dest); } } //----------------------------------------------------------------------------- // Message trashing // void KMMainWidget::trashMessageSelected(MessageList::Core::MessageItemSetReference ref) { if (!mCurrentFolder) { return; } const Akonadi::Item::List select = mMessagePane->itemListFromPersistentSet(ref); mMessagePane->markMessageItemsAsAboutToBeRemoved(ref, true); // FIXME: Why we don't use KMMoveCommand( trashFolder(), selectedMessages ); ? // And stuff them into a KMTrashMsgCommand :) KMCommand *command = new KMTrashMsgCommand(mCurrentFolder->collection(), select, ref); QObject::connect( command, SIGNAL(moveDone(KMMoveCommand*)), this, SLOT(slotTrashMessagesCompleted(KMMoveCommand*)) ); command->start(); BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages to trash...")); } void KMMainWidget::slotTrashMessagesCompleted(KMMoveCommand *command) { Q_ASSERT(command); mMessagePane->markMessageItemsAsAboutToBeRemoved(command->refSet(), false); mMessagePane->deletePersistentSet(command->refSet()); if (command->result() == KMCommand::OK) { BroadcastStatus::instance()->setStatusMsg(i18n("Messages moved to trash successfully.")); } else { if (command->result() == KMCommand::Failed) { BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages to trash failed.")); } else { BroadcastStatus::instance()->setStatusMsg(i18n("Moving messages to trash canceled.")); } } // The command will autodelete itself and will also kill the set. } void KMMainWidget::slotTrashSelectedMessages() { MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet(); if (ref != -1) { trashMessageSelected(ref); } } void KMMainWidget::slotTrashThread() { MessageList::Core::MessageItemSetReference ref = mMessagePane->currentThreadAsPersistentSet(); if (ref != -1) { trashMessageSelected(ref); } } //----------------------------------------------------------------------------- // Message tag setting for messages // // FIXME: The "selection" version of these functions is in MessageActions. // We should probably move everything there.... void KMMainWidget::toggleMessageSetTag(const Akonadi::Item::List &select, const Akonadi::Tag &tag) { if (select.isEmpty()) { return; } KMCommand *command = new KMSetTagCommand(Akonadi::Tag::List() << tag, select, KMSetTagCommand::Toggle); command->start(); } void KMMainWidget::slotSelectMoreMessageTagList() { const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } TagSelectDialog dlg(this, selectedMessages.count(), selectedMessages.first()); dlg.setActionCollection(QList() << actionCollection()); if (dlg.exec()) { const Akonadi::Tag::List lst = dlg.selectedTag(); KMCommand *command = new KMSetTagCommand(lst, selectedMessages, KMSetTagCommand::CleanExistingAndAddNew); command->start(); } } void KMMainWidget::slotUpdateMessageTagList(const Akonadi::Tag &tag) { // Create a persistent set from the current thread. const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } toggleMessageSetTag(selectedMessages, tag); } void KMMainWidget::refreshMessageListSelection() { mAkonadiStandardActionManager->setItemSelectionModel(mMessagePane->currentItemSelectionModel()); slotMessageSelected(mMessagePane->currentItem()); } //----------------------------------------------------------------------------- // Status setting for threads // // FIXME: The "selection" version of these functions is in MessageActions. // We should probably move everything there.... void KMMainWidget::setMessageSetStatus(const Akonadi::Item::List &select, const Akonadi::MessageStatus &status, bool toggle) { KMCommand *command = new KMSetStatusCommand(status, select, toggle); command->start(); } void KMMainWidget::setCurrentThreadStatus(const Akonadi::MessageStatus &status, bool toggle) { const Akonadi::Item::List select = mMessagePane->currentThreadAsMessageList(); if (select.isEmpty()) { return; } setMessageSetStatus(select, status, toggle); } void KMMainWidget::slotSetThreadStatusUnread() { setCurrentThreadStatus(MessageStatus::statusRead(), true); } void KMMainWidget::slotSetThreadStatusImportant() { setCurrentThreadStatus(MessageStatus::statusImportant(), true); } void KMMainWidget::slotSetThreadStatusRead() { setCurrentThreadStatus(MessageStatus::statusRead(), false); } void KMMainWidget::slotSetThreadStatusToAct() { setCurrentThreadStatus(MessageStatus::statusToAct(), true); } void KMMainWidget::slotSetThreadStatusWatched() { setCurrentThreadStatus(MessageStatus::statusWatched(), true); if (mWatchThreadAction->isChecked()) { mIgnoreThreadAction->setChecked(false); } } void KMMainWidget::slotSetThreadStatusIgnored() { setCurrentThreadStatus(MessageStatus::statusIgnored(), true); if (mIgnoreThreadAction->isChecked()) { mWatchThreadAction->setChecked(false); } } //----------------------------------------------------------------------------- void KMMainWidget::slotRedirectMsg() { const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } KMCommand *command = new KMRedirectCommand(this, selectedMessages); command->start(); } //----------------------------------------------------------------------------- void KMMainWidget::slotCustomReplyToMsg(const QString &tmpl) { const Akonadi::Item msg = mMessagePane->currentItem(); if (!msg.isValid()) { return; } const QString text = mMsgView ? mMsgView->copyText() : QString(); qCDebug(KMAIL_LOG) << "Reply with template:" << tmpl; KMCommand *command = new KMReplyCommand(this, msg, MessageComposer::ReplySmart, text, false, tmpl); command->start(); } //----------------------------------------------------------------------------- void KMMainWidget::slotCustomReplyAllToMsg(const QString &tmpl) { const Akonadi::Item msg = mMessagePane->currentItem(); if (!msg.isValid()) { return; } const QString text = mMsgView ? mMsgView->copyText() : QString(); qCDebug(KMAIL_LOG) << "Reply to All with template:" << tmpl; KMCommand *command = new KMReplyCommand(this, msg, MessageComposer::ReplyAll, text, false, tmpl ); command->start(); } //----------------------------------------------------------------------------- void KMMainWidget::slotCustomForwardMsg(const QString &tmpl) { if (!mCurrentFolder) { return; } const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } qCDebug(KMAIL_LOG) << "Forward with template:" << tmpl; KMForwardCommand *command = new KMForwardCommand( this, selectedMessages, mCurrentFolder->identity(), tmpl ); command->start(); } void KMMainWidget::openFilterDialog(const QByteArray &field, const QString &value) { FilterIf->openFilterDialog(false); FilterIf->createFilter(field, value); } //----------------------------------------------------------------------------- void KMMainWidget::slotSubjectFilter() { const KMime::Message::Ptr msg = mMessagePane->currentMessage(); if (!msg) { return; } openFilterDialog("Subject", msg->subject()->asUnicodeString()); } //----------------------------------------------------------------------------- void KMMainWidget::slotFromFilter() { KMime::Message::Ptr msg = mMessagePane->currentMessage(); if (!msg) { return; } AddrSpecList al = MessageHelper::extractAddrSpecs(msg, "From"); if (al.empty()) { openFilterDialog("From", msg->from()->asUnicodeString()); } else { openFilterDialog("From", al.front().asString()); } } //----------------------------------------------------------------------------- void KMMainWidget::slotToFilter() { KMime::Message::Ptr msg = mMessagePane->currentMessage(); if (!msg) { return; } openFilterDialog("To", msg->to()->asUnicodeString()); } void KMMainWidget::slotCcFilter() { KMime::Message::Ptr msg = mMessagePane->currentMessage(); if (!msg) { return; } openFilterDialog("Cc", msg->cc()->asUnicodeString()); } void KMMainWidget::slotBandwidth(bool b) { PimCommon::NetworkUtil::self()->setLowBandwidth(b); } //----------------------------------------------------------------------------- void KMMainWidget::slotUndo() { kmkernel->undoStack()->undo(); updateMessageActions(); updateFolderMenu(); } //----------------------------------------------------------------------------- void KMMainWidget::slotJumpToFolder() { QPointer dialog(selectFromAllFoldersDialog()); dialog->setWindowTitle(i18n("Jump to Folder")); if (dialog->exec() && dialog) { Akonadi::Collection collection = dialog->selectedCollection(); if (collection.isValid()) { slotSelectCollectionFolder(collection); } } } void KMMainWidget::slotSelectCollectionFolder(const Akonadi::Collection &col) { if (mFolderTreeWidget) { mFolderTreeWidget->selectCollectionFolder(col); slotFolderChanged(col); } } void KMMainWidget::slotApplyFilters() { const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } applyFilters(selectedMessages); } void KMMainWidget::slotApplyFiltersOnFolder() { if (mCurrentFolder && mCurrentFolder->collection().isValid()) { Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(mCurrentFolder->collection(), this); connect(job, &Akonadi::ItemFetchJob::result, this, &KMMainWidget::slotFetchItemsForFolderDone); } } void KMMainWidget::slotFetchItemsForFolderDone(KJob *job) { Akonadi::ItemFetchJob *fjob = dynamic_cast(job); Q_ASSERT(fjob); Akonadi::Item::List items = fjob->items(); applyFilters(items); } void KMMainWidget::applyFilters(const Akonadi::Item::List &selectedMessages) { #ifndef QT_NO_CURSOR KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy()); #endif MailCommon::FilterManager::instance()->filter(selectedMessages); } //----------------------------------------------------------------------------- void KMMainWidget::slotCheckVacation() { updateVacationScriptStatus(false); if (!kmkernel->askToGoOnline()) { return; } mVacationManager->checkVacation(); connect(mVacationManager, SIGNAL(updateVacationScriptStatus(bool,QString)), SLOT(updateVacationScriptStatus(bool,QString))); connect(mVacationManager, SIGNAL(editVacation()), SLOT(slotEditVacation())); } void KMMainWidget::slotEditVacation(const QString &serverName) { if (!kmkernel->askToGoOnline()) { return; } mVacationManager->slotEditVacation(serverName); } //----------------------------------------------------------------------------- void KMMainWidget::slotDebugSieve() { #if !defined(NDEBUG) if (mSieveDebugDialog) { return; } mSieveDebugDialog = new KSieveUi::SieveDebugDialog(this); mSieveDebugDialog->exec(); delete mSieveDebugDialog; #endif } void KMMainWidget::slotConfigChanged() { readConfig(); mMsgActions->setupForwardActions(actionCollection()); mMsgActions->setupForwardingActionsList(mGUIClient); } //----------------------------------------------------------------------------- void KMMainWidget::slotSaveMsg() { const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand(this, selectedMessages); saveCommand->start(); } //----------------------------------------------------------------------------- void KMMainWidget::slotOpenMsg() { KMOpenMsgCommand *openCommand = new KMOpenMsgCommand(this, QUrl(), overrideEncoding(), this); openCommand->start(); } //----------------------------------------------------------------------------- void KMMainWidget::slotSaveAttachments() { const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } // Avoid re-downloading in the common case that only one message is selected, and the message // is also displayed in the viewer. For this, create a dummy item without a parent collection / item id, // so that KMCommand doesn't download it. KMSaveAttachmentsCommand *saveCommand = Q_NULLPTR; if (mMsgView && selectedMessages.size() == 1 && mMsgView->message().hasPayload() && selectedMessages.first().id() == mMsgView->message().id()) { Akonadi::Item dummyItem; dummyItem.setPayload(mMsgView->message().payload()); saveCommand = new KMSaveAttachmentsCommand(this, dummyItem, mMsgView->viewer()); } else { saveCommand = new KMSaveAttachmentsCommand(this, selectedMessages); } saveCommand->start(); } void KMMainWidget::slotOnlineStatus() { // KMKernel will Q_EMIT a signal when we toggle the network state that is caught by // KMMainWidget::slotUpdateOnlineStatus to update our GUI if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Online) { // if online; then toggle and set it offline. kmkernel->stopNetworkJobs(); } else { kmkernel->resumeNetworkJobs(); slotCheckVacation(); } } void KMMainWidget::slotUpdateOnlineStatus(KMailSettings::EnumNetworkState::type) { if (!mAkonadiStandardActionManager) { return; } QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::ToggleWorkOffline); if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Online) { action->setText(i18n("Work Offline")); action->setIcon(QIcon::fromTheme(QStringLiteral("user-offline"))); } else { action->setText(i18n("Work Online")); action->setIcon(QIcon::fromTheme(QStringLiteral("user-online"))); } } //----------------------------------------------------------------------------- void KMMainWidget::slotSendQueued() { if (kmkernel->msgSender()) { kmkernel->msgSender()->sendQueued(); } } //----------------------------------------------------------------------------- void KMMainWidget::slotSendQueuedVia(MailTransport::Transport *transport) { if (transport) { if (kmkernel->msgSender()) { kmkernel->msgSender()->sendQueued(transport->id()); } } } //----------------------------------------------------------------------------- void KMMainWidget::slotShowBusySplash() { if (mReaderWindowActive) { mMsgView->displayBusyPage(); } } void KMMainWidget::showOfflinePage() { if (!mReaderWindowActive) { return; } mMsgView->displayOfflinePage(); } void KMMainWidget::showResourceOfflinePage() { if (!mReaderWindowActive) { return; } mMsgView->displayResourceOfflinePage(); } //----------------------------------------------------------------------------- void KMMainWidget::slotReplaceMsgByUnencryptedVersion() { qCDebug(KMAIL_LOG); Akonadi::Item oldMsg = mMessagePane->currentItem(); if (oldMsg.isValid()) { #if 0 qCDebug(KMAIL_LOG) << "Old message found"; if (oldMsg->hasUnencryptedMsg()) { qCDebug(KMAIL_LOG) << "Extra unencrypted message found"; KMime::Message *newMsg = oldMsg->unencryptedMsg(); // adjust the message id { QString msgId(oldMsg->msgId()); QString prefix("DecryptedMsg."); int oldIdx = msgId.indexOf(prefix, 0, Qt::CaseInsensitive); if (-1 == oldIdx) { int leftAngle = msgId.lastIndexOf('<'); msgId = msgId.insert((-1 == leftAngle) ? 0 : ++leftAngle, prefix); } else { // toggle between "DecryptedMsg." and "DeCryptedMsg." // to avoid same message id QCharRef c = msgId[ oldIdx + 2 ]; if ('C' == c) { c = 'c'; } else { c = 'C'; } } newMsg->setMsgId(msgId); mMsgView->setIdOfLastViewedMessage(msgId); } // insert the unencrypted message qCDebug(KMAIL_LOG) << "Adding unencrypted message to folder"; mFolder->addMsg(newMsg); /* Figure out its index in the folder for selecting. This must be count()-1, * since we append. Be safe and do find, though, just in case. */ int newMsgIdx = mFolder->find(newMsg); Q_ASSERT(newMsgIdx != -1); /* we need this unget, to have the message displayed correctly initially */ mFolder->unGetMsg(newMsgIdx); int idx = mFolder->find(oldMsg); Q_ASSERT(idx != -1); /* only select here, so the old one is not un-Gotten before, which would * render the pointer we hold invalid so that find would fail */ #if 0 // FIXME (Pragma) mHeaders->setCurrentItemByIndex(newMsgIdx); #endif // remove the old one if (idx != -1) { qCDebug(KMAIL_LOG) << "Deleting encrypted message"; mFolder->take(idx); } qCDebug(KMAIL_LOG) << "Updating message actions"; updateMessageActions(); qCDebug(KMAIL_LOG) << "Done."; } else { qCDebug(KMAIL_LOG) << "NO EXTRA UNENCRYPTED MESSAGE FOUND"; } #else qCDebug(KMAIL_LOG) << "AKONADI PORT: Disabled code in " << Q_FUNC_INFO; #endif } else { qCDebug(KMAIL_LOG) << "PANIC: NO OLD MESSAGE FOUND"; } } void KMMainWidget::slotFocusOnNextMessage() { mMessagePane->focusNextMessageItem(MessageList::Core::MessageTypeAny, true, false); } void KMMainWidget::slotFocusOnPrevMessage() { mMessagePane->focusPreviousMessageItem(MessageList::Core::MessageTypeAny, true, false); } void KMMainWidget::slotSelectFirstMessage() { mMessagePane->selectFirstMessageItem(MessageList::Core::MessageTypeAny, true); } void KMMainWidget::slotSelectLastMessage() { mMessagePane->selectLastMessageItem(MessageList::Core::MessageTypeAny, true); } void KMMainWidget::slotSelectFocusedMessage() { mMessagePane->selectFocusedMessageItem(true); } void KMMainWidget::slotSelectNextMessage() { mMessagePane->selectNextMessageItem(MessageList::Core::MessageTypeAny, MessageList::Core::ClearExistingSelection, true, false); } void KMMainWidget::slotExtendSelectionToNextMessage() { mMessagePane->selectNextMessageItem( MessageList::Core::MessageTypeAny, MessageList::Core::GrowOrShrinkExistingSelection, true, // center item false // don't loop in folder ); } void KMMainWidget::slotSelectNextUnreadMessage() { // The looping logic is: "Don't loop" just never loops, "Loop in current folder" // loops just in current folder, "Loop in all folders" loops in the current folder // first and then after confirmation jumps to the next folder. // A bad point here is that if you answer "No, and don't ask me again" to the confirmation // dialog then you have "Loop in current folder" and "Loop in all folders" that do // the same thing and no way to get the old behaviour. However, after a consultation on #kontact, // for bug-to-bug backward compatibility, the masters decided to keep it b0rken :D // If nobody complains, it stays like it is: if you complain enough maybe the masters will // decide to reconsider :) if (!mMessagePane->selectNextMessageItem( MessageList::Core::MessageTypeUnreadOnly, MessageList::Core::ClearExistingSelection, true, // center item KMailSettings::self()->loopOnGotoUnread() != KMailSettings::EnumLoopOnGotoUnread::DontLoop )) { // no next unread message was found in the current folder if ((KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllFolders) || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllMarkedFolders)) { mGoToFirstUnreadMessageInSelectedFolder = true; mFolderTreeWidget->folderTreeView()->selectNextUnreadFolder(true); mGoToFirstUnreadMessageInSelectedFolder = false; } } } void KMMainWidget::slotSelectPreviousMessage() { mMessagePane->selectPreviousMessageItem(MessageList::Core::MessageTypeAny, MessageList::Core::ClearExistingSelection, true, false); } void KMMainWidget::slotExtendSelectionToPreviousMessage() { mMessagePane->selectPreviousMessageItem( MessageList::Core::MessageTypeAny, MessageList::Core::GrowOrShrinkExistingSelection, true, // center item false // don't loop in folder ); } void KMMainWidget::slotSearchButton() { - if(mHideShowSearchBarAction->isChecked()){ - mMessagePane->show(); - } else { - mMessagePane->hide(); - } + mMessagePane->executeActionShowQuicksearch(); } void KMMainWidget::slotSelectPreviousUnreadMessage() { if (!mMessagePane->selectPreviousMessageItem( MessageList::Core::MessageTypeUnreadOnly, MessageList::Core::ClearExistingSelection, true, // center item KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInCurrentFolder )) { // no next unread message was found in the current folder if ((KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllFolders) || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllMarkedFolders)) { mGoToFirstUnreadMessageInSelectedFolder = true; mFolderTreeWidget->folderTreeView()->selectPrevUnreadFolder(); mGoToFirstUnreadMessageInSelectedFolder = false; } } } void KMMainWidget::slotDisplayCurrentMessage() { if (mMessagePane->currentItem().isValid() && !mMessagePane->searchEditHasFocus()) { slotMessageActivated(mMessagePane->currentItem()); } } // Called by double-clicked or 'Enter' in the messagelist -> pop up reader window void KMMainWidget::slotMessageActivated(const Akonadi::Item &msg) { if (!mCurrentFolder || !msg.isValid()) { return; } if (CommonKernel->folderIsDraftOrOutbox(mCurrentFolder->collection())) { mMsgActions->setCurrentMessage(msg); mMsgActions->editCurrentMessage(); return; } if (CommonKernel->folderIsTemplates(mCurrentFolder->collection())) { slotUseTemplate(); return; } // Try to fetch the mail, even in offline mode, it might be cached KMFetchMessageCommand *cmd = new KMFetchMessageCommand(this, msg); connect(cmd, &KMCommand::completed, this, &KMMainWidget::slotItemsFetchedForActivation); cmd->start(); } void KMMainWidget::slotItemsFetchedForActivation(KMCommand *command) { KMCommand::Result result = command->result(); if (result != KMCommand::OK) { qCDebug(KMAIL_LOG) << "Result:" << result; return; } KMFetchMessageCommand *fetchCmd = qobject_cast(command); const Item msg = fetchCmd->item(); KMReaderMainWin *win = new KMReaderMainWin(mFolderDisplayFormatPreference, mFolderHtmlLoadExtPreference); const bool useFixedFont = mMsgView ? mMsgView->isFixedFont() : MessageViewer::MessageViewerSettings::self()->useFixedFont(); win->setUseFixedFont(useFixedFont); const Akonadi::Collection parentCollection = MailCommon::Util::parentCollectionFromItem(msg); win->showMessage(overrideEncoding(), msg, parentCollection); win->show(); } void KMMainWidget::slotMessageStatusChangeRequest(const Akonadi::Item &item, const Akonadi::MessageStatus &set, const Akonadi::MessageStatus &clear) { if (!item.isValid()) { return; } if (clear.toQInt32() != Akonadi::MessageStatus().toQInt32()) { KMCommand *command = new KMSetStatusCommand(clear, Akonadi::Item::List() << item, true); command->start(); } if (set.toQInt32() != Akonadi::MessageStatus().toQInt32()) { KMCommand *command = new KMSetStatusCommand(set, Akonadi::Item::List() << item, false); command->start(); } } //----------------------------------------------------------------------------- void KMMainWidget::slotSelectAllMessages() { mMessagePane->selectAll(); updateMessageActions(); } void KMMainWidget::slotMessagePopup(const Akonadi::Item &msg, const QUrl &aUrl, const QUrl &imageUrl, const QPoint &aPoint) { updateMessageMenu(); const QString email = KEmailAddress::firstEmailAddress(aUrl.path()).toLower(); if (aUrl.scheme() == QLatin1String("mailto") && !email.isEmpty()) { Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob(this); job->setLimit(1); job->setQuery(Akonadi::ContactSearchJob::Email, email, Akonadi::ContactSearchJob::ExactMatch); job->setProperty("msg", QVariant::fromValue(msg)); job->setProperty("point", aPoint); job->setProperty("imageUrl", imageUrl); job->setProperty("url", aUrl); connect(job, &Akonadi::ContactSearchJob::result, this, &KMMainWidget::slotContactSearchJobForMessagePopupDone); } else { showMessagePopup(msg, aUrl, imageUrl, aPoint, false, false); } } void KMMainWidget::slotContactSearchJobForMessagePopupDone(KJob *job) { const Akonadi::ContactSearchJob *searchJob = qobject_cast(job); const bool contactAlreadyExists = !searchJob->contacts().isEmpty(); const Akonadi::Item::List listContact = searchJob->items(); const bool uniqueContactFound = (listContact.count() == 1); if (uniqueContactFound) { mMsgView->setContactItem(listContact.first(), searchJob->contacts().at(0)); } else { mMsgView->clearContactItem(); } const Akonadi::Item msg = job->property("msg").value(); const QPoint aPoint = job->property("point").toPoint(); const QUrl imageUrl = job->property("imageUrl").toUrl(); const QUrl url = job->property("url").toUrl(); showMessagePopup(msg, url, imageUrl, aPoint, contactAlreadyExists, uniqueContactFound); } void KMMainWidget::showMessagePopup(const Akonadi::Item &msg, const QUrl &url, const QUrl &imageUrl, const QPoint &aPoint, bool contactAlreadyExists, bool uniqueContactFound) { QMenu *menu = new QMenu; bool urlMenuAdded = false; if (!url.isEmpty()) { if (url.scheme() == QLatin1String("mailto")) { // popup on a mailto URL menu->addAction(mMsgView->mailToComposeAction()); menu->addAction(mMsgView->mailToReplyAction()); menu->addAction(mMsgView->mailToForwardAction()); menu->addSeparator(); if (contactAlreadyExists) { if (uniqueContactFound) { menu->addAction(mMsgView->editContactAction()); } else { menu->addAction(mMsgView->openAddrBookAction()); } } else { menu->addAction(mMsgView->addAddrBookAction()); menu->addAction(mMsgView->addToExistingContactAction()); } menu->addSeparator(); menu->addMenu(mMsgView->viewHtmlOption()); menu->addSeparator(); menu->addAction(mMsgView->copyURLAction()); urlMenuAdded = true; } else if (url.scheme() != QLatin1String("attachment")) { // popup on a not-mailto URL menu->addAction(mMsgView->urlOpenAction()); menu->addAction(mMsgView->addBookmarksAction()); menu->addAction(mMsgView->urlSaveAsAction()); menu->addAction(mMsgView->copyURLAction()); menu->addSeparator(); menu->addAction(mMsgView->shareServiceUrlMenu()); if (mMsgView->isAShortUrl(url)) { menu->addSeparator(); menu->addAction(mMsgView->expandShortUrlAction()); } if (!imageUrl.isEmpty()) { menu->addSeparator(); menu->addAction(mMsgView->copyImageLocation()); menu->addAction(mMsgView->downloadImageToDiskAction()); menu->addAction(mMsgView->shareImage()); if (mMsgView->adblockEnabled()) { menu->addSeparator(); menu->addAction(mMsgView->blockImage()); } } urlMenuAdded = true; } qCDebug(KMAIL_LOG) << "URL is:" << url; } const QString selectedText = mMsgView ? mMsgView->copyText() : QString(); if (mMsgView && !selectedText.isEmpty()) { if (urlMenuAdded) { menu->addSeparator(); } menu->addAction(mMsgActions->replyMenu()); menu->addSeparator(); menu->addAction(mMsgView->copyAction()); menu->addAction(mMsgView->selectAllAction()); menu->addSeparator(); mMsgActions->addWebShortcutsMenu(menu, selectedText); menu->addSeparator(); menu->addActions(mMsgView->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedSelection)); if (KPIMTextEdit::TextToSpeech::self()->isReady()) { menu->addSeparator(); menu->addAction(mMsgView->speakTextAction()); } } else if (!urlMenuAdded) { // popup somewhere else (i.e., not a URL) on the message if (!mMessagePane->currentMessage()) { // no messages delete menu; return; } Akonadi::Collection parentCol = msg.parentCollection(); if (parentCol.isValid() && CommonKernel->folderIsTemplates(parentCol)) { menu->addAction(mUseAction); } else { menu->addAction(mMsgActions->replyMenu()); menu->addAction(mMsgActions->forwardMenu()); } if (parentCol.isValid() && CommonKernel->folderIsSentMailFolder(parentCol)) { menu->addAction(sendAgainAction()); } else { menu->addAction(editAction()); } menu->addAction(mailingListActionMenu()); menu->addSeparator(); menu->addAction(mCopyActionMenu); menu->addAction(mMoveActionMenu); menu->addSeparator(); menu->addAction(mMsgActions->messageStatusMenu()); menu->addSeparator(); if (mMsgView) { if (!imageUrl.isEmpty()) { menu->addSeparator(); menu->addAction(mMsgView->copyImageLocation()); menu->addAction(mMsgView->downloadImageToDiskAction()); menu->addAction(mMsgView->shareImage()); menu->addSeparator(); if (mMsgView->adblockEnabled()) { menu->addAction(mMsgView->blockImage()); menu->addSeparator(); } } menu->addAction(mMsgView->viewSourceAction()); menu->addAction(mMsgView->toggleFixFontAction()); menu->addAction(mMsgView->toggleMimePartTreeAction()); } menu->addSeparator(); if (mMsgActions->printPreviewAction()) { menu->addAction(mMsgActions->printPreviewAction()); } menu->addAction(mMsgActions->printAction()); menu->addAction(mSaveAsAction); menu->addAction(mSaveAttachmentsAction); menu->addSeparator(); if (parentCol.isValid() && CommonKernel->folderIsTrash(parentCol)) { menu->addAction(mDeleteAction); } else { menu->addAction(akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash)); } menu->addSeparator(); if (mMsgView) { menu->addActions(mMsgView->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedMessage)); menu->addSeparator(); menu->addAction(mMsgView->saveMessageDisplayFormatAction()); menu->addAction(mMsgView->resetMessageDisplayFormatAction()); menu->addSeparator(); } menu->addAction(mMsgActions->annotateAction()); if (mMsgView && mMsgView->adblockEnabled()) { menu->addSeparator(); menu->addAction(mMsgView->openBlockableItems()); } menu->addSeparator(); menu->addAction(mMsgActions->addFollowupReminderAction()); if (kmkernel->allowToDebugBalooSupport()) { menu->addSeparator(); menu->addAction(mMsgActions->debugBalooAction()); } } KAcceleratorManager::manage(menu); menu->exec(aPoint, Q_NULLPTR); delete menu; } void KMMainWidget::setupActions() { mMsgActions = new KMail::MessageActions(actionCollection(), this); mMsgActions->setMessageView(mMsgView); //----- File Menu mSaveAsAction = new QAction(QIcon::fromTheme(QStringLiteral("document-save")), i18n("Save &As..."), this); actionCollection()->addAction(QStringLiteral("file_save_as"), mSaveAsAction); connect(mSaveAsAction, &QAction::triggered, this, &KMMainWidget::slotSaveMsg); actionCollection()->setDefaultShortcut(mSaveAsAction, KStandardShortcut::save().first()); mOpenAction = KStandardAction::open(this, SLOT(slotOpenMsg()), actionCollection()); mOpenRecentAction = KStandardAction::openRecent(this, SLOT(slotOpenRecentMsg(QUrl)), actionCollection()); KConfigGroup grp = mConfig->group(QStringLiteral("Recent Files")); mOpenRecentAction->loadEntries(grp); { QAction *action = new QAction(i18n("&Expire All Folders"), this); actionCollection()->addAction(QStringLiteral("expire_all_folders"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotExpireAll); } { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("mail-receive")), i18n("Check &Mail"), this); actionCollection()->addAction(QStringLiteral("check_mail"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotCheckMail); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_L)); } mAccountActionMenu = new KActionMenuAccount(this); mAccountActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("mail-receive"))); mAccountActionMenu->setText(i18n("Check Mail In")); mAccountActionMenu->setIconText(i18n("Check Mail")); mAccountActionMenu->setToolTip(i18n("Check Mail")); actionCollection()->addAction(QStringLiteral("check_mail_in"), mAccountActionMenu); connect(mAccountActionMenu, &KActionMenu::triggered, this, &KMMainWidget::slotCheckMail); mSendQueued = new QAction(QIcon::fromTheme(QStringLiteral("mail-send")), i18n("&Send Queued Messages"), this); actionCollection()->addAction(QStringLiteral("send_queued"), mSendQueued); connect(mSendQueued, &QAction::triggered, this, &KMMainWidget::slotSendQueued); { QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::ToggleWorkOffline); mAkonadiStandardActionManager->interceptAction(Akonadi::StandardActionManager::ToggleWorkOffline); action->setCheckable(false); connect(action, &QAction::triggered, this, &KMMainWidget::slotOnlineStatus); action->setText(i18n("Online status (unknown)")); } mSendActionMenu = new KActionMenuTransport(this); mSendActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("mail-send-via"))); mSendActionMenu->setText(i18n("Send Queued Messages Via")); actionCollection()->addAction(QStringLiteral("send_queued_via"), mSendActionMenu); connect(mSendActionMenu, &KActionMenuTransport::transportSelected, this, &KMMainWidget::slotSendQueuedVia); //----- Tools menu if (parent()->inherits("KMMainWin")) { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("x-office-address-book")), i18n("&Address Book"), this); actionCollection()->addAction(QStringLiteral("addressbook"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotAddrBook); if (QStandardPaths::findExecutable(QStringLiteral("kaddressbook")).isEmpty()) { action->setEnabled(false); } } { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("pgp-keys")), i18n("Certificate Manager"), this); actionCollection()->addAction(QStringLiteral("tools_start_certman"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotStartCertManager); // disable action if no certman binary is around if (QStandardPaths::findExecutable(QStringLiteral("kleopatra")).isEmpty()) { action->setEnabled(false); } } { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("pgp-keys")), i18n("GnuPG Log Viewer"), this); actionCollection()->addAction(QStringLiteral("tools_start_kwatchgnupg"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotStartWatchGnuPG); #ifdef Q_OS_WIN32 // not ported yet, underlying infrastructure missing on Windows const bool usableKWatchGnupg = false; #else // disable action if no kwatchgnupg binary is around bool usableKWatchGnupg = !QStandardPaths::findExecutable(QStringLiteral("kwatchgnupg")).isEmpty(); #endif action->setEnabled(usableKWatchGnupg); } { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("document-import")), i18n("&Import Messages..."), this); actionCollection()->addAction(QStringLiteral("import"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotImport); if (QStandardPaths::findExecutable(QStringLiteral("importwizard")).isEmpty()) { action->setEnabled(false); } } #if !defined(NDEBUG) { QAction *action = new QAction(i18n("&Debug Sieve..."), this); actionCollection()->addAction(QStringLiteral("tools_debug_sieve"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotDebugSieve); } #endif { QAction *action = new QAction(i18n("Filter &Log Viewer..."), this); actionCollection()->addAction(QStringLiteral("filter_log_viewer"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotFilterLogViewer); } { QAction *action = new QAction(i18n("&Anti-Spam Wizard..."), this); actionCollection()->addAction(QStringLiteral("antiSpamWizard"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotAntiSpamWizard); } { QAction *action = new QAction(i18n("&Anti-Virus Wizard..."), this); actionCollection()->addAction(QStringLiteral("antiVirusWizard"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotAntiVirusWizard); } { QAction *action = new QAction(i18n("&Account Wizard..."), this); actionCollection()->addAction(QStringLiteral("accountWizard"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotAccountWizard); } { QAction *action = new QAction(i18n("&Import Wizard..."), this); actionCollection()->addAction(QStringLiteral("importWizard"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotImportWizard); } if (KSieveUi::Util::allowOutOfOfficeSettings()) { QAction *action = new QAction(i18n("Edit \"Out of Office\" Replies..."), this); actionCollection()->addAction(QStringLiteral("tools_edit_vacation"), action); connect(action, SIGNAL(triggered(bool)), SLOT(slotEditVacation())); } { QAction *action = new QAction(i18n("&Configure Automatic Archiving..."), this); actionCollection()->addAction(QStringLiteral("tools_automatic_archiving"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotConfigureAutomaticArchiving); } { QAction *action = new QAction(i18n("Delayed Messages..."), this); actionCollection()->addAction(QStringLiteral("message_delayed"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotConfigureSendLater); } { QAction *action = new QAction(i18n("Followup Reminder Messages..."), this); actionCollection()->addAction(QStringLiteral("followup_reminder_messages"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotConfigureFollowupReminder); } // Disable the standard action delete key sortcut. QAction *const standardDelAction = akonadiStandardAction(Akonadi::StandardActionManager::DeleteItems); standardDelAction->setShortcut(QKeySequence()); //----- Edit Menu /* The delete action is nowhere in the gui, by default, so we need to make * sure it is plugged into the KAccel now, since that won't happen on * XMLGui construction or manual ->plug(). This is only a problem when run * as a part, though. */ mDeleteAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action Hard delete, bypassing trash", "&Delete"), this); actionCollection()->addAction(QStringLiteral("delete"), mDeleteAction); connect(mDeleteAction, &QAction::triggered, this, &KMMainWidget::slotDeleteMessages); actionCollection()->setDefaultShortcut(mDeleteAction, QKeySequence(Qt::SHIFT + Qt::Key_Delete)); mTrashThreadAction = new QAction(i18n("M&ove Thread to Trash"), this); actionCollection()->addAction(QStringLiteral("move_thread_to_trash"), mTrashThreadAction); actionCollection()->setDefaultShortcut(mTrashThreadAction, QKeySequence(Qt::CTRL + Qt::Key_Delete)); mTrashThreadAction->setIcon(QIcon::fromTheme(QStringLiteral("user-trash"))); KMail::Util::addQActionHelpText(mTrashThreadAction, i18n("Move thread to trashcan")); connect(mTrashThreadAction, &QAction::triggered, this, &KMMainWidget::slotTrashThread); mDeleteThreadAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete T&hread"), this); actionCollection()->addAction(QStringLiteral("delete_thread"), mDeleteThreadAction); //Don't use new connect api. connect(mDeleteThreadAction, SIGNAL(triggered(bool)), this, SLOT(slotDeleteThread())); actionCollection()->setDefaultShortcut(mDeleteThreadAction, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Delete)); mSearchMessages = new QAction(QIcon::fromTheme(QStringLiteral("edit-find-mail")), i18n("&Find Messages..."), this); actionCollection()->addAction(QStringLiteral("search_messages"), mSearchMessages); connect(mSearchMessages, &QAction::triggered, this, &KMMainWidget::slotRequestFullSearchFromQuickSearch); actionCollection()->setDefaultShortcut(mSearchMessages, QKeySequence(Qt::Key_S)); { QAction *action = new QAction(i18n("Select &All Messages"), this); actionCollection()->addAction(QStringLiteral("mark_all_messages"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectAllMessages); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_A)); } //----- Folder Menu mFolderMailingListPropertiesAction = new QAction(i18n("&Mailing List Management..."), this); actionCollection()->addAction(QStringLiteral("folder_mailinglist_properties"), mFolderMailingListPropertiesAction); connect(mFolderMailingListPropertiesAction, &QAction::triggered, mManageShowCollectionProperties, &ManageShowCollectionProperties::slotFolderMailingListProperties); // mFolderMailingListPropertiesAction->setIcon(QIcon::fromTheme("document-properties-mailing-list")); mShowFolderShortcutDialogAction = new QAction(QIcon::fromTheme(QStringLiteral("configure-shortcuts")), i18n("&Assign Shortcut..."), this); actionCollection()->addAction(QStringLiteral("folder_shortcut_command"), mShowFolderShortcutDialogAction); connect(mShowFolderShortcutDialogAction, &QAction::triggered, mManageShowCollectionProperties, &ManageShowCollectionProperties::slotShowFolderShortcutDialog); // FIXME: this action is not currently enabled in the rc file, but even if // it were there is inconsistency between the action name and action. // "Expiration Settings" implies that this will lead to a settings dialogue // and it should be followed by a "...", but slotExpireFolder() performs // an immediate expiry. // // TODO: expire action should be disabled if there is no content or if // the folder can't delete messages. // // Leaving the action here for the moment, it and the "Expire" option in the // folder popup menu should be combined or at least made consistent. Same for // slotExpireFolder() and FolderViewItem::slotShowExpiryProperties(). mExpireFolderAction = new QAction(i18n("&Expiration Settings"), this); actionCollection()->addAction(QStringLiteral("expire"), mExpireFolderAction); connect(mExpireFolderAction, &QAction::triggered, this, &KMMainWidget::slotExpireFolder); mAkonadiStandardActionManager->interceptAction(Akonadi::StandardMailActionManager::MoveToTrash); connect(mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveToTrash), &QAction::triggered, this, &KMMainWidget::slotTrashSelectedMessages); mAkonadiStandardActionManager->interceptAction(Akonadi::StandardMailActionManager::MoveAllToTrash); connect(mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash), &QAction::triggered, this, &KMMainWidget::slotEmptyFolder); mAkonadiStandardActionManager->interceptAction(Akonadi::StandardActionManager::DeleteCollections); connect(mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections), &QAction::triggered, this, &KMMainWidget::slotRemoveFolder); // ### PORT ME: Add this to the context menu. Not possible right now because // the context menu uses XMLGUI, and that would add the entry to // all collection context menus mArchiveFolderAction = new QAction(i18n("&Archive Folder..."), this); actionCollection()->addAction(QStringLiteral("archive_folder"), mArchiveFolderAction); connect(mArchiveFolderAction, &QAction::triggered, this, &KMMainWidget::slotArchiveFolder); mDisplayMessageFormatMenu = new DisplayMessageFormatActionMenu(this); connect(mDisplayMessageFormatMenu, &DisplayMessageFormatActionMenu::changeDisplayMessageFormat, this, &KMMainWidget::slotChangeDisplayMessageFormat); actionCollection()->addAction(QStringLiteral("display_format_message"), mDisplayMessageFormatMenu); mPreferHtmlLoadExtAction = new KToggleAction(i18n("Load E&xternal References"), this); actionCollection()->addAction(QStringLiteral("prefer_html_external_refs"), mPreferHtmlLoadExtAction); connect(mPreferHtmlLoadExtAction, &KToggleAction::triggered, this, &KMMainWidget::slotOverrideHtmlLoadExt); { QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyCollections); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_C)); } { QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::Paste); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_V)); } { QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyItems); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::CTRL + Qt::Key_C)); } { QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CutItems); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::CTRL + Qt::Key_X)); } { QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyItemToMenu); action->setText(i18n("Copy Message To...")); action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::MoveItemToMenu); action->setText(i18n("Move Message To...")); } //----- Message Menu { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new")), i18n("&New Message..."), this); actionCollection()->addAction(QStringLiteral("new_message"), action); action->setIconText(i18nc("@action:intoolbar New Empty Message", "New")); connect(action, &QAction::triggered, this, &KMMainWidget::slotCompose); // do not set a New shortcut if kmail is a component if (!kmkernel->xmlGuiInstanceName().isEmpty()) { actionCollection()->setDefaultShortcut(action, KStandardShortcut::openNew().first()); } } mTemplateMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Message From &Template"), actionCollection()); mTemplateMenu->setDelayed(true); actionCollection()->addAction(QStringLiteral("new_from_template"), mTemplateMenu); connect(mTemplateMenu->menu(), &QMenu::aboutToShow, this, &KMMainWidget::slotShowNewFromTemplate); connect(mTemplateMenu->menu(), &QMenu::triggered, this, &KMMainWidget::slotNewFromTemplate); mMessageNewList = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new-list")), i18n("New Message t&o Mailing-List..."), this); actionCollection()->addAction(QStringLiteral("post_message"), mMessageNewList); connect(mMessageNewList, &QAction::triggered, this, &KMMainWidget::slotPostToML); actionCollection()->setDefaultShortcut(mMessageNewList, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_N)); mSendAgainAction = new QAction(i18n("Send A&gain..."), this); actionCollection()->addAction(QStringLiteral("send_again"), mSendAgainAction); connect(mSendAgainAction, &QAction::triggered, this, &KMMainWidget::slotResendMsg); //----- Create filter actions mFilterMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("&Create Filter"), this); actionCollection()->addAction(QStringLiteral("create_filter"), mFilterMenu); connect(mFilterMenu, &QAction::triggered, this, &KMMainWidget::slotFilter); { QAction *action = new QAction(i18n("Filter on &Subject..."), this); actionCollection()->addAction(QStringLiteral("subject_filter"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotSubjectFilter); mFilterMenu->addAction(action); } { QAction *action = new QAction(i18n("Filter on &From..."), this); actionCollection()->addAction(QStringLiteral("from_filter"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotFromFilter); mFilterMenu->addAction(action); } { QAction *action = new QAction(i18n("Filter on &To..."), this); actionCollection()->addAction(QStringLiteral("to_filter"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotToFilter); mFilterMenu->addAction(action); } { QAction *action = new QAction(i18n("Filter on &Cc..."), this); actionCollection()->addAction(QStringLiteral("cc_filter"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotCcFilter); mFilterMenu->addAction(action); } mFilterMenu->addAction(mMsgActions->listFilterAction()); mUseAction = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New Message From &Template"), this); actionCollection()->addAction(QStringLiteral("use_template"), mUseAction); connect(mUseAction, &QAction::triggered, this, &KMMainWidget::slotUseTemplate); actionCollection()->setDefaultShortcut(mUseAction, QKeySequence(Qt::SHIFT + Qt::Key_N)); //----- "Mark Thread" submenu mThreadStatusMenu = new KActionMenu(i18n("Mark &Thread"), this); actionCollection()->addAction(QStringLiteral("thread_status"), mThreadStatusMenu); mMarkThreadAsReadAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-mark-read")), i18n("Mark Thread as &Read"), this); actionCollection()->addAction(QStringLiteral("thread_read"), mMarkThreadAsReadAction); connect(mMarkThreadAsReadAction, &QAction::triggered, this, &KMMainWidget::slotSetThreadStatusRead); KMail::Util::addQActionHelpText(mMarkThreadAsReadAction, i18n("Mark all messages in the selected thread as read")); mThreadStatusMenu->addAction(mMarkThreadAsReadAction); mMarkThreadAsUnreadAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-mark-unread")), i18n("Mark Thread as &Unread"), this); actionCollection()->addAction(QStringLiteral("thread_unread"), mMarkThreadAsUnreadAction); connect(mMarkThreadAsUnreadAction, &QAction::triggered, this, &KMMainWidget::slotSetThreadStatusUnread); KMail::Util::addQActionHelpText(mMarkThreadAsUnreadAction, i18n("Mark all messages in the selected thread as unread")); mThreadStatusMenu->addAction(mMarkThreadAsUnreadAction); mThreadStatusMenu->addSeparator(); //----- "Mark Thread" toggle actions mToggleThreadImportantAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-mark-important")), i18n("Mark Thread as &Important"), this); actionCollection()->addAction(QStringLiteral("thread_flag"), mToggleThreadImportantAction); connect(mToggleThreadImportantAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusImportant); mToggleThreadImportantAction->setCheckedState(KGuiItem(i18n("Remove &Important Thread Mark"))); mThreadStatusMenu->addAction(mToggleThreadImportantAction); mToggleThreadToActAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-mark-task")), i18n("Mark Thread as &Action Item"), this); actionCollection()->addAction(QStringLiteral("thread_toact"), mToggleThreadToActAction); connect(mToggleThreadToActAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusToAct); mToggleThreadToActAction->setCheckedState(KGuiItem(i18n("Remove &Action Item Thread Mark"))); mThreadStatusMenu->addAction(mToggleThreadToActAction); //------- "Watch and ignore thread" actions mWatchThreadAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-thread-watch")), i18n("&Watch Thread"), this); actionCollection()->addAction(QStringLiteral("thread_watched"), mWatchThreadAction); connect(mWatchThreadAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusWatched); mIgnoreThreadAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("mail-thread-ignored")), i18n("&Ignore Thread"), this); actionCollection()->addAction(QStringLiteral("thread_ignored"), mIgnoreThreadAction); connect(mIgnoreThreadAction, &KToggleAction::triggered, this, &KMMainWidget::slotSetThreadStatusIgnored); mThreadStatusMenu->addSeparator(); mThreadStatusMenu->addAction(mWatchThreadAction); mThreadStatusMenu->addAction(mIgnoreThreadAction); mSaveAttachmentsAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("Save A&ttachments..."), this); actionCollection()->addAction(QStringLiteral("file_save_attachments"), mSaveAttachmentsAction); connect(mSaveAttachmentsAction, &QAction::triggered, this, &KMMainWidget::slotSaveAttachments); mMoveActionMenu = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::MoveItemToMenu); mCopyActionMenu = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyItemToMenu); mApplyAllFiltersAction = new QAction(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("Appl&y All Filters"), this); actionCollection()->addAction(QStringLiteral("apply_filters"), mApplyAllFiltersAction); connect(mApplyAllFiltersAction, &QAction::triggered, this, &KMMainWidget::slotApplyFilters); actionCollection()->setDefaultShortcut(mApplyAllFiltersAction, QKeySequence(Qt::CTRL + Qt::Key_J)); mApplyFilterActionsMenu = new KActionMenu(i18n("A&pply Filter"), this); actionCollection()->addAction(QStringLiteral("apply_filter_actions"), mApplyFilterActionsMenu); { QAction *action = new QAction(i18nc("View->", "&Expand Thread / Group"), this); actionCollection()->addAction(QStringLiteral("expand_thread"), action); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Period)); KMail::Util::addQActionHelpText(action, i18n("Expand the current thread or group")); connect(action, &QAction::triggered, this, &KMMainWidget::slotExpandThread); } { QAction *action = new QAction(i18nc("View->", "&Collapse Thread / Group"), this); actionCollection()->addAction(QStringLiteral("collapse_thread"), action); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Comma)); KMail::Util::addQActionHelpText(action, i18n("Collapse the current thread or group")); connect(action, &QAction::triggered, this, &KMMainWidget::slotCollapseThread); } { QAction *action = new QAction(i18nc("View->", "Ex&pand All Threads"), this); actionCollection()->addAction(QStringLiteral("expand_all_threads"), action); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Period)); KMail::Util::addQActionHelpText(action, i18n("Expand all threads in the current folder")); connect(action, &QAction::triggered, this, &KMMainWidget::slotExpandAllThreads); } { QAction *action = new QAction(i18nc("View->", "C&ollapse All Threads"), this); actionCollection()->addAction(QStringLiteral("collapse_all_threads"), action); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Comma)); KMail::Util::addQActionHelpText(action, i18n("Collapse all threads in the current folder")); connect(action, &QAction::triggered, this, &KMMainWidget::slotCollapseAllThreads); } QAction *dukeOfMonmoth = new QAction(i18n("&Display Message"), this); actionCollection()->addAction(QStringLiteral("display_message"), dukeOfMonmoth); connect(dukeOfMonmoth, &QAction::triggered, this, &KMMainWidget::slotDisplayCurrentMessage); QList shortcuts; shortcuts << QKeySequence(Qt::Key_Enter) << QKeySequence(Qt::Key_Return); actionCollection()->setDefaultShortcuts(dukeOfMonmoth, shortcuts); //----- Go Menu { QAction *action = new QAction(i18n("&Next Message"), this); actionCollection()->addAction(QStringLiteral("go_next_message"), action); actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("N; Right"))); KMail::Util::addQActionHelpText(action, i18n("Go to the next message")); connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectNextMessage); } { QAction *action = new QAction(i18n("Next &Unread Message"), this); actionCollection()->addAction(QStringLiteral("go_next_unread_message"), action); actionCollection()->setDefaultShortcuts(action, QList() << QKeySequence(Qt::Key_Plus) << QKeySequence(Qt::Key_Plus + Qt::KeypadModifier)); if (QApplication::isRightToLeft()) { action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); } else { action->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); } action->setIconText(i18nc("@action:inmenu Goto next unread message", "Next")); KMail::Util::addQActionHelpText(action, i18n("Go to the next unread message")); connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectNextUnreadMessage); } { QAction *action = new QAction(i18n("&Previous Message"), this); actionCollection()->addAction(QStringLiteral("go_prev_message"), action); KMail::Util::addQActionHelpText(action, i18n("Go to the previous message")); actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("P; Left"))); connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectPreviousMessage); } { mHideShowSearchBarAction = new QAction(i18n("Search"), this); actionCollection()->addAction(QStringLiteral("search_button"), mHideShowSearchBarAction); mHideShowSearchBarAction->setIcon(QIcon::fromTheme(QStringLiteral("go-search"))); mHideShowSearchBarAction->setCheckable(true); mHideShowSearchBarAction->setChecked(false); connect(mHideShowSearchBarAction, &QAction::triggered, this, &KMMainWidget::slotSearchButton); + /* execute action for hidding the search bar at launch */ + if(mMessagePane->showHideQuicksearchIsChecked()) + mMessagePane->executeActionShowQuicksearch(); + + } { QAction *action = new QAction(i18n("Previous Unread &Message"), this); actionCollection()->addAction(QStringLiteral("go_prev_unread_message"), action); actionCollection()->setDefaultShortcuts(action, QList() << QKeySequence(Qt::Key_Minus) << QKeySequence(Qt::Key_Minus + Qt::KeypadModifier)); if (QApplication::isRightToLeft()) { action->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); } else { action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); } action->setIconText(i18nc("@action:inmenu Goto previous unread message.", "Previous")); KMail::Util::addQActionHelpText(action, i18n("Go to the previous unread message")); connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectPreviousUnreadMessage); } { QAction *action = new QAction(i18n("Next Unread &Folder"), this); actionCollection()->addAction(QStringLiteral("go_next_unread_folder"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotNextUnreadFolder); actionCollection()->setDefaultShortcuts(action, QList() << QKeySequence(Qt::ALT + Qt::Key_Plus) << QKeySequence(Qt::ALT + Qt::Key_Plus + Qt::KeypadModifier)); KMail::Util::addQActionHelpText(action, i18n("Go to the next folder with unread messages")); } { QAction *action = new QAction(i18n("Previous Unread F&older"), this); actionCollection()->addAction(QStringLiteral("go_prev_unread_folder"), action); actionCollection()->setDefaultShortcuts(action, QList() << QKeySequence(Qt::ALT + Qt::Key_Minus) << QKeySequence(Qt::ALT + Qt::Key_Minus + Qt::KeypadModifier)); KMail::Util::addQActionHelpText(action, i18n("Go to the previous folder with unread messages")); connect(action, &QAction::triggered, this, &KMMainWidget::slotPrevUnreadFolder); } { QAction *action = new QAction(i18nc("Go->", "Next Unread &Text"), this); actionCollection()->addAction(QStringLiteral("go_next_unread_text"), action); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Space)); KMail::Util::addQActionHelpText(action, i18n("Go to the next unread text")); action->setWhatsThis(i18n("Scroll down current message. " "If at end of current message, " "go to next unread message.")); connect(action, &QAction::triggered, this, &KMMainWidget::slotReadOn); } //----- Settings Menu { QAction *action = new QAction(i18n("Configure &Filters..."), this); action->setMenuRole(QAction::NoRole); // do not move to application menu on OS X actionCollection()->addAction(QStringLiteral("filter"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotFilter); } { QAction *action = new QAction(i18n("Manage &Sieve Scripts..."), this); actionCollection()->addAction(QStringLiteral("sieveFilters"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotManageSieveScripts); } { mShowIntroductionAction = new QAction(QIcon::fromTheme(QStringLiteral("kmail")), i18n("KMail &Introduction"), this); actionCollection()->addAction(QStringLiteral("help_kmail_welcomepage"), mShowIntroductionAction); KMail::Util::addQActionHelpText(mShowIntroductionAction, i18n("Display KMail's Welcome Page")); connect(mShowIntroductionAction, &QAction::triggered, this, &KMMainWidget::slotIntro); mShowIntroductionAction->setEnabled(mMsgView != Q_NULLPTR); } // ----- Standard Actions // KStandardAction::configureNotifications(this, SLOT(slotEditNotifications()), actionCollection()); { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("preferences-desktop-notification")), i18n("Configure &Notifications..."), this); action->setMenuRole(QAction::NoRole); // do not move to application menu on OS X actionCollection()->addAction(QStringLiteral("kmail_configure_notifications"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotEditNotifications); } { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("&Configure KMail..."), this); action->setMenuRole(QAction::PreferencesRole); // this one should move to the application menu on OS X actionCollection()->addAction(QStringLiteral("kmail_configure_kmail"), action); connect(action, &QAction::triggered, kmkernel, &KMKernel::slotShowConfigurationDialog); } { mExpireConfigAction = new QAction(i18n("Expire..."), this); actionCollection()->addAction(QStringLiteral("expire_settings"), mExpireConfigAction); connect(mExpireConfigAction, &QAction::triggered, mManageShowCollectionProperties, &ManageShowCollectionProperties::slotShowExpiryProperties); } { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("Add Favorite Folder..."), this); actionCollection()->addAction(QStringLiteral("add_favorite_folder"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotAddFavoriteFolder); } { mServerSideSubscription = new QAction(QIcon::fromTheme(QStringLiteral("folder-bookmarks")), i18n("Serverside Subscription..."), this); actionCollection()->addAction(QStringLiteral("serverside_subscription"), mServerSideSubscription); connect(mServerSideSubscription, &QAction::triggered, this, &KMMainWidget::slotServerSideSubscription); } { mApplyFiltersOnFolder = new QAction(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("Appl&y All Filters On Folder"), this); actionCollection()->addAction(QStringLiteral("apply_filters_on_folder"), mApplyFiltersOnFolder); connect(mApplyFiltersOnFolder, &QAction::triggered, this, &KMMainWidget::slotApplyFiltersOnFolder); } { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("kmail")), i18n("&Export KMail Data..."), this); actionCollection()->addAction(QStringLiteral("kmail_export_data"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotExportData); } { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("New AddressBook Contact..."), this); actionCollection()->addAction(QStringLiteral("kmail_new_addressbook_contact"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotCreateAddressBookContact); } actionCollection()->addAction(KStandardAction::Undo, QStringLiteral("kmail_undo"), this, SLOT(slotUndo())); menutimer = new QTimer(this); menutimer->setObjectName(QStringLiteral("menutimer")); menutimer->setSingleShot(true); connect(menutimer, &QTimer::timeout, this, &KMMainWidget::updateMessageActionsDelayed); connect(kmkernel->undoStack(), &KMail::UndoStack::undoStackChanged, this, &KMMainWidget::slotUpdateUndo); updateMessageActions(); updateFolderMenu(); mTagActionManager = new KMail::TagActionManager(this, actionCollection(), mMsgActions, mGUIClient); mFolderShortcutActionManager = new KMail::FolderShortcutActionManager(this, actionCollection()); { QAction *action = new QAction(i18n("Copy Message to Folder"), this); actionCollection()->addAction(QStringLiteral("copy_message_to_folder"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotCopySelectedMessagesToFolder); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_C)); } { QAction *action = new QAction(i18n("Jump to Folder..."), this); actionCollection()->addAction(QStringLiteral("jump_to_folder"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotJumpToFolder); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_J)); } { QAction *action = new QAction(i18n("Abort Current Operation"), this); actionCollection()->addAction(QStringLiteral("cancel"), action); connect(action, &QAction::triggered, ProgressManager::instance(), &KPIM::ProgressManager::slotAbortAll); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Escape)); } { QAction *action = new QAction(i18n("Focus on Next Folder"), this); actionCollection()->addAction(QStringLiteral("inc_current_folder"), action); connect(action, &QAction::triggered, mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusNextFolder); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Right)); } { QAction *action = new QAction(i18n("Focus on Previous Folder"), this); actionCollection()->addAction(QStringLiteral("dec_current_folder"), action); connect(action, &QAction::triggered, mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusPrevFolder); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Left)); } { QAction *action = new QAction(i18n("Select Folder with Focus"), this); actionCollection()->addAction(QStringLiteral("select_current_folder"), action); connect(action, &QAction::triggered, mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotSelectFocusFolder); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Space)); } { QAction *action = new QAction(i18n("Focus on First Folder"), this); actionCollection()->addAction(QStringLiteral("focus_first_folder"), action); connect(action, &QAction::triggered, mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusFirstFolder); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Home)); } { QAction *action = new QAction(i18n("Focus on Last Folder"), this); actionCollection()->addAction(QStringLiteral("focus_last_folder"), action); connect(action, &QAction::triggered, mFolderTreeWidget->folderTreeView(), &FolderTreeView::slotFocusLastFolder); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_End)); } { QAction *action = new QAction(i18n("Focus on Next Message"), this); actionCollection()->addAction(QStringLiteral("inc_current_message"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotFocusOnNextMessage); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Right)); } { QAction *action = new QAction(i18n("Focus on Previous Message"), this); actionCollection()->addAction(QStringLiteral("dec_current_message"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotFocusOnPrevMessage); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Left)); } { QAction *action = new QAction(i18n("Select First Message"), this); actionCollection()->addAction(QStringLiteral("select_first_message"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectFirstMessage); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Home)); } { QAction *action = new QAction(i18n("Select Last Message"), this); actionCollection()->addAction(QStringLiteral("select_last_message"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectLastMessage); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_End)); } { QAction *action = new QAction(i18n("Select Message with Focus"), this); actionCollection()->addAction(QStringLiteral("select_current_message"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectFocusedMessage); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Space)); } { mQuickSearchAction = new QAction(i18n("Set Focus to Quick Search"), this); //If change shortcut change Panel::setQuickSearchClickMessage(...) message actionCollection()->setDefaultShortcut(mQuickSearchAction, QKeySequence(Qt::ALT + Qt::Key_Q)); actionCollection()->addAction(QStringLiteral("focus_to_quickseach"), mQuickSearchAction); connect(mQuickSearchAction, &QAction::triggered, this, &KMMainWidget::slotFocusQuickSearch); updateQuickSearchLineText(); } { QAction *action = new QAction(i18n("Extend Selection to Previous Message"), this); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::Key_Left)); actionCollection()->addAction(QStringLiteral("previous_message"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotExtendSelectionToPreviousMessage); } { QAction *action = new QAction(i18n("Extend Selection to Next Message"), this); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::Key_Right)); actionCollection()->addAction(QStringLiteral("next_message"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotExtendSelectionToNextMessage); } { mMoveMsgToFolderAction = new QAction(i18n("Move Message to Folder"), this); actionCollection()->setDefaultShortcut(mMoveMsgToFolderAction, QKeySequence(Qt::Key_M)); actionCollection()->addAction(QStringLiteral("move_message_to_folder"), mMoveMsgToFolderAction); connect(mMoveMsgToFolderAction, &QAction::triggered, this, &KMMainWidget::slotMoveSelectedMessageToFolder); } mArchiveAction = new QAction(i18nc("@action", "Archive"), this); actionCollection()->addAction(QStringLiteral("archive_mails"), mArchiveAction); connect(mArchiveAction, &QAction::triggered, this, &KMMainWidget::slotArchiveMails); mLowBandwithAction = new KToggleAction(i18n("Low Bandwidth"), this); actionCollection()->addAction(QStringLiteral("low_bandwidth"), mLowBandwithAction); connect(mLowBandwithAction, &KToggleAction::triggered, this, &KMMainWidget::slotBandwidth); mMarkAllMessageAsReadAndInAllSubFolder = new QAction(i18n("Mark All Messages As Read in This Folder and All its Subfolder"), this); mMarkAllMessageAsReadAndInAllSubFolder->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-read"))); actionCollection()->addAction(QStringLiteral("markallmessagereadcurentfolderandsubfolder"), mMarkAllMessageAsReadAndInAllSubFolder); connect(mMarkAllMessageAsReadAndInAllSubFolder, &KToggleAction::triggered, this, &KMMainWidget::slotMarkAllMessageAsReadInCurrentFolderAndSubfolder); } void KMMainWidget::slotAddFavoriteFolder() { if (!mFavoritesModel) { return; } QPointer dialog(selectFromAllFoldersDialog()); dialog->setWindowTitle(i18n("Add Favorite Folder")); if (dialog->exec() && dialog) { const Akonadi::Collection collection = dialog->selectedCollection(); if (collection.isValid()) { mFavoritesModel->addCollection(collection); } } } //----------------------------------------------------------------------------- void KMMainWidget::slotEditNotifications() { KMail::KMKnotify notifyDlg(this); notifyDlg.exec(); } //----------------------------------------------------------------------------- void KMMainWidget::slotReadOn() { if (!mMsgView) { return; } if (!mMsgView->viewer()->atBottom()) { mMsgView->viewer()->slotJumpDown(); return; } slotSelectNextUnreadMessage(); } void KMMainWidget::slotNextUnreadFolder() { if (!mFolderTreeWidget) { return; } mGoToFirstUnreadMessageInSelectedFolder = true; mFolderTreeWidget->folderTreeView()->selectNextUnreadFolder(); mGoToFirstUnreadMessageInSelectedFolder = false; } void KMMainWidget::slotPrevUnreadFolder() { if (!mFolderTreeWidget) { return; } mGoToFirstUnreadMessageInSelectedFolder = true; mFolderTreeWidget->folderTreeView()->selectPrevUnreadFolder(); mGoToFirstUnreadMessageInSelectedFolder = false; } void KMMainWidget::slotExpandThread() { mMessagePane->setCurrentThreadExpanded(true); } void KMMainWidget::slotCollapseThread() { mMessagePane->setCurrentThreadExpanded(false); } void KMMainWidget::slotExpandAllThreads() { // TODO: Make this asynchronous ? (if there is enough demand) #ifndef QT_NO_CURSOR KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy()); #endif mMessagePane->setAllThreadsExpanded(true); } void KMMainWidget::slotCollapseAllThreads() { // TODO: Make this asynchronous ? (if there is enough demand) #ifndef QT_NO_CURSOR KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy()); #endif mMessagePane->setAllThreadsExpanded(false); } //----------------------------------------------------------------------------- void KMMainWidget::updateMessageMenu() { updateMessageActions(); } void KMMainWidget::startUpdateMessageActionsTimer() { // FIXME: This delay effectively CAN make the actions to be in an incoherent state // Maybe we should mark actions as "dirty" here and check it in every action handler... updateMessageActions(true); menutimer->stop(); menutimer->start(500); } void KMMainWidget::updateMessageActions(bool fast) { Akonadi::Item::List selectedItems; Akonadi::Item::List selectedVisibleItems; bool allSelectedBelongToSameThread = false; if (mCurrentFolder && mCurrentFolder->isValid() && mMessagePane->getSelectionStats(selectedItems, selectedVisibleItems, &allSelectedBelongToSameThread) ) { mMsgActions->setCurrentMessage(mMessagePane->currentItem(), selectedVisibleItems); } else { mMsgActions->setCurrentMessage(Akonadi::Item()); } if (!fast) { updateMessageActionsDelayed(); } } void KMMainWidget::updateMessageActionsDelayed() { int count; Akonadi::Item::List selectedItems; Akonadi::Item::List selectedVisibleItems; bool allSelectedBelongToSameThread = false; Akonadi::Item currentMessage; if (mCurrentFolder && mCurrentFolder->isValid() && mMessagePane->getSelectionStats(selectedItems, selectedVisibleItems, &allSelectedBelongToSameThread) ) { count = selectedItems.count(); currentMessage = mMessagePane->currentItem(); } else { count = 0; currentMessage = Akonadi::Item(); } mApplyFiltersOnFolder->setEnabled(mCurrentFolder && mCurrentFolder->isValid()); // // Here we have: // // - A list of selected messages stored in selectedSernums. // The selected messages might contain some invisible ones as a selected // collapsed node "includes" all the children in the selection. // - A list of selected AND visible messages stored in selectedVisibleSernums. // This list does not contain children of selected and collapsed nodes. // // Now, actions can operate on: // - Any set of messages // These are called "mass actions" and are enabled whenever we have a message selected. // In fact we should differentiate between actions that operate on visible selection // and those that operate on the selection as a whole (without considering visibility)... // - A single thread // These are called "thread actions" and are enabled whenever all the selected messages belong // to the same thread. If the selection doesn't cover the whole thread then the action // will act on the whole thread anyway (thus will silently extend the selection) // - A single message // And we have two sub-cases: // - The selection must contain exactly one message // These actions can't ignore the hidden messages and thus must be disabled if // the selection contains any. // - The selection must contain exactly one visible message // These actions will ignore the hidden message and thus can be enabled if // the selection contains any. // bool readOnly = mCurrentFolder && mCurrentFolder->isValid() && (mCurrentFolder->rights() & Akonadi::Collection::ReadOnly); // can we apply strictly single message actions ? (this is false if the whole selection contains more than one message) bool single_actions = count == 1; // can we apply loosely single message actions ? (this is false if the VISIBLE selection contains more than one message) bool singleVisibleMessageSelected = selectedVisibleItems.count() == 1; // can we apply "mass" actions to the selection ? (this is actually always true if the selection is non-empty) bool mass_actions = count >= 1; // does the selection identify a single thread ? bool thread_actions = mass_actions && allSelectedBelongToSameThread && mMessagePane->isThreaded(); // can we apply flags to the selected messages ? bool flags_available = KMailSettings::self()->allowLocalFlags() || !(mCurrentFolder && mCurrentFolder->isValid() ? readOnly : true); mThreadStatusMenu->setEnabled(thread_actions); // these need to be handled individually, the user might have them // in the toolbar mWatchThreadAction->setEnabled(thread_actions && flags_available); mIgnoreThreadAction->setEnabled(thread_actions && flags_available); mMarkThreadAsReadAction->setEnabled(thread_actions); mMarkThreadAsUnreadAction->setEnabled(thread_actions); mToggleThreadToActAction->setEnabled(thread_actions && flags_available); mToggleThreadImportantAction->setEnabled(thread_actions && flags_available); bool canDeleteMessages = mCurrentFolder && mCurrentFolder->isValid() && (mCurrentFolder->rights() & Akonadi::Collection::CanDeleteItem); mTrashThreadAction->setEnabled(thread_actions && canDeleteMessages); mDeleteThreadAction->setEnabled(thread_actions && canDeleteMessages); if (currentMessage.isValid()) { MessageStatus status; status.setStatusFromFlags(currentMessage.flags()); mTagActionManager->updateActionStates(count, mMessagePane->currentItem()); if (thread_actions) { mToggleThreadToActAction->setChecked(status.isToAct()); mToggleThreadImportantAction->setChecked(status.isImportant()); mWatchThreadAction->setChecked(status.isWatched()); mIgnoreThreadAction->setChecked(status.isIgnored()); } } mMoveActionMenu->setEnabled(mass_actions && canDeleteMessages); if (mMoveMsgToFolderAction) { mMoveMsgToFolderAction->setEnabled(mass_actions && canDeleteMessages); } //mCopyActionMenu->setEnabled( mass_actions ); mDeleteAction->setEnabled(mass_actions && canDeleteMessages); mExpireConfigAction->setEnabled(canDeleteMessages); if (mMsgView) { mMsgView->findInMessageAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentFolder->collection())); } mMsgActions->forwardInlineAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentFolder->collection())); mMsgActions->forwardAttachedAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentFolder->collection())); mMsgActions->forwardMenu()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentFolder->collection())); mMsgActions->editAction()->setEnabled(single_actions); mUseAction->setEnabled(single_actions && CommonKernel->folderIsTemplates(mCurrentFolder->collection())); filterMenu()->setEnabled(single_actions); mMsgActions->redirectAction()->setEnabled(/*single_actions &&*/mass_actions && !CommonKernel->folderIsTemplates(mCurrentFolder->collection())); if (mMsgActions->customTemplatesMenu()) { mMsgActions->customTemplatesMenu()->forwardActionMenu()->setEnabled(mass_actions); mMsgActions->customTemplatesMenu()->replyActionMenu()->setEnabled(single_actions); mMsgActions->customTemplatesMenu()->replyAllActionMenu()->setEnabled(single_actions); } // "Print" will act on the current message: it will ignore any hidden selection mMsgActions->printAction()->setEnabled(singleVisibleMessageSelected); // "Print preview" will act on the current message: it will ignore any hidden selection QAction *printPreviewAction = mMsgActions->printPreviewAction(); if (printPreviewAction) { printPreviewAction->setEnabled(singleVisibleMessageSelected); } // "View Source" will act on the current message: it will ignore any hidden selection if (mMsgView) { mMsgView->viewSourceAction()->setEnabled(singleVisibleMessageSelected); } MessageStatus status; status.setStatusFromFlags(currentMessage.flags()); QList< QAction *> actionList; bool statusSendAgain = single_actions && ((currentMessage.isValid() && status.isSent()) || (currentMessage.isValid() && CommonKernel->folderIsSentMailFolder(mCurrentFolder->collection()))); if (statusSendAgain) { actionList << mSendAgainAction; } else if (single_actions) { actionList << messageActions()->editAction(); } actionList << mSaveAttachmentsAction; if (mCurrentFolder && FolderArchive::FolderArchiveUtil::resourceSupportArchiving(mCurrentFolder->collection().resource())) { actionList << mArchiveAction; } mGUIClient->unplugActionList(QStringLiteral("messagelist_actionlist")); mGUIClient->plugActionList(QStringLiteral("messagelist_actionlist"), actionList); mSendAgainAction->setEnabled(statusSendAgain); mSaveAsAction->setEnabled(mass_actions); if ((mCurrentFolder && mCurrentFolder->isValid())) { updateMoveAction(mCurrentFolder->statistics()); } else { updateMoveAction(false, false); } const qint64 nbMsgOutboxCollection = MailCommon::Util::updatedCollection(CommonKernel->outboxCollectionFolder()).statistics().count(); mSendQueued->setEnabled(nbMsgOutboxCollection > 0); mSendActionMenu->setEnabled(nbMsgOutboxCollection > 0); const bool newPostToMailingList = mCurrentFolder && mCurrentFolder->isMailingListEnabled(); mMessageNewList->setEnabled(newPostToMailingList); slotUpdateOnlineStatus(static_cast(KMailSettings::self()->networkState())); if (action(QStringLiteral("kmail_undo"))) { action(QStringLiteral("kmail_undo"))->setEnabled(kmkernel->undoStack()->size() > 0); } // Enable / disable all filters. foreach (QAction *filterAction, mFilterMenuActions) { filterAction->setEnabled(count > 0); } mApplyAllFiltersAction->setEnabled(count); mApplyFilterActionsMenu->setEnabled(count); } void KMMainWidget::slotAkonadiStandardActionUpdated() { bool multiFolder = false; if (mFolderTreeWidget) { multiFolder = mFolderTreeWidget->selectedCollections().count() > 1; } if (mCollectionProperties) { if (mCurrentFolder && mCurrentFolder->collection().isValid()) { const Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(mCurrentFolder->collection().resource()); mCollectionProperties->setEnabled(!multiFolder && !mCurrentFolder->isStructural() && (instance.status() != Akonadi::AgentInstance::Broken)); } else { mCollectionProperties->setEnabled(false); } QList< QAction * > collectionProperties; if (mCollectionProperties->isEnabled()) { collectionProperties << mCollectionProperties; } mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_collectionproperties_actionlist")); mGUIClient->plugActionList(QStringLiteral("akonadi_collection_collectionproperties_actionlist"), collectionProperties); } const bool folderWithContent = mCurrentFolder && !mCurrentFolder->isStructural(); if (mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections)) { mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections)->setEnabled(mCurrentFolder && !multiFolder && (mCurrentFolder->collection().rights() & Collection::CanDeleteCollection) && !mCurrentFolder->isSystemFolder() && folderWithContent); } if (mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)) { mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)->setEnabled(folderWithContent && (mCurrentFolder->count() > 0) && mCurrentFolder->canDeleteMessages() && !multiFolder); mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)->setText((mCurrentFolder && CommonKernel->folderIsTrash(mCurrentFolder->collection())) ? i18n("E&mpty Trash") : i18n("&Move All Messages to Trash")); } QList< QAction * > addToFavorite; QAction *actionAddToFavoriteCollections = akonadiStandardAction(Akonadi::StandardActionManager::AddToFavoriteCollections); if (actionAddToFavoriteCollections) { if (mEnableFavoriteFolderView && actionAddToFavoriteCollections->isEnabled()) { addToFavorite << actionAddToFavoriteCollections; } mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_add_to_favorites_actionlist")); mGUIClient->plugActionList(QStringLiteral("akonadi_collection_add_to_favorites_actionlist"), addToFavorite); } QList< QAction * > syncActionList; QAction *actionSync = akonadiStandardAction(Akonadi::StandardActionManager::SynchronizeCollections); if (actionSync && actionSync->isEnabled()) { syncActionList << actionSync; } actionSync = akonadiStandardAction(Akonadi::StandardActionManager::SynchronizeCollectionsRecursive); if (actionSync && actionSync->isEnabled()) { syncActionList << actionSync; } mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_sync_actionlist")); mGUIClient->plugActionList(QStringLiteral("akonadi_collection_sync_actionlist"), syncActionList); QList< QAction * > actionList; QAction *action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CreateCollection); if (action && action->isEnabled()) { actionList << action; } action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::MoveCollectionToMenu); if (action && action->isEnabled()) { actionList << action; } action = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::CopyCollectionToMenu); if (action && action->isEnabled()) { actionList << action; } mGUIClient->unplugActionList(QStringLiteral("akonadi_collection_move_copy_menu_actionlist")); mGUIClient->plugActionList(QStringLiteral("akonadi_collection_move_copy_menu_actionlist"), actionList); } void KMMainWidget::updateHtmlMenuEntry() { if (mDisplayMessageFormatMenu && mPreferHtmlLoadExtAction) { bool multiFolder = false; if (mFolderTreeWidget) { multiFolder = mFolderTreeWidget->selectedCollections().count() > 1; } // the visual ones only make sense if we are showing a message list const bool enabledAction = (mFolderTreeWidget && mFolderTreeWidget->folderTreeView()->currentFolder().isValid() && !multiFolder); mDisplayMessageFormatMenu->setEnabled(enabledAction); const bool isEnabled = (mFolderTreeWidget && mFolderTreeWidget->folderTreeView()->currentFolder().isValid() && !multiFolder); const bool useHtml = (mFolderDisplayFormatPreference == MessageViewer::Viewer::Html || (mHtmlGlobalSetting && mFolderDisplayFormatPreference == MessageViewer::Viewer::UseGlobalSetting)); mPreferHtmlLoadExtAction->setEnabled(isEnabled && useHtml); mDisplayMessageFormatMenu->setDisplayMessageFormat(mFolderDisplayFormatPreference); mPreferHtmlLoadExtAction->setChecked(!multiFolder && (mHtmlLoadExtGlobalSetting ? !mFolderHtmlLoadExtPreference : mFolderHtmlLoadExtPreference)); } } //----------------------------------------------------------------------------- void KMMainWidget::updateFolderMenu() { if (!CommonKernel->outboxCollectionFolder().isValid()) { QTimer::singleShot(1000, this, &KMMainWidget::updateFolderMenu); return; } const bool folderWithContent = mCurrentFolder && !mCurrentFolder->isStructural(); bool multiFolder = false; if (mFolderTreeWidget) { multiFolder = mFolderTreeWidget->selectedCollections().count() > 1; } mFolderMailingListPropertiesAction->setEnabled(folderWithContent && !multiFolder && !mCurrentFolder->isSystemFolder()); QList< QAction * > actionlist; if (mCurrentFolder && mCurrentFolder->collection().id() == CommonKernel->outboxCollectionFolder().id() && (mCurrentFolder->collection()).statistics().count() > 0) { qCDebug(KMAIL_LOG) << "Enabling send queued"; mSendQueued->setEnabled(true); actionlist << mSendQueued; } // if ( mCurrentFolder && mCurrentFolder->collection().id() != CommonKernel->trashCollectionFolder().id() ) { // actionlist << mTrashAction; // } mGUIClient->unplugActionList(QStringLiteral("outbox_folder_actionlist")); mGUIClient->plugActionList(QStringLiteral("outbox_folder_actionlist"), actionlist); actionlist.clear(); const bool isASearchFolder = mCurrentFolder && mCurrentFolder->collection().resource() == QLatin1String("akonadi_search_resource"); if (isASearchFolder) { mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections)->setText(i18n("&Delete Search")); } mArchiveFolderAction->setEnabled(mCurrentFolder && !multiFolder && folderWithContent); bool isInTrashFolder = (mCurrentFolder && CommonKernel->folderIsTrash(mCurrentFolder->collection())); QAction *moveToTrash = akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash); akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash)->setText(isInTrashFolder ? i18nc("@action Hard delete, bypassing trash", "&Delete") : i18n("&Move to Trash")); akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash)->setIcon(isInTrashFolder ? QIcon::fromTheme(QStringLiteral("edit-delete")) : QIcon::fromTheme(QStringLiteral("user-trash"))); //Use same text as in Text property. Change it in kf5 moveToTrash->setToolTip(isInTrashFolder ? i18nc("@action Hard delete, bypassing trash", "&Delete") : i18n("&Move to Trash")); mTrashThreadAction->setIcon(isInTrashFolder ? QIcon::fromTheme(QStringLiteral("edit-delete")) : QIcon::fromTheme(QStringLiteral("user-trash"))); mTrashThreadAction->setText(isInTrashFolder ? i18n("Delete T&hread") : i18n("M&ove Thread to Trash")); mSearchMessages->setText((mCurrentFolder && mCurrentFolder->collection().resource() == QLatin1String("akonadi_search_resource")) ? i18n("Edit Search...") : i18n("&Find Messages...")); mExpireConfigAction->setEnabled(mCurrentFolder && !mCurrentFolder->isStructural() && !multiFolder && mCurrentFolder->canDeleteMessages() && folderWithContent && !MailCommon::Util::isVirtualCollection(mCurrentFolder->collection())); updateHtmlMenuEntry(); mShowFolderShortcutDialogAction->setEnabled(!multiFolder && folderWithContent); actionlist << akonadiStandardAction(Akonadi::StandardActionManager::ManageLocalSubscriptions); bool imapFolderIsOnline = false; if (mCurrentFolder && PimCommon::Util::isImapFolder(mCurrentFolder->collection(), imapFolderIsOnline)) { if (imapFolderIsOnline) { actionlist << mServerSideSubscription; } } mGUIClient->unplugActionList(QStringLiteral("collectionview_actionlist")); mGUIClient->plugActionList(QStringLiteral("collectionview_actionlist"), actionlist); } //----------------------------------------------------------------------------- void KMMainWidget::slotIntro() { if (!mMsgView) { return; } mMsgView->clear(true); // hide widgets that are in the way: if (mMessagePane && mLongFolderList) { mMessagePane->hide(); } mMsgView->displayAboutPage(); mCurrentFolder.clear(); } void KMMainWidget::slotShowStartupFolder() { connect(MailCommon::FilterManager::instance(), &FilterManager::filtersChanged, this, &KMMainWidget::initializeFilterActions); // Plug various action lists. This can't be done in the constructor, as that is called before // the main window or Kontact calls createGUI(). // This function however is called with a single shot timer. checkAkonadiServerManagerState(); mFolderShortcutActionManager->createActions(); mTagActionManager->createActions(); messageActions()->setupForwardingActionsList(mGUIClient); QString newFeaturesMD5 = KMReaderWin::newFeaturesMD5(); if (kmkernel->firstStart() || KMailSettings::self()->previousNewFeaturesMD5() != newFeaturesMD5) { KMailSettings::self()->setPreviousNewFeaturesMD5(newFeaturesMD5); slotIntro(); return; } } void KMMainWidget::checkAkonadiServerManagerState() { Akonadi::ServerManager::State state = Akonadi::ServerManager::self()->state(); if (state == Akonadi::ServerManager::Running) { initializeFilterActions(); } else { connect(Akonadi::ServerManager::self(), &ServerManager::stateChanged, this, &KMMainWidget::slotServerStateChanged); } } void KMMainWidget::slotServerStateChanged(Akonadi::ServerManager::State state) { if (state == Akonadi::ServerManager::Running) { initializeFilterActions(); disconnect(Akonadi::ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State))); } } QList KMMainWidget::actionCollections() const { return QList() << actionCollection(); } //----------------------------------------------------------------------------- void KMMainWidget::slotUpdateUndo() { if (actionCollection()->action(QStringLiteral("kmail_undo"))) { QAction *act = actionCollection()->action(QStringLiteral("kmail_undo")); act->setEnabled(!kmkernel->undoStack()->isEmpty()); const QString infoStr = kmkernel->undoStack()->undoInfo(); if (infoStr.isEmpty()) { act->setText(i18n("&Undo")); } else { act->setText(i18n("&Undo: \"%1\"", kmkernel->undoStack()->undoInfo())); } } } //----------------------------------------------------------------------------- void KMMainWidget::clearFilterActions() { if (!mFilterTBarActions.isEmpty()) if (mGUIClient->factory()) { mGUIClient->unplugActionList(QStringLiteral("toolbar_filter_actions")); } if (!mFilterMenuActions.isEmpty()) if (mGUIClient->factory()) { mGUIClient->unplugActionList(QStringLiteral("menu_filter_actions")); } foreach (QAction *a, mFilterMenuActions) { actionCollection()->removeAction(a); } mApplyFilterActionsMenu->menu()->clear(); mFilterTBarActions.clear(); mFilterMenuActions.clear(); qDeleteAll(mFilterCommands); mFilterCommands.clear(); } //----------------------------------------------------------------------------- void KMMainWidget::initializeFilterActions() { clearFilterActions(); mApplyFilterActionsMenu->menu()->addAction(mApplyAllFiltersAction); bool addedSeparator = false; foreach (MailFilter *filter, MailCommon::FilterManager::instance()->filters()) { if (!filter->isEmpty() && filter->configureShortcut() && filter->isEnabled()) { QString filterName = QStringLiteral("Filter %1").arg(filter->name()); QString normalizedName = filterName.replace(QLatin1Char(' '), QLatin1Char('_')); if (action(normalizedName)) { continue; } KMMetaFilterActionCommand *filterCommand = new KMMetaFilterActionCommand(filter->identifier(), this); mFilterCommands.append(filterCommand); QString displayText = i18n("Filter %1", filter->name()); QString icon = filter->icon(); if (icon.isEmpty()) { icon = QStringLiteral("system-run"); } QAction *filterAction = new QAction(QIcon::fromTheme(icon), displayText, actionCollection()); filterAction->setIconText(filter->toolbarName()); // The shortcut configuration is done in the filter dialog. // The shortcut set in the shortcut dialog would not be saved back to // the filter settings correctly. actionCollection()->setShortcutsConfigurable(filterAction, false); actionCollection()->addAction(normalizedName, filterAction); connect(filterAction, &QAction::triggered, filterCommand, &KMMetaFilterActionCommand::start); actionCollection()->setDefaultShortcut(filterAction, filter->shortcut()); if (!addedSeparator) { QAction *a = mApplyFilterActionsMenu->menu()->addSeparator(); mFilterMenuActions.append(a); addedSeparator = true; } mApplyFilterActionsMenu->menu()->addAction(filterAction); mFilterMenuActions.append(filterAction); if (filter->configureToolbar()) { mFilterTBarActions.append(filterAction); } } } if (!mFilterMenuActions.isEmpty() && mGUIClient->factory()) { mGUIClient->plugActionList(QStringLiteral("menu_filter_actions"), mFilterMenuActions); } if (!mFilterTBarActions.isEmpty() && mGUIClient->factory()) { mFilterTBarActions.prepend(mToolbarActionSeparator); mGUIClient->plugActionList(QStringLiteral("toolbar_filter_actions"), mFilterTBarActions); } // Our filters have changed, now enable/disable them updateMessageActions(); } void KMMainWidget::updateFileMenu() { const bool isEmpty = MailCommon::Util::agentInstances().isEmpty(); actionCollection()->action(QStringLiteral("check_mail"))->setEnabled(!isEmpty); actionCollection()->action(QStringLiteral("check_mail_in"))->setEnabled(!isEmpty); } //----------------------------------------------------------------------------- const KMMainWidget::PtrList *KMMainWidget::mainWidgetList() { // better safe than sorry; check whether the global static has already been destroyed if (theMainWidgetList.isDestroyed()) { return Q_NULLPTR; } return theMainWidgetList; } QSharedPointer KMMainWidget::currentFolder() const { return mCurrentFolder; } //----------------------------------------------------------------------------- QString KMMainWidget::overrideEncoding() const { if (mMsgView) { return mMsgView->overrideEncoding(); } else { return MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding(); } } void KMMainWidget::showEvent(QShowEvent *event) { QWidget::showEvent(event); mWasEverShown = true; } void KMMainWidget::slotRequestFullSearchFromQuickSearch() { // First, open the search window. If we are currently on a search folder, // the search associated with that will be loaded. if (!slotSearch()) { return; } assert(mSearchWin); // Now we look at the current state of the quick search, and if there's // something in there, we add the criteria to the existing search for // the search folder, if applicable, or make a new one from it. SearchPattern pattern; const QString searchString = mMessagePane->currentFilterSearchString(); if (!searchString.isEmpty()) { QByteArray searchStringVal; searchStringVal = ""; pattern.append(SearchRule::createInstance(searchStringVal, SearchRule::FuncContains, searchString)); QList statusList = mMessagePane->currentFilterStatus(); Q_FOREACH (MessageStatus status, statusList) { if (status.hasAttachment()) { pattern.append(SearchRule::createInstance(searchStringVal, SearchRule::FuncHasAttachment, searchString)); status.setHasAttachment(false); } if (!status.isOfUnknownStatus()) { pattern.append(SearchRule::Ptr(new SearchRuleStatus(status))); } } } if (!pattern.isEmpty()) { mSearchWin->addRulesToSearchPattern(pattern); } } void KMMainWidget::updateVacationScriptStatus(bool active, const QString &serverName) { mVacationScriptIndicator->setVacationScriptActive(active, serverName); mVacationIndicatorActive = mVacationScriptIndicator->hasVacationScriptActive(); } QWidget *KMMainWidget::vacationScriptIndicator() const { return mVacationScriptIndicator; } void KMMainWidget::updateVacationScriptStatus() { updateVacationScriptStatus(mVacationIndicatorActive); } KMail::TagActionManager *KMMainWidget::tagActionManager() const { return mTagActionManager; } KMail::FolderShortcutActionManager *KMMainWidget::folderShortcutActionManager() const { return mFolderShortcutActionManager; } void KMMainWidget::slotMessageSelected(const Akonadi::Item &item) { delete mShowBusySplashTimer; mShowBusySplashTimer = Q_NULLPTR; if (mMsgView) { // The current selection was cleared, so we'll remove the previously // selected message from the preview pane if (!item.isValid()) { mMsgView->clear(); } else { mShowBusySplashTimer = new QTimer(this); mShowBusySplashTimer->setSingleShot(true); connect(mShowBusySplashTimer, &QTimer::timeout, this, &KMMainWidget::slotShowBusySplash); mShowBusySplashTimer->start(1000); Akonadi::ItemFetchJob *itemFetchJob = MessageViewer::Viewer::createFetchJob(item); if (mCurrentFolder) { const QString resource = mCurrentFolder->collection().resource(); itemFetchJob->setProperty("_resource", QVariant::fromValue(resource)); connect(itemFetchJob, &ItemFetchJob::itemsReceived, this, &KMMainWidget::itemsReceived); connect(itemFetchJob, &Akonadi::ItemFetchJob::result, this, &KMMainWidget::itemsFetchDone); } } } } void KMMainWidget::itemsReceived(const Akonadi::Item::List &list) { Q_ASSERT(list.size() == 1); delete mShowBusySplashTimer; mShowBusySplashTimer = Q_NULLPTR; if (!mMsgView) { return; } Item item = list.first(); if (mMessagePane) { mMessagePane->show(); if (mMessagePane->currentItem() != item) { // The user has selected another email already, so don't render this one. // Mark it as read, though, if the user settings say so. if (MessageViewer::MessageViewerSettings::self()->delayedMarkAsRead() && MessageViewer::MessageViewerSettings::self()->delayedMarkTime() == 0) { item.setFlag(Akonadi::MessageFlags::Seen); Akonadi::ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob(item, this); modifyJob->disableRevisionCheck(); modifyJob->setIgnorePayload(true); } return; } } mMsgView->setMessage(item); // reset HTML Q_DECL_OVERRIDE to the folder setting mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference); mMsgView->setHtmlLoadExtOverride(mFolderHtmlLoadExtPreference); mMsgView->setDecryptMessageOverwrite(false); mMsgActions->setCurrentMessage(item); } void KMMainWidget::itemsFetchDone(KJob *job) { delete mShowBusySplashTimer; mShowBusySplashTimer = Q_NULLPTR; if (job->error()) { // Unfortunately job->error() is Job::Unknown in many cases. // (see JobPrivate::handleResponse in akonadi/job.cpp) // So we show the "offline" page after checking the resource status. qCDebug(KMAIL_LOG) << job->error() << job->errorString(); const QString resource = job->property("_resource").toString(); const Akonadi::AgentInstance agentInstance = Akonadi::AgentManager::self()->instance(resource); if (!agentInstance.isOnline()) { // The resource is offline if (mMsgView) { mMsgView->viewer()->enableMessageDisplay(); mMsgView->clear(true); } mMessagePane->show(); if (kmkernel->isOffline()) { showOfflinePage(); } else { if (mMsgView) { showResourceOfflinePage(); } } } else { // Some other error BroadcastStatus::instance()->setStatusMsg(job->errorString()); } } } QAction *KMMainWidget::akonadiStandardAction(Akonadi::StandardActionManager::Type type) { return mAkonadiStandardActionManager->action(type); } QAction *KMMainWidget::akonadiStandardAction(Akonadi::StandardMailActionManager::Type type) { return mAkonadiStandardActionManager->action(type); } void KMMainWidget::slotRemoveDuplicates() { RemoveDuplicateMailJob *job = new RemoveDuplicateMailJob(mFolderTreeWidget->folderTreeView()->selectionModel(), this, this); job->start(); } void KMMainWidget::slotServerSideSubscription() { if (!mCurrentFolder) { return; } PimCommon::ManageServerSideSubscriptionJob *job = new PimCommon::ManageServerSideSubscriptionJob(this); job->setCurrentCollection(mCurrentFolder->collection()); job->setParentWidget(this); job->start(); } void KMMainWidget::savePaneSelection() { if (mMessagePane) { mMessagePane->saveCurrentSelection(); } } void KMMainWidget::updatePaneTagComboBox() { if (mMessagePane) { mMessagePane->updateTagComboBox(); } } void KMMainWidget::slotCreateAddressBookContact() { CreateNewContactJob *job = new CreateNewContactJob(this, this); job->start(); } void KMMainWidget::slotOpenRecentMsg(const QUrl &url) { KMOpenMsgCommand *openCommand = new KMOpenMsgCommand(this, url, overrideEncoding(), this); openCommand->start(); } void KMMainWidget::addRecentFile(const QUrl &mUrl) { mOpenRecentAction->addUrl(mUrl); KConfigGroup grp = mConfig->group(QStringLiteral("Recent Files")); mOpenRecentAction->saveEntries(grp); grp.sync(); } void KMMainWidget::slotMoveMessageToTrash() { if (messageView() && messageView()->viewer() && mCurrentFolder) { KMTrashMsgCommand *command = new KMTrashMsgCommand(mCurrentFolder->collection(), messageView()->viewer()->messageItem(), -1); command->start(); } } void KMMainWidget::slotArchiveMails() { if (mCurrentFolder && mCurrentFolder->collection().isValid()) { const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); KMKernel::self()->folderArchiveManager()->setArchiveItems(selectedMessages, mCurrentFolder->collection().resource()); } } void KMMainWidget::updateQuickSearchLineText() { //If change change shortcut mMessagePane->setQuickSearchClickMessage(i18nc("Show shortcut for focus quick search. Don't change it", "Search...<%1>", mQuickSearchAction->shortcut().toString())); } void KMMainWidget::slotChangeDisplayMessageFormat(MessageViewer::Viewer::DisplayFormatMessage format) { if (format == MessageViewer::Viewer::Html) { const int result = KMessageBox::warningContinueCancel(this, // the warning text is taken from configuredialog.cpp: i18n("Use of HTML in mail will make you more vulnerable to " "\"spam\" and may increase the likelihood that your system will be " "compromised by other present and anticipated security exploits."), i18n("Security Warning"), KGuiItem(i18n("Use HTML")), KStandardGuiItem::cancel(), QStringLiteral("OverrideHtmlWarning"), Q_NULLPTR); if (result == KMessageBox::Cancel) { mDisplayMessageFormatMenu->setDisplayMessageFormat(MessageViewer::Viewer::Text); return; } } mFolderDisplayFormatPreference = format; //Update mPrefererHtmlLoadExtAction const bool useHtml = (mFolderDisplayFormatPreference == MessageViewer::Viewer::Html || (mHtmlGlobalSetting && mFolderDisplayFormatPreference == MessageViewer::Viewer::UseGlobalSetting)); mPreferHtmlLoadExtAction->setEnabled(useHtml); if (mMsgView) { mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference); mMsgView->update(true); } } void KMMainWidget::populateMessageListStatusFilterCombo() { mMessagePane->populateStatusFilterCombo(); } void KMMainWidget::slotCollectionRemoved(const Akonadi::Collection &col) { if (mFavoritesModel) { mFavoritesModel->removeCollection(col); } } void KMMainWidget::slotMarkAllMessageAsReadInCurrentFolderAndSubfolder() { if (mCurrentFolder && mCurrentFolder->collection().isValid()) { MarkAllMessagesAsReadInFolderAndSubFolderJob *job = new MarkAllMessagesAsReadInFolderAndSubFolderJob(this); job->setTopLevelCollection(mCurrentFolder->collection()); job->start(); } } diff --git a/kmail/kmmainwidget.h b/kmail/kmmainwidget.h index a05bfcbed4..86eca646e4 100644 --- a/kmail/kmmainwidget.h +++ b/kmail/kmmainwidget.h @@ -1,673 +1,674 @@ /* This file is part of KMail, the KDE mail client. Copyright (c) 2002 Don Sanders Based on the work of Stefan Taferner KMail is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. KMail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __KMMAINWIDGET #define __KMMAINWIDGET #include "kmail_export.h" #include "kmreaderwin.h" //for inline actions #include "kmkernel.h" // for access to config #include "mailcommon/foldertreewidget.h" #include #include "messageactions.h" #include #include #include #include #include #include #include namespace MailTransport { class Transport; } namespace Akonadi { class Tag; } namespace KMime { class Message; } class QUrl; class QVBoxLayout; class QSplitter; class KMLaunchExternalComponent; class DisplayMessageFormatActionMenu; class QAction; class KActionMenu; class KToggleAction; class KMMetaFilterActionCommand; class CollectionPane; class KMCommand; class KMMoveCommand; class KRecentFilesAction; class ManageShowCollectionProperties; class KActionMenuTransport; class KActionMenuAccount; template class QMap; namespace KIO { class Job; } namespace KMail { class SearchWindow; class VacationScriptIndicatorWidget; class TagActionManager; class FolderShortcutActionManager; } namespace KSieveUi { class SieveDebugDialog; class ManageSieveScriptsDialog; class VacationManager; } namespace MailCommon { class FolderSelectionDialog; class FavoriteCollectionWidget; } class KMAIL_EXPORT KMMainWidget : public QWidget { Q_OBJECT public: typedef QList PtrList; KMMainWidget(QWidget *parent, KXMLGUIClient *aGUIClient, KActionCollection *actionCollection, KSharedConfig::Ptr config = KMKernel::self()->config()); virtual ~KMMainWidget(); void destruct(); /** Read configuration options before widgets are created. */ void readPreConfig(); /** Read configuration for current folder. */ void readFolderConfig(); /** Write configuration for current folder. */ void writeFolderConfig(); /** Read configuration options after widgets are created. */ void readConfig(); /** Write configuration options. */ void writeConfig(bool force = true); void writeReaderConfig(); /** Easy access to main components of the window. */ KMReaderWin *messageView() const; /** Access to the header list pane. */ CollectionPane *messageListPane() const; QSharedPointer currentFolder() const; static void cleanup(); QAction *action(const QString &name) { return mActionCollection->action(name); } KActionMenu *filterMenu() const { return mFilterMenu; } KActionMenu *mailingListActionMenu() const { return mMsgActions->mailingListActionMenu(); } QAction *editAction() const { return mMsgActions->editAction(); } QAction *sendAgainAction() const { return mSendAgainAction; } QAction *sendQueuedAction() const { return mSendQueued; } KActionMenuTransport *sendQueueViaMenu() const { return mSendActionMenu; } KMail::MessageActions *messageActions() const { return mMsgActions; } /** Returns a list of all KMMainWidgets. Warning, the list itself can be 0. @return the list of all main widgets, or 0 if it is not yet initialized */ static const PtrList *mainWidgetList(); QWidget *vacationScriptIndicator() const; void updateVacationScriptStatus(); MailCommon::FolderTreeView *folderTreeView() const { return mFolderTreeWidget->folderTreeView(); } /** Returns the XML GUI client. */ KXMLGUIClient *guiClient() const { return mGUIClient; } KMail::TagActionManager *tagActionManager() const; KMail::FolderShortcutActionManager *folderShortcutActionManager() const; void savePaneSelection(); void updatePaneTagComboBox(); void clearViewer(); void addRecentFile(const QUrl &mUrl); void updateQuickSearchLineText(); void populateMessageListStatusFilterCombo(); + public Q_SLOTS: // Moving messages around /** * This will ask for a destination folder and move the currently selected * messages (in MessageListView) into it. */ void slotMoveSelectedMessageToFolder(); // Copying messages around /** * This will ask for a destination folder and copy the currently selected * messages (in MessageListView) into it. */ void slotCopySelectedMessagesToFolder(); /** * Implements the "move to trash" action */ void slotTrashSelectedMessages(); void slotCheckMail(); void slotCheckMailOnStartup(); /** Select the given folder If the folder is 0 the intro is shown */ void folderSelected(const Akonadi::Collection &col); /** Open a separate viewer window containing the specified message. */ void slotMessageActivated(const Akonadi::Item &); /** Opens mail in the internal viewer. */ void slotMessageSelected(const Akonadi::Item &); void slotItemsFetchedForActivation(KMCommand *command); void slotMessageStatusChangeRequest(const Akonadi::Item &, const Akonadi::MessageStatus &, const Akonadi::MessageStatus &); void slotReplaceMsgByUnencryptedVersion(); /** Update message menu */ void updateMessageMenu(); /** Start a timer to update message actions */ void startUpdateMessageActionsTimer(); /** Update message actions */ void updateMessageActions(bool fast = false); void updateMessageActionsDelayed(); /** Clear and create actions for marked filters */ void clearFilterActions(); void initializeFilterActions(); /** Trigger the dialog for editing out-of-office scripts. */ void slotEditVacation(const QString &serverName = QString()); /** Adds if not existing/removes if existing the tag identified by @p aLabel in all selected messages */ void slotUpdateMessageTagList(const Akonadi::Tag &tag); void slotSelectMoreMessageTagList(); /** * Convenience function to get the action collection in a list. * * @return a list of action collections. The list only has one item, and * that is the action collection of this main widget as returned * by actionCollection(). */ QList actionCollections() const; QAction *akonadiStandardAction(Akonadi::StandardActionManager::Type type); QAction *akonadiStandardAction(Akonadi::StandardMailActionManager::Type type); Akonadi::StandardMailActionManager *standardMailActionManager() const { return mAkonadiStandardActionManager; } void refreshMessageListSelection(); void slotStartCheckMail(); void slotEndCheckMail(); void slotRemoveDuplicates(); void slotSelectCollectionFolder(const Akonadi::Collection &col); void restoreCollectionFolderViewConfig(); void slotSearchButton(); Q_SIGNALS: void messagesTransfered(bool); void captionChangeRequest(const QString &caption); void recreateGui(); protected: void setupActions(); void createWidgets(); void deleteWidgets(); void layoutSplitters(); void newFromTemplate(const Akonadi::Item &); void moveSelectedMessagesToFolder(const Akonadi::Collection &dest); void copySelectedMessagesToFolder(const Akonadi::Collection &dest); void showEvent(QShowEvent *event) Q_DECL_OVERRIDE; KActionCollection *actionCollection() const { return mActionCollection; } /** @return the correct config dialog depending on whether the parent of the mainWidget is a KPart or a KMMainWindow. When dealing with geometries, use this pointer */ KSharedConfig::Ptr config(); protected Q_SLOTS: void updateFileMenu(); void slotHelp(); void slotFilter(); void slotManageSieveScripts(); void slotCompose(); void slotPostToML(); void slotExpireFolder(); void slotExpireAll(); void slotArchiveFolder(); void slotRemoveFolder(); void slotEmptyFolder(); void slotClearCurrentFolder(); void slotAddFavoriteFolder(); void slotShowSelectedFolderInPane(); void slotOverrideHtmlLoadExt(); void slotMessageQueuedOrDrafted(); void slotUseTemplate(); void slotDeleteMsg(bool confirmDelete = true); // completely delete message void slotTrashThread(); void slotDeleteThread(bool confirmDelete = true); // completely delete thread void slotUndo(); void slotReadOn(); void slotSaveMsg(); void slotOpenMsg(); void slotSaveAttachments(); void slotJumpToFolder(); void slotResendMsg(); void slotCheckVacation(); void slotDebugSieve(); void slotApplyFilters(); void slotApplyFiltersOnFolder(); void slotExpandThread(); void slotExpandAllThreads(); void slotCollapseThread(); void slotCollapseAllThreads(); void slotSetThreadStatusUnread(); void slotSetThreadStatusRead(); void slotSetThreadStatusImportant(); void slotSetThreadStatusToAct(); void slotSetThreadStatusWatched(); void slotSetThreadStatusIgnored(); void slotSendQueued(); void slotSendQueuedVia(MailTransport::Transport *transport); void slotOnlineStatus(); void slotUpdateOnlineStatus(KMailSettings::EnumNetworkState::type); void slotMessagePopup(const Akonadi::Item &, const QUrl &, const QUrl &imageUrl, const QPoint &); void slotContactSearchJobForMessagePopupDone(KJob *job); void slotSelectAllMessages(); void slotFocusQuickSearch(); bool slotSearch(); void slotIntro(); void slotShowStartupFolder(); /** Message navigation */ void slotSelectNextMessage(); void slotExtendSelectionToNextMessage(); void slotSelectNextUnreadMessage(); void slotSelectPreviousMessage(); void slotExtendSelectionToPreviousMessage(); void slotSelectPreviousUnreadMessage(); void slotFocusOnNextMessage(); void slotFocusOnPrevMessage(); void slotSelectFirstMessage(); void slotSelectLastMessage(); void slotSelectFocusedMessage(); void slotNextUnreadFolder(); void slotPrevUnreadFolder(); /** etc. */ void slotDisplayCurrentMessage(); void slotShowNewFromTemplate(); void slotDelayedShowNewFromTemplate(KJob *); void slotNewFromTemplate(QAction *); /** Update the undo action */ void slotUpdateUndo(); /** Update html and threaded messages preferences in Folder menu. */ void updateFolderMenu(); /** Settings menu */ /** XML-GUI stuff */ void slotEditNotifications(); /** Slot to reply to a message */ void slotCustomReplyToMsg(const QString &tmpl); void slotCustomReplyAllToMsg(const QString &tmpl); void slotForwardInlineMsg(); void slotForwardAttachedMsg(); void slotRedirectMsg(); void slotCustomForwardMsg(const QString &tmpl); void slotSubjectFilter(); void slotFromFilter(); void slotToFilter(); void slotConfigChanged(); /** Show a splash screen for the longer-lasting operation */ void slotShowBusySplash(); /** Show a message screen explaining that we are currently offline, when an online folder is selected. */ void showOfflinePage(); void showResourceOfflinePage(); void updateVacationScriptStatus(bool active, const QString &serverName = QString()); void slotItemAdded(const Akonadi::Item &, const Akonadi::Collection &col); void slotItemRemoved(const Akonadi::Item &); void slotItemMoved(const Akonadi::Item &item, const Akonadi::Collection &from, const Akonadi::Collection &to); void slotCollectionStatisticsChanged(Akonadi::Collection::Id, const Akonadi::CollectionStatistics &); void slotAkonadiStandardActionUpdated(); void slotCollectionChanged(const Akonadi::Collection &, const QSet &); void slotCreateNewTab(bool); void slotUpdateActionsAfterMailChecking(); void slotCreateAddressBookContact(); void slotOpenRecentMsg(const QUrl &url); private: void checkAkonadiServerManagerState(); void updateHtmlMenuEntry(); void updateMoveAction(const Akonadi::CollectionStatistics &statistic); void updateMoveAction(bool hasUnreadMails, bool hasMails); void updateAllToTrashAction(int statistics); /** Get override character encoding. */ QString overrideEncoding() const; void moveMessageSelected(MessageList::Core::MessageItemSetReference ref, const Akonadi::Collection &dest, bool confirmOnDeletion = true); void copyMessageSelected(const Akonadi::Item::List &selectMsg, const Akonadi::Collection &dest); /** * Move the messages referenced by the specified set to trash. * The set parameter must not be null and the ownership is passed * to this function. */ void trashMessageSelected(MessageList::Core::MessageItemSetReference ref); /** * Set the status of the messages referenced by the specified set, eventually toggling it. * The set parameter must not be null and the ownership is passed to this function. */ void setMessageSetStatus(const Akonadi::Item::List &select, const Akonadi::MessageStatus &status, bool toggle ); /** * Toggles a tag for the messages referenced by the specified set. * The set parameter must not be null and the ownership is passed to this function. */ void toggleMessageSetTag(const Akonadi::Item::List &select, const Akonadi::Tag &tag); /** * This applies setMessageSetStatus() on the current thread. */ void setCurrentThreadStatus(const Akonadi::MessageStatus &status, bool toggle); void applyFilters(const Akonadi::Item::List &selectedMessages); /** * Internal helper that creates the folder selection dialog used for the * move and copy to folder actions on demand. Only folders where items can * be added are listed. */ MailCommon::FolderSelectionDialog *moveOrCopyToDialog(); /** * Internal helper that creates the folder selection dialog used for * jumping to folders, or adding them as favourites. All folders are listed. */ MailCommon::FolderSelectionDialog *selectFromAllFoldersDialog(); /** * Internal helper that applies the current settings so the * favorite folder view. */ void refreshFavoriteFoldersViewProperties(); void openFilterDialog(const QByteArray &field, const QString &value); void showMessagePopup(const Akonadi::Item &msg, const QUrl &aUrl, const QUrl &imageUrl, const QPoint &aPoint, bool contactAlreadyExists, bool uniqueContactFound); private Q_SLOTS: void slotMoveMessageToTrash(); /** * Called when a "move to trash" operation is completed */ void slotTrashMessagesCompleted(KMMoveCommand *command); /** * Called when a "move" operation is completed */ void slotMoveMessagesCompleted(KMMoveCommand *command); /** * Called when a "copy" operation is completed */ void slotCopyMessagesCompleted(KMCommand *command); void slotRequestFullSearchFromQuickSearch(); void slotFolderChanged(const Akonadi::Collection &); void slotCollectionFetched(int collectionId); void itemsReceived(const Akonadi::Item::List &list); void itemsFetchDone(KJob *job); void slotServerSideSubscription(); void slotFetchItemsForFolderDone(KJob *job); void slotServerStateChanged(Akonadi::ServerManager::State state); void slotArchiveMails(); void slotChangeDisplayMessageFormat(MessageViewer::Viewer::DisplayFormatMessage format); void slotCollectionRemoved(const Akonadi::Collection &col); void slotCcFilter(); void slotBandwidth(bool b); void slotDeleteMessages(); void slotMarkAllMessageAsReadInCurrentFolderAndSubfolder(); private: // Message actions QAction *mDeleteAction; QAction *mTrashThreadAction; QAction *mDeleteThreadAction; QAction *mSaveAsAction; QAction *mUseAction; QAction *mSendAgainAction; QAction *mApplyAllFiltersAction; QAction *mSaveAttachmentsAction; QAction *mOpenAction; QAction *mMoveMsgToFolderAction; QAction *mCollectionProperties; QAction *mSendQueued; QAction *mArchiveAction; KActionMenuTransport *mSendActionMenu; // Filter actions KActionMenu *mFilterMenu; QAction *mExpireConfigAction; QAction *mApplyFiltersOnFolder; // Custom template actions menu KActionMenu *mTemplateMenu; QAction *mHideShowSearchBarAction; KActionMenu *mThreadStatusMenu, *mApplyFilterActionsMenu; QAction *mCopyActionMenu; QAction *mMoveActionMenu; QAction *mMarkThreadAsReadAction; QAction *mMarkThreadAsUnreadAction; KToggleAction *mToggleThreadImportantAction; KToggleAction *mToggleThreadToActAction; KToggleAction *mWatchThreadAction, *mIgnoreThreadAction; MailCommon::FavoriteCollectionWidget *mFavoriteCollectionsView; Akonadi::FavoriteCollectionsModel *mFavoritesModel; QWidget *mSearchAndTree; KMReaderWin *mMsgView; QSplitter *mSplitter1; QSplitter *mSplitter2; QSplitter *mFolderViewSplitter; Akonadi::Collection mTemplateFolder; bool mLongFolderList; bool mStartupDone; bool mWasEverShown; bool mHtmlGlobalSetting; bool mHtmlLoadExtGlobalSetting; bool mFolderHtmlLoadExtPreference; bool mReaderWindowActive; bool mReaderWindowBelow; bool mEnableFavoriteFolderView; bool mEnableFolderQuickSearch; QPointer mSearchWin; QAction *mExpireFolderAction; QAction *mFolderMailingListPropertiesAction; QAction *mShowFolderShortcutDialogAction; QAction *mArchiveFolderAction; QAction *mMessageNewList; KToggleAction *mPreferHtmlLoadExtAction; QTimer *menutimer; QTimer *mShowBusySplashTimer; KSieveUi::VacationManager *mVacationManager; #if !defined(NDEBUG) QPointer mSieveDebugDialog; #endif KActionCollection *mActionCollection; QAction *mToolbarActionSeparator; QVBoxLayout *mTopLayout; bool mDestructed; QList mFilterMenuActions; QList mFilterTBarActions; QList mFilterCommands; KMail::TagActionManager *mTagActionManager; KMail::FolderShortcutActionManager *mFolderShortcutActionManager; KSharedConfig::Ptr mConfig; KXMLGUIClient *mGUIClient; KMail::MessageActions *mMsgActions; Akonadi::StandardMailActionManager *mAkonadiStandardActionManager; CollectionPane *mMessagePane; QSharedPointer mCurrentFolder; MailCommon::FolderTreeWidget *mFolderTreeWidget; KMail::VacationScriptIndicatorWidget *mVacationScriptIndicator; bool mVacationIndicatorActive; bool mGoToFirstUnreadMessageInSelectedFolder; MessageList::Core::PreSelectionMode mPreSelectionMode; QTimer mCheckMailTimer; QPointer mMoveOrCopyToDialog; QPointer mSelectFromAllFoldersDialog; QAction *mServerSideSubscription; KRecentFilesAction *mOpenRecentAction; QPointer mManageSieveDialog; QAction *mQuickSearchAction; DisplayMessageFormatActionMenu *mDisplayMessageFormatMenu; MessageViewer::Viewer::DisplayFormatMessage mFolderDisplayFormatPreference; QAction *mSearchMessages; KMLaunchExternalComponent *mLaunchExternalComponent; ManageShowCollectionProperties *mManageShowCollectionProperties; QAction *mShowIntroductionAction; KToggleAction *mLowBandwithAction; QAction *mMarkAllMessageAsReadAndInAllSubFolder; KActionMenuAccount *mAccountActionMenu; }; #endif diff --git a/messagelist/src/pane.cpp b/messagelist/src/pane.cpp index 95041238e4..fa585a3955 100644 --- a/messagelist/src/pane.cpp +++ b/messagelist/src/pane.cpp @@ -1,1189 +1,1203 @@ /* Copyright (c) 2009 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "pane.h" #include #include #include #include #include #include #include -#include + #include #include #include #include #include #include #include #include #include "storagemodel.h" #include "core/widgets/quicksearchline.h" #include "widget.h" #include "messagelistsettings.h" #include "core/manager.h" #include #include "core/model.h" namespace MessageList { class Q_DECL_HIDDEN Pane::Private { public: Private(Pane *owner) : q(owner), mXmlGuiClient(Q_NULLPTR), mActionMenu(Q_NULLPTR), mModel(Q_NULLPTR), mSelectionModel(Q_NULLPTR), mPreSelectionMode(Core::PreSelectLastSelected), mNewTabButton(Q_NULLPTR), mCloseTabButton(Q_NULLPTR), mCloseTabAction(Q_NULLPTR), mActivateNextTabAction(Q_NULLPTR), mActivatePreviousTabAction(Q_NULLPTR), mMoveTabLeftAction(Q_NULLPTR), mMoveTabRightAction(Q_NULLPTR), mPreferEmptyTab(false), mMaxTabCreated(0) { } void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void onNewTabClicked(); void onCloseTabClicked(); void activateTab(); void closeTab(QWidget *); void onCurrentTabChanged(); void onTabContextMenuRequest(const QPoint &pos); void activateNextTab(); void activatePreviousTab(); void moveTabLeft(); void moveTabRight(); void moveTabBackward(); void moveTabForward(); void changeQuicksearchVisibility(bool); void addActivateTabAction(int i); void slotTabCloseRequested(int index); QItemSelection mapSelectionToSource(const QItemSelection &selection) const; QItemSelection mapSelectionFromSource(const QItemSelection &selection) const; void updateTabControls(); Pane *const q; KXMLGUIClient *mXmlGuiClient; KActionMenu *mActionMenu; QAbstractItemModel *mModel; QItemSelectionModel *mSelectionModel; Core::PreSelectionMode mPreSelectionMode; QHash mWidgetSelectionHash; QList mProxyStack; QToolButton *mNewTabButton; QToolButton *mCloseTabButton; QAction *mCloseTabAction; QAction *mActivateNextTabAction; QAction *mActivatePreviousTabAction; QAction *mMoveTabLeftAction; QAction *mMoveTabRightAction; bool mPreferEmptyTab; int mMaxTabCreated; }; } // namespace MessageList using namespace Akonadi; using namespace MessageList; Pane::Pane(bool restoreSession, QAbstractItemModel *model, QItemSelectionModel *selectionModel, QWidget *parent) : QTabWidget(parent), d(new Private(this)) { setDocumentMode(true); d->mModel = model; d->mSelectionModel = selectionModel; // Build the proxy stack const QAbstractProxyModel *proxyModel = qobject_cast(d->mSelectionModel->model()); while (proxyModel) { if (static_cast(proxyModel) == d->mModel) { break; } d->mProxyStack << proxyModel; const QAbstractProxyModel *nextProxyModel = qobject_cast(proxyModel->sourceModel()); if (!nextProxyModel) { // It's the final model in the chain, so it is necessarily the sourceModel. Q_ASSERT(qobject_cast(proxyModel->sourceModel()) == d->mModel); break; } proxyModel = nextProxyModel; } // Proxy stack done d->mNewTabButton = new QToolButton(this); d->mNewTabButton->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); d->mNewTabButton->adjustSize(); d->mNewTabButton->setToolTip(i18nc("@info:tooltip", "Open a new tab")); #ifndef QT_NO_ACCESSIBILITY d->mNewTabButton->setAccessibleName(i18n("New tab")); #endif setCornerWidget(d->mNewTabButton, Qt::TopLeftCorner); connect(d->mNewTabButton, SIGNAL(clicked()), SLOT(onNewTabClicked())); d->mCloseTabButton = new QToolButton(this); d->mCloseTabButton->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); d->mCloseTabButton->adjustSize(); d->mCloseTabButton->setToolTip(i18nc("@info:tooltip", "Close the current tab")); #ifndef QT_NO_ACCESSIBILITY d->mCloseTabButton->setAccessibleName(i18n("Close tab")); #endif setCornerWidget(d->mCloseTabButton, Qt::TopRightCorner); connect(d->mCloseTabButton, SIGNAL(clicked()), SLOT(onCloseTabClicked())); setTabsClosable(MessageListSettings::self()->tabsHaveCloseButton()); connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(slotTabCloseRequested(int))); readConfig(restoreSession); setMovable(true); connect(d->mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(onSelectionChanged(QItemSelection,QItemSelection))); connect(this, SIGNAL(currentChanged(int)), this, SLOT(onCurrentTabChanged())); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onTabContextMenuRequest(QPoint))); connect(MessageListSettings::self(), SIGNAL(configChanged()), this, SLOT(updateTabControls())); connect(this, &QTabWidget::tabBarDoubleClicked, this, &Pane::createNewTab); tabBar()->installEventFilter(this); } Pane::~Pane() { writeConfig(true); delete d; } void Pane::Private::addActivateTabAction(int i) { QString actionname; actionname.sprintf("activate_tab_%02d", i); QAction *action = new QAction(i18n("Activate Tab %1", i), q); mXmlGuiClient->actionCollection()->addAction(actionname, action); mXmlGuiClient->actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("Alt+%1").arg(i))); connect(action, SIGNAL(triggered(bool)), q, SLOT(activateTab())); } void Pane::Private::slotTabCloseRequested(int index) { QWidget *w = q->widget(index); if (w) { closeTab(w); } } void Pane::setXmlGuiClient(KXMLGUIClient *xmlGuiClient) { d->mXmlGuiClient = xmlGuiClient; - KToggleAction *const showHideQuicksearch = new KToggleAction(i18n("Show Quick Search Bar"), this); + showHideQuicksearch = new KToggleAction(i18n("Show Quick Search Bar"), this); d->mXmlGuiClient->actionCollection()->setDefaultShortcut(showHideQuicksearch, Qt::CTRL + Qt::Key_H); showHideQuicksearch->setChecked(MessageListSettings::showQuickSearch()); d->mXmlGuiClient->actionCollection()->addAction(QStringLiteral("show_quick_search"), showHideQuicksearch); connect(showHideQuicksearch, SIGNAL(triggered(bool)), this, SLOT(changeQuicksearchVisibility(bool))); for (int i = 0; i < count(); ++i) { Widget *w = qobject_cast(widget(i)); w->setXmlGuiClient(d->mXmlGuiClient); } // Setup "View->Message List" actions. if (xmlGuiClient) { if (d->mActionMenu) { d->mXmlGuiClient->actionCollection()->removeAction(d->mActionMenu); } d->mActionMenu = new KActionMenu(QIcon(), i18n("Message List"), this); d->mXmlGuiClient->actionCollection()->addAction(QStringLiteral("view_message_list"), d->mActionMenu); MessageList::Util::fillViewMenu(d->mActionMenu->menu(), this); d->mActionMenu->addSeparator(); QAction *action = new QAction(i18n("Create New Tab"), this); d->mXmlGuiClient->actionCollection()->addAction(QStringLiteral("create_new_tab"), action); d->mXmlGuiClient->actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O)); connect(action, SIGNAL(triggered(bool)), SLOT(onNewTabClicked())); d->mActionMenu->addAction(action); d->mMaxTabCreated = count(); for (int i = 1; i < 10 && i <= count(); ++i) { d->addActivateTabAction(i); } d->mCloseTabAction = new QAction(i18n("Close Tab"), this); d->mXmlGuiClient->actionCollection()->addAction(QStringLiteral("close_current_tab"), d->mCloseTabAction); d->mXmlGuiClient->actionCollection()->setDefaultShortcut(d->mCloseTabAction, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W)); connect(d->mCloseTabAction, SIGNAL(triggered(bool)), SLOT(onCloseTabClicked())); d->mActionMenu->addAction(d->mCloseTabAction); d->mCloseTabAction->setEnabled(false); d->mActivateNextTabAction = new QAction(i18n("Activate Next Tab"), this); d->mXmlGuiClient->actionCollection()->addAction(QStringLiteral("activate_next_tab"), d->mActivateNextTabAction); d->mActivateNextTabAction->setEnabled(false); connect(d->mActivateNextTabAction, SIGNAL(triggered(bool)), SLOT(activateNextTab())); d->mActivatePreviousTabAction = new QAction(i18n("Activate Previous Tab"), this); d->mXmlGuiClient->actionCollection()->addAction(QStringLiteral("activate_previous_tab"), d->mActivatePreviousTabAction); d->mActivatePreviousTabAction->setEnabled(false); connect(d->mActivatePreviousTabAction, SIGNAL(triggered(bool)), SLOT(activatePreviousTab())); d->mMoveTabLeftAction = new QAction(i18n("Move Tab Left"), this); d->mXmlGuiClient->actionCollection()->addAction(QStringLiteral("move_tab_left"), d->mMoveTabLeftAction); d->mMoveTabLeftAction->setEnabled(false); connect(d->mMoveTabLeftAction, SIGNAL(triggered(bool)), SLOT(moveTabLeft())); d->mMoveTabRightAction = new QAction(i18n("Move Tab Right"), this); d->mXmlGuiClient->actionCollection()->addAction(QStringLiteral("move_tab_right"), d->mMoveTabRightAction); d->mMoveTabRightAction->setEnabled(false); connect(d->mMoveTabRightAction, SIGNAL(triggered(bool)), SLOT(moveTabRight())); } } +void Pane::executeActionShowQuicksearch(){ + showHideQuicksearch->trigger(); +} + +bool Pane::showHideQuicksearchIsChecked() const{ + return showHideQuicksearch->isChecked(); +} + bool Pane::selectNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop) { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return true; } return w->selectNextMessageItem(messageTypeFilter, existingSelectionBehaviour, centerItem, loop); } else { return false; } } bool Pane::selectPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop) { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return true; } return w->selectPreviousMessageItem(messageTypeFilter, existingSelectionBehaviour, centerItem, loop); } else { return false; } } bool Pane::focusNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop) { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return true; } return w->focusNextMessageItem(messageTypeFilter, centerItem, loop); } else { return false; } } bool Pane::focusPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop) { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return true; } return w->focusPreviousMessageItem(messageTypeFilter, centerItem, loop); } else { return false; } } void Pane::selectFocusedMessageItem(bool centerItem) { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return; } w->selectFocusedMessageItem(centerItem); } } bool Pane::selectFirstMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem) { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return true; } return w->selectFirstMessageItem(messageTypeFilter, centerItem); } else { return false; } } bool Pane::selectLastMessageItem(Core::MessageTypeFilter messageTypeFilter, bool centerItem) { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return true; } return w->selectLastMessageItem(messageTypeFilter, centerItem); } else { return false; } } void Pane::selectAll() { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return; } w->selectAll(); } } void Pane::setCurrentThreadExpanded(bool expand) { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return; } w->setCurrentThreadExpanded(expand); } } void Pane::setAllThreadsExpanded(bool expand) { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return; } w->setAllThreadsExpanded(expand); } } void Pane::setAllGroupsExpanded(bool expand) { Widget *w = static_cast(currentWidget()); if (w) { if (w->view()->model()->isLoading()) { return; } w->setAllGroupsExpanded(expand); } } void Pane::focusQuickSearch(const QString &selectedText) { Widget *w = static_cast(currentWidget()); if (w) { w->focusQuickSearch(selectedText); } } void Pane::setQuickSearchClickMessage(const QString &msg) { Widget *w = static_cast(currentWidget()); if (w) { w->setQuickSearchClickMessage(msg); } } void Pane::Private::onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { if (mPreferEmptyTab) { q->createNewTab(); } Widget *w = static_cast(q->currentWidget()); QItemSelectionModel *s = mWidgetSelectionHash[w]; w->saveCurrentSelection(); // Deselect old before we select new - so that the messagelist can clear first. s->select(mapSelectionToSource(deselected), QItemSelectionModel::Deselect); if (s->selection().isEmpty()) { w->view()->model()->setPreSelectionMode(mPreSelectionMode); } s->select(mapSelectionToSource(selected), QItemSelectionModel::Select); QString label; QIcon icon; QString toolTip; foreach (const QModelIndex &index, s->selectedRows()) { label += index.data(Qt::DisplayRole).toString() + QLatin1String(", "); } label.chop(2); if (label.isEmpty()) { label = i18nc("@title:tab Empty messagelist", "Empty"); icon = QIcon(); } else if (s->selectedRows().size() == 1) { icon = s->selectedRows().first().data(Qt::DecorationRole).value(); QModelIndex idx = s->selectedRows().first().parent(); toolTip = label; while (idx != QModelIndex()) { toolTip = idx.data().toString() + QLatin1Char('/') + toolTip; idx = idx.parent(); } } else { icon = QIcon::fromTheme(QStringLiteral("folder")); } const int index = q->indexOf(w); q->setTabText(index, label); q->setTabIcon(index, icon); q->setTabToolTip(index, toolTip); if (mPreferEmptyTab) { disconnect(mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(onSelectionChanged(QItemSelection,QItemSelection))); mSelectionModel->select(mapSelectionFromSource(s->selection()), QItemSelectionModel::ClearAndSelect); connect(mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(onSelectionChanged(QItemSelection,QItemSelection))); } } void Pane::Private::activateTab() { q->tabBar()->setCurrentIndex(q->sender()->objectName().rightRef(2).toInt() - 1); } void Pane::Private::moveTabRight() { const int numberOfTab = q->tabBar()->count(); if (numberOfTab == 1) { return; } if (QApplication::isRightToLeft()) { moveTabForward(); } else { moveTabBackward(); } } void Pane::Private::moveTabLeft() { const int numberOfTab = q->tabBar()->count(); if (numberOfTab == 1) { return; } if (QApplication::isRightToLeft()) { moveTabBackward(); } else { moveTabForward(); } } void Pane::Private::moveTabForward() { const int currentIndex = q->tabBar()->currentIndex(); if (currentIndex == q->tabBar()->count() - 1) { return; } q->tabBar()->moveTab(currentIndex, currentIndex + 1); } void Pane::Private::moveTabBackward() { const int currentIndex = q->tabBar()->currentIndex(); if (currentIndex == 0) { return; } q->tabBar()->moveTab(currentIndex, currentIndex - 1); } void Pane::Private::activateNextTab() { const int numberOfTab = q->tabBar()->count(); if (numberOfTab == 1) { return; } int indexTab = (q->tabBar()->currentIndex() + 1); if (indexTab == numberOfTab) { indexTab = 0; } q->tabBar()->setCurrentIndex(indexTab); } void Pane::Private::activatePreviousTab() { const int numberOfTab = q->tabBar()->count(); if (numberOfTab == 1) { return; } int indexTab = (q->tabBar()->currentIndex() - 1); if (indexTab == -1) { indexTab = numberOfTab - 1; } q->tabBar()->setCurrentIndex(indexTab); } void Pane::Private::onNewTabClicked() { q->createNewTab(); } void Pane::Private::onCloseTabClicked() { closeTab(q->currentWidget()); } void Pane::Private::closeTab(QWidget *w) { if (!w || (q->count() < 2)) { return; } delete w; updateTabControls(); } void Pane::Private::changeQuicksearchVisibility(bool show) { for (int i = 0; i < q->count(); ++i) { Widget *w = qobject_cast(q->widget(i)); w->changeQuicksearchVisibility(show); } + + /* Put the same state for the 2 QAction */ + if(q->showHideQuicksearchIsChecked()) + mXmlGuiClient->actionCollection()->action(QStringLiteral("search_button"))->setChecked(true); + else + mXmlGuiClient->actionCollection()->action(QStringLiteral("search_button"))->setChecked(false); } bool Pane::eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *const mouseEvent = static_cast(event); if (mouseEvent->button() == Qt::MidButton) { return true; } } return QTabWidget::eventFilter(object, event); } void Pane::Private::onCurrentTabChanged() { Q_EMIT q->currentTabChanged(); Widget *w = static_cast(q->currentWidget()); QItemSelectionModel *s = mWidgetSelectionHash[w]; disconnect(mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(onSelectionChanged(QItemSelection,QItemSelection))); mSelectionModel->select(mapSelectionFromSource(s->selection()), QItemSelectionModel::ClearAndSelect); connect(mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(onSelectionChanged(QItemSelection,QItemSelection))); } void Pane::Private::onTabContextMenuRequest(const QPoint &pos) { QTabBar *bar = q->tabBar(); if (q->count() <= 1) { return; } const int indexBar = bar->tabAt(bar->mapFrom(q, pos)); if (indexBar == -1) { return; } Widget *w = qobject_cast(q->widget(indexBar)); if (!w) { return; } QMenu menu(q); QAction *closeTabAction = menu.addAction(i18nc("@action:inmenu", "Close Tab")); closeTabAction->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); QAction *allOther = menu.addAction(i18nc("@action:inmenu", "Close All Other Tabs")); allOther->setIcon(QIcon::fromTheme(QStringLiteral("tab-close-other"))); QAction *action = menu.exec(q->mapToGlobal(pos)); if (action == allOther) { // Close all other tabs QList widgets; const int index = q->indexOf(w); for (int i = 0; i < q->count(); ++i) { if (i == index) { continue; // Skip the current one } Widget *other = qobject_cast(q->widget(i)); widgets << other; } foreach (Widget *other, widgets) { delete other; } updateTabControls(); } else if (action == closeTabAction) { closeTab(q->widget(indexBar)); } } MessageList::StorageModel *Pane::createStorageModel(QAbstractItemModel *model, QItemSelectionModel *selectionModel, QObject *parent) { return new MessageList::StorageModel(model, selectionModel, parent); } void Pane::setCurrentFolder(const Akonadi::Collection &collection, bool, Core::PreSelectionMode preSelectionMode, const QString &overrideLabel) { d->mPreSelectionMode = preSelectionMode; Widget *w = static_cast(currentWidget()); if (w) { w->setCurrentFolder(collection); QItemSelectionModel *s = d->mWidgetSelectionHash[w]; MessageList::StorageModel *m = createStorageModel(d->mModel, s, w); w->setStorageModel(m, preSelectionMode); if (!overrideLabel.isEmpty()) { int index = indexOf(w); setTabText(index, overrideLabel); } } } void Pane::updateTabIconText(const Akonadi::Collection &collection, const QString &label, const QIcon &icon) { for (int i = 0; i < count(); ++i) { Widget *w = qobject_cast(widget(i)); if (w->currentCollection() == collection) { const int index = indexOf(w); setTabText(index, label); setTabIcon(index, icon); } } } QItemSelectionModel *Pane::createNewTab() { Widget *w = new Widget(this); w->setXmlGuiClient(d->mXmlGuiClient); addTab(w, i18nc("@title:tab Empty messagelist", "Empty")); if (d->mXmlGuiClient && count() < 10) { if (d->mMaxTabCreated < count()) { d->mMaxTabCreated = count(); d->addActivateTabAction(d->mMaxTabCreated); } } QItemSelectionModel *s = new QItemSelectionModel(d->mModel, w); MessageList::StorageModel *m = createStorageModel(d->mModel, s, w); w->setStorageModel(m); d->mWidgetSelectionHash[w] = s; connect(w, &Widget::messageSelected, this, &Pane::messageSelected); connect(w, &Widget::messageActivated, this, &Pane::messageActivated); connect(w, &Widget::selectionChanged, this, &Pane::selectionChanged); connect(w, &Widget::messageStatusChangeRequest, this, &Pane::messageStatusChangeRequest); connect(w, &Core::Widget::statusMessage, this, &Pane::statusMessage); d->updateTabControls(); setCurrentWidget(w); return s; } QItemSelection Pane::Private::mapSelectionToSource(const QItemSelection &selection) const { QItemSelection result = selection; foreach (const QAbstractProxyModel *proxy, mProxyStack) { result = proxy->mapSelectionToSource(result); } return result; } QItemSelection Pane::Private::mapSelectionFromSource(const QItemSelection &selection) const { QItemSelection result = selection; typedef QList::ConstIterator Iterator; for (Iterator it = mProxyStack.end() - 1; it != mProxyStack.begin(); --it) { result = (*it)->mapSelectionFromSource(result); } result = mProxyStack.first()->mapSelectionFromSource(result); return result; } void Pane::Private::updateTabControls() { const bool enableAction = (q->count() > 1); if (enableAction) { q->setCornerWidget(mCloseTabButton, Qt::TopRightCorner); mCloseTabButton->setVisible(true); } else { q->setCornerWidget(Q_NULLPTR, Qt::TopRightCorner); } if (mCloseTabAction) { mCloseTabAction->setEnabled(enableAction); } if (mActivatePreviousTabAction) { mActivatePreviousTabAction->setEnabled(enableAction); } if (mActivateNextTabAction) { mActivateNextTabAction->setEnabled(enableAction); } if (mMoveTabRightAction) { mMoveTabRightAction->setEnabled(enableAction); } if (mMoveTabLeftAction) { mMoveTabLeftAction->setEnabled(enableAction); } if (MessageListSettings::self()->autoHideTabBarWithSingleTab()) { q->tabBar()->setVisible(enableAction); if (enableAction) { q->setCornerWidget(mNewTabButton, Qt::TopLeftCorner); mNewTabButton->setVisible(true); } else { q->setCornerWidget(Q_NULLPTR, Qt::TopLeftCorner); } } else { q->tabBar()->setVisible(true); q->setCornerWidget(mNewTabButton, Qt::TopLeftCorner); mNewTabButton->setVisible(true); } const bool hasCloseButton(MessageListSettings::self()->tabsHaveCloseButton()); q->setTabsClosable(hasCloseButton); if (hasCloseButton) { const int numberOfTab(q->count()); if (numberOfTab == 1) { q->tabBar()->tabButton(0, QTabBar::RightSide)->setEnabled(false); } else if (numberOfTab > 1) { q->tabBar()->tabButton(0, QTabBar::RightSide)->setEnabled(true); } } } Item Pane::currentItem() const { Widget *w = static_cast(currentWidget()); if (w == Q_NULLPTR) { return Item(); } return w->currentItem(); } KMime::Message::Ptr Pane::currentMessage() const { Widget *w = static_cast(currentWidget()); if (w == Q_NULLPTR) { return KMime::Message::Ptr(); } return w->currentMessage(); } QList Pane::selectionAsMessageList(bool includeCollapsedChildren) const { Widget *w = static_cast(currentWidget()); if (w == Q_NULLPTR) { return QList(); } return w->selectionAsMessageList(includeCollapsedChildren); } Akonadi::Item::List Pane::selectionAsMessageItemList(bool includeCollapsedChildren) const { Widget *w = static_cast(currentWidget()); if (w == Q_NULLPTR) { return Akonadi::Item::List(); } return w->selectionAsMessageItemList(includeCollapsedChildren); } QList Pane::selectionAsListMessageId(bool includeCollapsedChildren) const { Widget *w = static_cast(currentWidget()); if (w == Q_NULLPTR) { return QList(); } return w->selectionAsListMessageId(includeCollapsedChildren); } QVector Pane::selectionAsMessageItemListId(bool includeCollapsedChildren) const { Widget *w = static_cast(currentWidget()); if (w == Q_NULLPTR) { return QVector(); } return w->selectionAsMessageItemListId(includeCollapsedChildren); } Akonadi::Item::List Pane::currentThreadAsMessageList() const { Widget *w = static_cast(currentWidget()); if (w == Q_NULLPTR) { return Akonadi::Item::List(); } return w->currentThreadAsMessageList(); } Akonadi::Item::List Pane::itemListFromPersistentSet(MessageList::Core::MessageItemSetReference ref) { Widget *w = static_cast(currentWidget()); if (w) { return w->itemListFromPersistentSet(ref); } return Akonadi::Item::List(); } void Pane::deletePersistentSet(MessageList::Core::MessageItemSetReference ref) { Widget *w = static_cast(currentWidget()); if (w) { w->deletePersistentSet(ref); } } void Pane::markMessageItemsAsAboutToBeRemoved(MessageList::Core::MessageItemSetReference ref, bool bMark) { Widget *w = static_cast(currentWidget()); if (w) { w->markMessageItemsAsAboutToBeRemoved(ref, bMark); } } QList Pane::currentFilterStatus() const { Widget *w = static_cast(currentWidget()); if (w == Q_NULLPTR) { return QList(); } return w->currentFilterStatus(); } QString Pane::currentFilterSearchString() const { Widget *w = static_cast(currentWidget()); if (w) { return w->currentFilterSearchString(); } return QString(); } bool Pane::isThreaded() const { Widget *w = static_cast(currentWidget()); if (w) { return w->isThreaded(); } return false; } bool Pane::selectionEmpty() const { Widget *w = static_cast(currentWidget()); if (w) { return w->selectionEmpty(); } return false; } bool Pane::getSelectionStats(Akonadi::Item::List &selectedItems, Akonadi::Item::List &selectedVisibleItems, bool *allSelectedBelongToSameThread, bool includeCollapsedChildren) const { Widget *w = static_cast(currentWidget()); if (w == Q_NULLPTR) { return false; } return w->getSelectionStats( selectedItems, selectedVisibleItems, allSelectedBelongToSameThread, includeCollapsedChildren ); } MessageList::Core::MessageItemSetReference Pane::selectionAsPersistentSet(bool includeCollapsedChildren) const { Widget *w = static_cast(currentWidget()); if (w) { return w->selectionAsPersistentSet(includeCollapsedChildren); } return -1; } MessageList::Core::MessageItemSetReference Pane::currentThreadAsPersistentSet() const { Widget *w = static_cast(currentWidget()); if (w) { return w->currentThreadAsPersistentSet(); } return -1; } void Pane::focusView() { Widget *w = static_cast(currentWidget()); if (w) { QWidget *view = w->view(); if (view) { view->setFocus(); } } } void Pane::reloadGlobalConfiguration() { d->updateTabControls(); } QItemSelectionModel *Pane::currentItemSelectionModel() { Widget *w = static_cast(currentWidget()); if (w) { return w->view()->selectionModel(); } return Q_NULLPTR; } void Pane::resetModelStorage() { Widget *w = static_cast(currentWidget()); if (w) { MessageList::StorageModel *m = static_cast(w->storageModel()); if (m) { m->resetModelStorage(); } } } void Pane::setPreferEmptyTab(bool emptyTab) { d->mPreferEmptyTab = emptyTab; } void Pane::saveCurrentSelection() { for (int i = 0; i < count(); ++i) { Widget *w = qobject_cast(widget(i)); w->saveCurrentSelection(); } } void Pane::updateTagComboBox() { for (int i = 0; i < count(); ++i) { Widget *w = qobject_cast(widget(i)); w->populateStatusFilterCombo(); } } void Pane::writeConfig(bool restoreSession) { KConfigGroup conf(MessageList::MessageListSettings::self()->config(), "MessageListPane"); // Delete list before const QStringList list = MessageList::MessageListSettings::self()->config()->groupList().filter(QRegularExpression(QStringLiteral("MessageListTab\\d+"))); foreach (const QString &group, list) { MessageList::MessageListSettings::self()->config()->deleteGroup(group); } if (restoreSession) { conf.writeEntry(QStringLiteral("currentIndex"), currentIndex()); conf.writeEntry(QStringLiteral("tabNumber"), count()); for (int i = 0; i < count(); ++i) { Widget *w = qobject_cast(widget(i)); KConfigGroup grp(MessageList::MessageListSettings::self()->config(), QStringLiteral("MessageListTab%1").arg(i)); grp.writeEntry(QStringLiteral("collectionId"), w->currentCollection().id()); grp.writeEntry(QStringLiteral("HeaderState"), w->view()->header()->saveState()); } } conf.sync(); } void Pane::readConfig(bool restoreSession) { if (MessageList::MessageListSettings::self()->config()->hasGroup(QStringLiteral("MessageListPane"))) { KConfigGroup conf(MessageList::MessageListSettings::self()->config(), "MessageListPane"); const int numberOfTab = conf.readEntry(QStringLiteral("tabNumber"), 0); if (numberOfTab == 0) { createNewTab(); } else { for (int i = 0; i < numberOfTab; ++i) { createNewTab(); restoreHeaderSettings(i); if (restoreSession) { #if 0 //TODO fix me Akonadi::Collection::Id id = grp.readEntry(QStringLiteral("collectionId"), -1); ETMViewStateSaver *saver = new ETMViewStateSaver; saver->setSelectionModel(selectionModel); if (id != -1) { ETMViewStateSaver *saver = new ETMViewStateSaver; saver->setSelectionModel(selectionModel); saver->restoreState(grp); saver->selectCollections(Akonadi::Collection::List() << Akonadi::Collection(id)); saver->restoreCurrentItem(QString::fromLatin1("c%1").arg(id)); } #endif } } setCurrentIndex(conf.readEntry(QStringLiteral("currentIndex"), 0)); } } else { createNewTab(); restoreHeaderSettings(0); } } void Pane::restoreHeaderSettings(int index) { KConfigGroup grp(MessageList::MessageListSettings::self()->config(), QStringLiteral("MessageListTab%1").arg(index)); if (grp.exists()) { Widget *w = qobject_cast(widget(index)); w->view()->header()->restoreState(grp.readEntry(QStringLiteral("HeaderState"), QByteArray())); } } bool Pane::searchEditHasFocus() const { Widget *w = static_cast(currentWidget()); if (w) { return w->searchEditHasFocus(); } return false; } void Pane::sortOrderMenuAboutToShow() { QMenu *menu = dynamic_cast< QMenu * >(sender()); if (!menu) { return; } const Widget *const w = static_cast(currentWidget()); w->view()->sortOrderMenuAboutToShow(menu); } void Pane::aggregationMenuAboutToShow() { QMenu *menu = dynamic_cast< QMenu * >(sender()); if (!menu) { return; } const Widget *const w = static_cast(currentWidget()); w->view()->aggregationMenuAboutToShow(menu); } void Pane::themeMenuAboutToShow() { QMenu *menu = dynamic_cast< QMenu * >(sender()); if (!menu) { return; } const Widget *const w = static_cast(currentWidget()); w->view()->themeMenuAboutToShow(menu); } void Pane::populateStatusFilterCombo() { for (int i = 0; i < count(); ++i) { Widget *w = qobject_cast(widget(i)); w->populateStatusFilterCombo(); } } #include "moc_pane.cpp" diff --git a/messagelist/src/pane.h b/messagelist/src/pane.h index de55549da3..808f3fb8c0 100644 --- a/messagelist/src/pane.h +++ b/messagelist/src/pane.h @@ -1,470 +1,476 @@ /* Copyright (c) 2009 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __MESSAGELIST_PANE_H__ #define __MESSAGELIST_PANE_H__ #include #include #include #include #include #include #include #include #include +#include class KXMLGUIClient; class QAbstractItemModel; class QItemSelectionModel; class QItemSelection; namespace KPIM { class MessageStatus; } namespace MessageList { class Widget; class StorageModel; /** * This is the main MessageList panel for Akonadi applications. * It contains multiple MessageList::Widget tabs * so it can actually display multiple folder sets at once. * * When a KXmlGuiWindow is passed to setXmlGuiClient, the XMLGUI * defined context menu @c akonadi_messagelist_contextmenu is * used if available. * */ class MESSAGELIST_EXPORT Pane : public QTabWidget { Q_OBJECT public: /** * Create a Pane wrapping the specified model and selection. */ explicit Pane(bool restoreSession, QAbstractItemModel *model, QItemSelectionModel *selectionModel, QWidget *parent = Q_NULLPTR); ~Pane(); virtual MessageList::StorageModel *createStorageModel(QAbstractItemModel *model, QItemSelectionModel *selectionModel, QObject *parent); virtual void writeConfig(bool restoreSession); /** * Sets the XML GUI client which the pane is used in. * * This is needed if you want to use the built-in context menu. * Passing 0 is ok and will disable the builtin context menu. * * @param xmlGuiClient The KXMLGUIClient the view is used in. */ void setXmlGuiClient(KXMLGUIClient *xmlGuiClient); /** * Returns the current message for the list as Akonadi::Item. * May return an invalid Item if there is no current message or no current folder. */ Akonadi::Item currentItem() const; /** * Returns the current message for the list as KMime::Message::Ptr. * May return 0 if there is no current message or no current folder. */ KMime::Message::Ptr currentMessage() const; /** * Returns the currently selected KMime::Message::Ptr (bound to current StorageModel). * The list may be empty if there are no selected messages or no StorageModel. * * If includeCollapsedChildren is true then the children of the selected but * collapsed items are also added to the list. * * The returned list is guaranteed to be valid only until you return control * to the main even loop. Don't store it for any longer. If you need to reference * this set of messages at a later stage then take a look at createPersistentSet(). */ QList selectionAsMessageList(bool includeCollapsedChildren = true) const; /** * Returns the currently selected Items (bound to current StorageModel). * The list may be empty if there are no selected messages or no StorageModel. * * If includeCollapsedChildren is true then the children of the selected but * collapsed items are also added to the list. * * The returned list is guaranteed to be valid only until you return control * to the main even loop. Don't store it for any longer. If you need to reference * this set of messages at a later stage then take a look at createPersistentSet(). */ Akonadi::Item::List selectionAsMessageItemList(bool includeCollapsedChildren = true) const; /** * Returns the currently selected Items id(bound to current StorageModel). * The list may be empty if there are no selected messages or no StorageModel. * * If includeCollapsedChildren is true then the children of the selected but * collapsed items are also added to the list. * * The returned list is guaranteed to be valid only until you return control * to the main even loop. Don't store it for any longer. If you need to reference * this set of messages at a later stage then take a look at createPersistentSet(). */ QVector selectionAsMessageItemListId(bool includeCollapsedChildren = true) const; QList selectionAsListMessageId(bool includeCollapsedChildren = true) const; /** * Returns the Akonadi::Item bound to the current StorageModel that * are part of the current thread. The current thread is the thread * that contains currentMessageItem(). * The list may be empty if there is no currentMessageItem() or no StorageModel. * * The returned list is guaranteed to be valid only until you return control * to the main even loop. Don't store it for any longer. If you need to reference * this set of messages at a later stage then take a look at createPersistentSet(). */ Akonadi::Item::List currentThreadAsMessageList() const; /** * Selects the next message item in the view. * * messageTypeFilter can be used to restrict the selection to only certain message types. * * existingSelectionBehaviour specifies how the existing selection * is manipulated. It may be cleared, expanded or grown/shrinked. * * If centerItem is true then the specified item will be positioned * at the center of the view, if possible. * If loop is true then the "next" algorithm will restart from the beginning * of the list if the end is reached, otherwise it will just stop returning false. */ bool selectNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop); /** * Selects the previous message item in the view. * If centerItem is true then the specified item will be positioned * at the center of the view, if possible. * * messageTypeFilter can be used to restrict the selection to only certain message types. * * existingSelectionBehaviour specifies how the existing selection * is manipulated. It may be cleared, expanded or grown/shrinked. * * If loop is true then the "previous" algorithm will restart from the end * of the list if the beginning is reached, otherwise it will just stop returning false. */ bool selectPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour, bool centerItem, bool loop); /** * Focuses the next message item in the view without actually selecting it. * * messageTypeFilter can be used to restrict the selection to only certain message types. * * If centerItem is true then the specified item will be positioned * at the center of the view, if possible. * If loop is true then the "next" algorithm will restart from the beginning * of the list if the end is reached, otherwise it will just stop returning false. */ bool focusNextMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop); /** * Focuses the previous message item in the view without actually selecting it. * * messageTypeFilter can be used to restrict the selection to only certain message types. * * If centerItem is true then the specified item will be positioned * at the center of the view, if possible. * If loop is true then the "previous" algorithm will restart from the end * of the list if the beginning is reached, otherwise it will just stop returning false. */ bool focusPreviousMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem, bool loop); /** * Selects the currently focused message item. May do nothing if the * focused message item is already selected (which is very likely). * If centerItem is true then the specified item will be positioned * at the center of the view, if possible. */ void selectFocusedMessageItem(bool centerItem); /** * Selects the first message item in the view that matches the specified Core::MessageTypeFilter. * If centerItem is true then the specified item will be positioned * at the center of the view, if possible. * * If the current view is already loaded then the request will * be satisfied immediately (well... if an unread message exists at all). * If the current view is still loading then the selection of the first * message will be scheduled to be executed when loading terminates. * * So this function doesn't actually guarantee that an unread or new message * was selected when the call returns. Take care :) * * The function returns true if a message was selected and false otherwise. */ bool selectFirstMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem); /** * Selects the last message item in the view that matches the specified Core::MessageTypeFilter. * If centerItem is true then the specified item will be positioned * at the center of the view, if possible. * * The function returns true if a message was selected and false otherwise. */ bool selectLastMessageItem(MessageList::Core::MessageTypeFilter messageTypeFilter, bool centerItem); /** * If expand is true then it expands the current thread, otherwise * collapses it. */ void setCurrentThreadExpanded(bool expand); /** * If expand is true then it expands all the threads, otherwise * collapses them. */ void setAllThreadsExpanded(bool expand); /** * If expand is true then it expands all the groups (only the toplevel * group item: inner threads are NOT expanded). If expand is false * then it collapses all the groups. If no grouping is in effect * then this function does nothing. */ void setAllGroupsExpanded(bool expand); /** * Sets the focus on the quick search line of the currently active tab. */ void focusQuickSearch(const QString &selectedText = QString()); /** * Returns the Akonadi::MessageStatus in the current quicksearch field. */ QList currentFilterStatus() const; /** * Returns the search term in the current quicksearch field. */ QString currentFilterSearchString() const; /** * Returns true if the current Aggregation is threaded, false otherwise * (or if there is no current Aggregation). */ bool isThreaded() const; /** * Fast function that determines if the selection is empty */ bool selectionEmpty() const; /** * Fills the lists of the selected message serial numbers and of the selected+visible ones. * Returns true if the returned stats are valid (there is a current folder after all) * and false otherwise. This is called by KMMainWidget in a single place so we optimize by * making it a single sweep on the selection. * * If includeCollapsedChildren is true then the children of the selected but * collapsed items are also included in the stats */ bool getSelectionStats(Akonadi::Item::List &selectedItems, Akonadi::Item::List &selectedVisibleItems, bool *allSelectedBelongToSameThread, bool includeCollapsedChildren = true) const; /** * Deletes the persistent set pointed by the specified reference. * If the set does not exist anymore, nothing happens. */ void deletePersistentSet(MessageList::Core::MessageItemSetReference ref); /** * If bMark is true this function marks the messages as "about to be removed" * so they appear dimmer and aren't selectable in the view. * If bMark is false then this function clears the "about to be removed" state * for the specified MessageItems. */ void markMessageItemsAsAboutToBeRemoved(MessageList::Core::MessageItemSetReference ref, bool bMark); /** * Return Akonadi::Item from messageItemReference */ Akonadi::Item::List itemListFromPersistentSet(MessageList::Core::MessageItemSetReference ref); /** * Return a persistent set from current selection */ MessageList::Core::MessageItemSetReference selectionAsPersistentSet(bool includeCollapsedChildren = true) const; /** * Return a persistent set from current thread */ MessageList::Core::MessageItemSetReference currentThreadAsPersistentSet() const; /** * Sets the focus on the view of the currently active tab. */ void focusView(); /** * Reloads global configuration and eventually reloads all the views. */ void reloadGlobalConfiguration(); /** * Returns the QItemSelectionModel for the currently displayed tab. */ QItemSelectionModel *currentItemSelectionModel(); /** * Sets the current folder to be displayed by this Pane. * If the specified folder is already open in one of the tabs * then that tab is made current (and no reloading happens). * If the specified folder is not open yet then behaviour * depends on the preferEmptyTab value as follows. * * If preferEmptyTab is set to false then the (new) folder is loaded * in the current tab. If preferEmptyTab is set to true then the (new) folder is * loaded in the first empty tab (or a new one if there are no empty ones). * * Pre-selection is the action of automatically selecting a message just after the folder * has finished loading. See Model::setStorageModel() for more information. * * If overrideLabel is not empty then it's used as the tab text for the * specified folder. This is useful to signal a particular folder state * like "loading..." */ void setCurrentFolder( const Akonadi::Collection &fld, bool preferEmptyTab = false, MessageList::Core::PreSelectionMode preSelectionMode = MessageList::Core::PreSelectLastSelected, const QString &overrideLabel = QString() ); void resetModelStorage(); void setPreferEmptyTab(bool emptyTab); void updateTabIconText(const Akonadi::Collection &collection, const QString &label, const QIcon &icon); void saveCurrentSelection(); void updateTagComboBox(); bool searchEditHasFocus() const; void setQuickSearchClickMessage(const QString &msg); void populateStatusFilterCombo(); + void executeActionShowQuicksearch(); + + bool showHideQuicksearchIsChecked() const; + public Q_SLOTS: /** * Selects all the items in the current folder. */ void selectAll(); /** * Add a new tab to the Pane and select it. */ QItemSelectionModel *createNewTab(); void sortOrderMenuAboutToShow(); void aggregationMenuAboutToShow(); void themeMenuAboutToShow(); Q_SIGNALS: /** * Emitted when a message is selected (that is, single clicked and thus made current in the view) * Note that this message CAN be 0 (when the current item is cleared, for example). * * This signal is emitted when a SINGLE message is selected in the view, probably * by clicking on it or by simple keyboard navigation. When multiple items * are selected at once (by shift+clicking, for example) then you will get * this signal only for the last clicked message (or at all, if the last shift+clicked * thing is a group header...). You should handle selection changed in this case. */ void messageSelected(const Akonadi::Item &item); /** * Emitted when a message is doubleclicked or activated by other input means */ void messageActivated(const Akonadi::Item &item); /** * Emitted when the selection in the view changes. */ void selectionChanged(); /** * Emitted when a message wants its status to be changed */ void messageStatusChangeRequest(const Akonadi::Item &item, const Akonadi::MessageStatus &set, const Akonadi::MessageStatus &clear); /** * Notify the outside when updating the status bar with a message * could be useful */ void statusMessage(const QString &message); /** * Emitted when the current tab has changed. Clients using the * selection model from currentItemSelectionModel() should * ask for it again, as it may be different now. */ void currentTabChanged(); private: void restoreHeaderSettings(int index); void readConfig(bool restoreSession); Q_PRIVATE_SLOT(d, void onSelectionChanged(const QItemSelection &, const QItemSelection &)) Q_PRIVATE_SLOT(d, void onNewTabClicked()) Q_PRIVATE_SLOT(d, void onCloseTabClicked()) Q_PRIVATE_SLOT(d, void activateTab()) Q_PRIVATE_SLOT(d, void moveTabLeft()) Q_PRIVATE_SLOT(d, void moveTabRight()) Q_PRIVATE_SLOT(d, void activateNextTab()) Q_PRIVATE_SLOT(d, void activatePreviousTab()) Q_PRIVATE_SLOT(d, void closeTab(QWidget *)) Q_PRIVATE_SLOT(d, void onCurrentTabChanged()) Q_PRIVATE_SLOT(d, void onTabContextMenuRequest(const QPoint &)) Q_PRIVATE_SLOT(d, void updateTabControls()) Q_PRIVATE_SLOT(d, void changeQuicksearchVisibility(bool)) Q_PRIVATE_SLOT(d, void slotTabCloseRequested(int index)) bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE; class Private; Private *const d; + KToggleAction *showHideQuicksearch; }; } // namespace MessageList #endif //!__MESSAGELIST_PANE_H__