diff --git a/agents/archivemailagent/archivemailagent.cpp b/agents/archivemailagent/archivemailagent.cpp index ead9bebf7..e55164487 100644 --- a/agents/archivemailagent/archivemailagent.cpp +++ b/agents/archivemailagent/archivemailagent.cpp @@ -1,152 +1,152 @@ /* Copyright (C) 2012-2019 Montel Laurent 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "archivemailagent.h" #include "archivemailagentadaptor.h" #include "archivemailmanager.h" #include "archivemailagentsettings.h" #include #include -#include +#include #include #include #include #include #include -#include +#include //#define DEBUG_ARCHIVEMAILAGENT 1 ArchiveMailAgent::ArchiveMailAgent(const QString &id) : Akonadi::AgentBase(id) { Kdelibs4ConfigMigrator migrate(QStringLiteral("archivemailagent")); migrate.setConfigFiles(QStringList() << QStringLiteral("akonadi_archivemail_agentrc") << QStringLiteral("akonadi_archivemail_agent.notifyrc")); migrate.migrate(); connect(this, &Akonadi::AgentBase::reloadConfiguration, this, &ArchiveMailAgent::reload); mArchiveManager = new ArchiveMailManager(this); connect(mArchiveManager, &ArchiveMailManager::needUpdateConfigDialogBox, this, &ArchiveMailAgent::needUpdateConfigDialogBox); Akonadi::Monitor *collectionMonitor = new Akonadi::Monitor(this); collectionMonitor->setObjectName(QStringLiteral("ArchiveMailCollectionMonitor")); collectionMonitor->fetchCollection(true); collectionMonitor->ignoreSession(Akonadi::Session::defaultSession()); collectionMonitor->collectionFetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All); collectionMonitor->setMimeTypeMonitored(KMime::Message::mimeType()); new ArchiveMailAgentAdaptor(this); KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/ArchiveMailAgent"), this, QDBusConnection::ExportAdaptors); const QString service = Akonadi::ServerManager::self()->agentServiceName(Akonadi::ServerManager::Agent, identifier()); KDBusConnectionPool::threadConnection().registerService(service); connect(collectionMonitor, &Akonadi::Monitor::collectionRemoved, this, &ArchiveMailAgent::mailCollectionRemoved); if (enabledAgent()) { #ifdef DEBUG_ARCHIVEMAILAGENT QTimer::singleShot(1000, mArchiveManager, &ArchiveMailManager::load); #else QTimer::singleShot(1000 * 60 * 5, mArchiveManager, &ArchiveMailManager::load); #endif } mTimer = new QTimer(this); connect(mTimer, &QTimer::timeout, this, &ArchiveMailAgent::reload); mTimer->start(24 * 60 * 60 * 1000); } ArchiveMailAgent::~ArchiveMailAgent() { } void ArchiveMailAgent::setEnableAgent(bool enabled) { if (enabled != ArchiveMailAgentSettings::enabled()) { ArchiveMailAgentSettings::setEnabled(enabled); ArchiveMailAgentSettings::self()->save(); if (!enabled) { mTimer->stop(); pause(); } else { mTimer->start(); } } } bool ArchiveMailAgent::enabledAgent() const { return ArchiveMailAgentSettings::enabled(); } void ArchiveMailAgent::mailCollectionRemoved(const Akonadi::Collection &collection) { mArchiveManager->removeCollection(collection); } void ArchiveMailAgent::doSetOnline(bool online) { if (online) { resume(); } else { pause(); } } void ArchiveMailAgent::reload() { if (isOnline() && enabledAgent()) { mArchiveManager->load(); mTimer->start(); } } void ArchiveMailAgent::pause() { if (isOnline() && enabledAgent()) { mArchiveManager->pause(); } } void ArchiveMailAgent::resume() { if (isOnline() && enabledAgent()) { mArchiveManager->resume(); } } QString ArchiveMailAgent::printArchiveListInfo() const { return mArchiveManager->printArchiveListInfo(); } QString ArchiveMailAgent::printCurrentListInfo() const { return mArchiveManager->printCurrentListInfo(); } void ArchiveMailAgent::archiveFolder(const QString &path, Akonadi::Collection::Id collectionId) { mArchiveManager->archiveFolder(path, collectionId); } AKONADI_AGENT_MAIN(ArchiveMailAgent) diff --git a/agents/followupreminderagent/followupreminderagent.cpp b/agents/followupreminderagent/followupreminderagent.cpp index 7bd9dc5cf..a2eae8d5c 100644 --- a/agents/followupreminderagent/followupreminderagent.cpp +++ b/agents/followupreminderagent/followupreminderagent.cpp @@ -1,123 +1,123 @@ /* Copyright (C) 2014-2019 Montel Laurent 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "followupreminderagent.h" #include "followupremindermanager.h" #include "FollowupReminder/FollowUpReminderUtil" #include "followupreminderadaptor.h" #include "followupreminderagentsettings.h" #include #include #include -#include +#include #include #include #include #include #include "followupreminderagent_debug.h" #include FollowUpReminderAgent::FollowUpReminderAgent(const QString &id) : Akonadi::AgentBase(id) { Kdelibs4ConfigMigrator migrate(QStringLiteral("followupreminderagent")); migrate.setConfigFiles(QStringList() << QStringLiteral("akonadi_followupreminder_agentrc") << QStringLiteral("akonadi_followupreminder_agent.notifyrc")); migrate.migrate(); new FollowUpReminderAgentAdaptor(this); KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/FollowUpReminder"), this, QDBusConnection::ExportAdaptors); const QString service = Akonadi::ServerManager::self()->agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_followupreminder_agent")); KDBusConnectionPool::threadConnection().registerService(service); mManager = new FollowUpReminderManager(this); setNeedsNetwork(true); changeRecorder()->setMimeTypeMonitored(KMime::Message::mimeType()); changeRecorder()->itemFetchScope().setCacheOnly(true); changeRecorder()->itemFetchScope().setFetchModificationTime(false); changeRecorder()->fetchCollection(true); changeRecorder()->setChangeRecordingEnabled(false); changeRecorder()->ignoreSession(Akonadi::Session::defaultSession()); changeRecorder()->collectionFetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All); changeRecorder()->setCollectionMonitored(Akonadi::Collection::root(), true); if (FollowUpReminderAgentSettings::enabled()) { mManager->load(); } mTimer = new QTimer(this); connect(mTimer, &QTimer::timeout, this, &FollowUpReminderAgent::reload); //Reload all each 24hours mTimer->start(24 * 60 * 60 * 1000); } FollowUpReminderAgent::~FollowUpReminderAgent() { } void FollowUpReminderAgent::setEnableAgent(bool enabled) { if (FollowUpReminderAgentSettings::self()->enabled() == enabled) { return; } FollowUpReminderAgentSettings::self()->setEnabled(enabled); FollowUpReminderAgentSettings::self()->save(); if (enabled) { mManager->load(); mTimer->start(); } else { mTimer->stop(); } } bool FollowUpReminderAgent::enabledAgent() const { return FollowUpReminderAgentSettings::self()->enabled(); } void FollowUpReminderAgent::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) { if (!enabledAgent()) { return; } if (item.mimeType() != KMime::Message::mimeType()) { qCDebug(FOLLOWUPREMINDERAGENT_LOG) << "FollowUpReminderAgent::itemAdded called for a non-message item!"; return; } mManager->checkFollowUp(item, collection); } void FollowUpReminderAgent::reload() { if (enabledAgent()) { mManager->load(true); mTimer->start(); } } QString FollowUpReminderAgent::printDebugInfo() const { return mManager->printDebugInfo(); } AKONADI_AGENT_MAIN(FollowUpReminderAgent) diff --git a/agents/followupreminderagent/followupremindermanager.cpp b/agents/followupreminderagent/followupremindermanager.cpp index 792b7b1f0..d36c34cc2 100644 --- a/agents/followupreminderagent/followupremindermanager.cpp +++ b/agents/followupreminderagent/followupremindermanager.cpp @@ -1,191 +1,191 @@ /* Copyright (C) 2014-2019 Montel Laurent 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "followupremindermanager.h" #include "followupreminderagent_debug.h" #include "FollowupReminder/FollowUpReminderInfo" #include "FollowupReminder/FollowUpReminderUtil" #include "followupremindernoanswerdialog.h" #include "jobs/followupreminderjob.h" #include "jobs/followupreminderfinishtaskjob.h" #include #include #include #include -#include +#include #include #include using namespace FollowUpReminder; FollowUpReminderManager::FollowUpReminderManager(QObject *parent) : QObject(parent) { mConfig = KSharedConfig::openConfig(); } FollowUpReminderManager::~FollowUpReminderManager() { qDeleteAll(mFollowUpReminderInfoList); mFollowUpReminderInfoList.clear(); } void FollowUpReminderManager::load(bool forceReloadConfig) { if (forceReloadConfig) { mConfig->reparseConfiguration(); } const QStringList itemList = mConfig->groupList().filter(QRegularExpression(QStringLiteral("FollowupReminderItem \\d+"))); const int numberOfItems = itemList.count(); QList noAnswerList; for (int i = 0; i < numberOfItems; ++i) { KConfigGroup group = mConfig->group(itemList.at(i)); FollowUpReminderInfo *info = new FollowUpReminderInfo(group); if (info->isValid()) { if (!info->answerWasReceived()) { mFollowUpReminderInfoList.append(info); if (!mInitialize) { FollowUpReminderInfo *noAnswerInfo = new FollowUpReminderInfo(*info); noAnswerList.append(noAnswerInfo); } else { delete info; } } else { delete info; } } else { delete info; } } if (!noAnswerList.isEmpty()) { mInitialize = true; if (!mNoAnswerDialog.data()) { mNoAnswerDialog = new FollowUpReminderNoAnswerDialog; connect(mNoAnswerDialog.data(), &FollowUpReminderNoAnswerDialog::needToReparseConfiguration, this, &FollowUpReminderManager::slotReparseConfiguration); } mNoAnswerDialog->setInfo(noAnswerList); mNoAnswerDialog->wakeUp(); } } void FollowUpReminderManager::slotReparseConfiguration() { load(true); } void FollowUpReminderManager::checkFollowUp(const Akonadi::Item &item, const Akonadi::Collection &col) { if (mFollowUpReminderInfoList.isEmpty()) { return; } const Akonadi::SpecialMailCollections::Type type = Akonadi::SpecialMailCollections::self()->specialCollectionType(col); switch (type) { case Akonadi::SpecialMailCollections::Trash: case Akonadi::SpecialMailCollections::Outbox: case Akonadi::SpecialMailCollections::Drafts: case Akonadi::SpecialMailCollections::Templates: case Akonadi::SpecialMailCollections::SentMail: return; default: break; } FollowUpReminderJob *job = new FollowUpReminderJob(this); connect(job, &FollowUpReminderJob::finished, this, &FollowUpReminderManager::slotCheckFollowUpFinished); job->setItem(item); job->start(); } void FollowUpReminderManager::slotCheckFollowUpFinished(const QString &messageId, Akonadi::Item::Id id) { for (FollowUpReminderInfo *info : qAsConst(mFollowUpReminderInfoList)) { qCDebug(FOLLOWUPREMINDERAGENT_LOG) << "FollowUpReminderManager::slotCheckFollowUpFinished info:" << info; if (!info) { continue; } if (info->messageId() == messageId) { info->setAnswerMessageItemId(id); info->setAnswerWasReceived(true); answerReceived(info->to()); if (info->todoId() != -1) { FollowUpReminderFinishTaskJob *job = new FollowUpReminderFinishTaskJob(info->todoId(), this); connect(job, &FollowUpReminderFinishTaskJob::finishTaskDone, this, &FollowUpReminderManager::slotFinishTaskDone); connect(job, &FollowUpReminderFinishTaskJob::finishTaskFailed, this, &FollowUpReminderManager::slotFinishTaskFailed); job->start(); } //Save item FollowUpReminder::FollowUpReminderUtil::writeFollowupReminderInfo(FollowUpReminder::FollowUpReminderUtil::defaultConfig(), info, true); break; } } } void FollowUpReminderManager::slotFinishTaskDone() { qCDebug(FOLLOWUPREMINDERAGENT_LOG) << " Task Done"; //TODO } void FollowUpReminderManager::slotFinishTaskFailed() { qCDebug(FOLLOWUPREMINDERAGENT_LOG) << " Task Failed"; //TODO } void FollowUpReminderManager::answerReceived(const QString &from) { KNotification::event(QStringLiteral("mailreceived"), QString(), i18n("Answer from %1 received", from), QStringLiteral("kmail"), nullptr, KNotification::CloseOnTimeout, QStringLiteral("akonadi_followupreminder_agent")); } QString FollowUpReminderManager::printDebugInfo() const { QString infoStr; if (mFollowUpReminderInfoList.isEmpty()) { infoStr = QStringLiteral("No mail"); } else { for (FollowUpReminder::FollowUpReminderInfo *info : qAsConst(mFollowUpReminderInfoList)) { if (!infoStr.isEmpty()) { infoStr += QLatin1Char('\n'); } infoStr += infoToStr(info); } } return infoStr; } QString FollowUpReminderManager::infoToStr(FollowUpReminder::FollowUpReminderInfo *info) const { QString infoStr = QStringLiteral("****************************************"); infoStr += QStringLiteral("Akonadi Item id :%1\n").arg(info->originalMessageItemId()); infoStr += QStringLiteral("MessageId :%1\n").arg(info->messageId()); infoStr += QStringLiteral("Subject :%1\n").arg(info->subject()); infoStr += QStringLiteral("To :%1\n").arg(info->to()); infoStr += QStringLiteral("Dead Line :%1\n").arg(info->followUpReminderDate().toString()); infoStr += QStringLiteral("Answer received :%1\n").arg(info->answerWasReceived() ? QStringLiteral("true") : QStringLiteral("false")); infoStr += QStringLiteral("****************************************\n"); return infoStr; } diff --git a/agents/followupreminderagent/jobs/followupremindershowmessagejob.cpp b/agents/followupreminderagent/jobs/followupremindershowmessagejob.cpp index fe24e9472..5d7b2a5f0 100644 --- a/agents/followupreminderagent/jobs/followupremindershowmessagejob.cpp +++ b/agents/followupreminderagent/jobs/followupremindershowmessagejob.cpp @@ -1,57 +1,57 @@ /* Copyright (C) 2014-2019 Montel Laurent 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "followupremindershowmessagejob.h" #include "followupreminderagent_debug.h" #include #include #include -#include +#include FollowUpReminderShowMessageJob::FollowUpReminderShowMessageJob(Akonadi::Item::Id id, QObject *parent) : QObject(parent) , mId(id) { } FollowUpReminderShowMessageJob::~FollowUpReminderShowMessageJob() { } void FollowUpReminderShowMessageJob::start() { if (mId < 0) { qCDebug(FOLLOWUPREMINDERAGENT_LOG) << " value < 0"; deleteLater(); return; } const QString kmailInterface = QStringLiteral("org.kde.kmail"); if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(kmailInterface)) { // Program is not already running, so start it QString errmsg; if (KToolInvocation::startServiceByDesktopName(QStringLiteral("org.kde.kmail2"), QString(), &errmsg)) { qCDebug(FOLLOWUPREMINDERAGENT_LOG) << " Can not start kmail" << errmsg; deleteLater(); return; } } QDBusInterface kmail(kmailInterface, QStringLiteral("/KMail"), QStringLiteral("org.kde.kmail.kmail")); kmail.call(QStringLiteral("showMail"), mId); deleteLater(); } diff --git a/agents/mailfilteragent/filterlogdialog.cpp b/agents/mailfilteragent/filterlogdialog.cpp index c90299f87..c670dbe66 100644 --- a/agents/mailfilteragent/filterlogdialog.cpp +++ b/agents/mailfilteragent/filterlogdialog.cpp @@ -1,396 +1,396 @@ /* Copyright (c) 2003 Andreas Gungl Copyright (C) 2012-2019 Laurent Montel 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 In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "filterlogdialog.h" #include #include "kpimtextedit/plaintexteditorwidget.h" #include "kpimtextedit/plaintexteditor.h" #include "mailfilterpurposemenuwidget.h" #include "mailfilteragent_debug.h" #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace MailCommon; FilterLogDialog::FilterLogDialog(QWidget *parent) : QDialog(parent) , mIsInitialized(false) { setWindowTitle(i18n("Filter Log Viewer")); QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, this); mUser1Button = new QPushButton(this); buttonBox->addButton(mUser1Button, QDialogButtonBox::ActionRole); mUser2Button = new QPushButton(this); buttonBox->addButton(mUser2Button, QDialogButtonBox::ActionRole); connect(buttonBox, &QDialogButtonBox::rejected, this, &FilterLogDialog::reject); setWindowIcon(QIcon::fromTheme(QStringLiteral("view-filter"))); setModal(false); buttonBox->button(QDialogButtonBox::Close)->setDefault(true); KGuiItem::assign(mUser1Button, KStandardGuiItem::clear()); KGuiItem::assign(mUser2Button, KStandardGuiItem::saveAs()); QFrame *page = new QFrame(this); QVBoxLayout *pageVBoxLayout = new QVBoxLayout; page->setLayout(pageVBoxLayout); pageVBoxLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addWidget(page); mTextEdit = new KPIMTextEdit::PlainTextEditorWidget(new FilterLogTextEdit(this), page); pageVBoxLayout->addWidget(mTextEdit); mTextEdit->setReadOnly(true); mTextEdit->editor()->setWordWrapMode(QTextOption::NoWrap); const QStringList logEntries = FilterLog::instance()->logEntries(); QStringList::ConstIterator end(logEntries.constEnd()); for (QStringList::ConstIterator it = logEntries.constBegin(); it != end; ++it) { mTextEdit->editor()->appendHtml(*it); } MailfilterPurposeMenuWidget *purposeMenu = new MailfilterPurposeMenuWidget(this, this); QPushButton *mShareButton = new QPushButton(i18n("Share..."), this); mShareButton->setMenu(purposeMenu->menu()); mShareButton->setIcon(QIcon::fromTheme(QStringLiteral("document-share"))); purposeMenu->setEditorWidget(mTextEdit->editor()); buttonBox->addButton(mShareButton, QDialogButtonBox::ActionRole); mLogActiveBox = new QCheckBox(i18n("&Log filter activities"), page); pageVBoxLayout->addWidget(mLogActiveBox); mLogActiveBox->setChecked(FilterLog::instance()->isLogging()); connect(mLogActiveBox, &QCheckBox::clicked, this, &FilterLogDialog::slotSwitchLogState); mLogActiveBox->setWhatsThis( i18n("You can turn logging of filter activities on and off here. " "Of course, log data is collected and shown only when logging " "is turned on. ")); mLogDetailsBox = new QGroupBox(i18n("Logging Details"), page); pageVBoxLayout->addWidget(mLogDetailsBox); QVBoxLayout *layout = new QVBoxLayout; mLogDetailsBox->setLayout(layout); mLogDetailsBox->setEnabled(mLogActiveBox->isChecked()); connect(mLogActiveBox, &QCheckBox::toggled, mLogDetailsBox, &QGroupBox::setEnabled); mLogPatternDescBox = new QCheckBox(i18n("Log pattern description")); layout->addWidget(mLogPatternDescBox); mLogPatternDescBox->setChecked( FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternDescription)); connect(mLogPatternDescBox, &QCheckBox::clicked, this, &FilterLogDialog::slotChangeLogDetail); // TODO //QWhatsThis::add( mLogPatternDescBox, // i18n( "" ) ); mLogRuleEvaluationBox = new QCheckBox(i18n("Log filter &rule evaluation")); layout->addWidget(mLogRuleEvaluationBox); mLogRuleEvaluationBox->setChecked( FilterLog::instance()->isContentTypeEnabled(FilterLog::RuleResult)); connect(mLogRuleEvaluationBox, &QCheckBox::clicked, this, &FilterLogDialog::slotChangeLogDetail); mLogRuleEvaluationBox->setWhatsThis( i18n("You can control the feedback in the log concerning the " "evaluation of the filter rules of applied filters: " "having this option checked will give detailed feedback " "for each single filter rule; alternatively, only " "feedback about the result of the evaluation of all rules " "of a single filter will be given.")); mLogPatternResultBox = new QCheckBox(i18n("Log filter pattern evaluation")); layout->addWidget(mLogPatternResultBox); mLogPatternResultBox->setChecked( FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternResult)); connect(mLogPatternResultBox, &QCheckBox::clicked, this, &FilterLogDialog::slotChangeLogDetail); // TODO //QWhatsThis::add( mLogPatternResultBox, // i18n( "" ) ); mLogFilterActionBox = new QCheckBox(i18n("Log filter actions")); layout->addWidget(mLogFilterActionBox); mLogFilterActionBox->setChecked( FilterLog::instance()->isContentTypeEnabled(FilterLog::AppliedAction)); connect(mLogFilterActionBox, &QCheckBox::clicked, this, &FilterLogDialog::slotChangeLogDetail); // TODO //QWhatsThis::add( mLogFilterActionBox, // i18n( "" ) ); QWidget *hbox = new QWidget(page); QHBoxLayout *hboxHBoxLayout = new QHBoxLayout; hbox->setLayout(hboxHBoxLayout); hboxHBoxLayout->setContentsMargins(0, 0, 0, 0); pageVBoxLayout->addWidget(hbox); QLabel *logSizeLab = new QLabel(i18n("Log size limit:"), hbox); hboxHBoxLayout->addWidget(logSizeLab); mLogMemLimitSpin = new QSpinBox(hbox); hboxHBoxLayout->addWidget(mLogMemLimitSpin); mLogMemLimitSpin->setMinimum(1); mLogMemLimitSpin->setMaximum(1024 * 256); // 256 MB // value in the QSpinBox is in KB while it's in Byte in the FilterLog mLogMemLimitSpin->setValue(FilterLog::instance()->maxLogSize() / 1024); mLogMemLimitSpin->setSuffix(i18n(" KB")); mLogMemLimitSpin->setSpecialValueText( i18nc("@label:spinbox Set the size of the logfile to unlimited.", "unlimited")); connect(mLogMemLimitSpin, qOverload(&QSpinBox::valueChanged), this, &FilterLogDialog::slotChangeLogMemLimit); mLogMemLimitSpin->setWhatsThis( i18n("Collecting log data uses memory to temporarily store the " "log data; here you can limit the maximum amount of memory " "to be used: if the size of the collected log data exceeds " "this limit then the oldest data will be discarded until " "the limit is no longer exceeded. ")); connect(FilterLog::instance(), &FilterLog::logEntryAdded, this, &FilterLogDialog::slotLogEntryAdded); connect(FilterLog::instance(), &FilterLog::logShrinked, this, &FilterLogDialog::slotLogShrinked); connect(FilterLog::instance(), &FilterLog::logStateChanged, this, &FilterLogDialog::slotLogStateChanged); mainLayout->addWidget(buttonBox); connect(mUser1Button, &QPushButton::clicked, this, &FilterLogDialog::slotUser1); connect(mUser2Button, &QPushButton::clicked, this, &FilterLogDialog::slotUser2); connect(mTextEdit->editor(), &KPIMTextEdit::PlainTextEditor::textChanged, this, &FilterLogDialog::slotTextChanged); slotTextChanged(); readConfig(); mIsInitialized = true; } void FilterLogDialog::slotTextChanged() { const bool hasText = !mTextEdit->isEmpty(); mUser2Button->setEnabled(hasText); mUser1Button->setEnabled(hasText); } void FilterLogDialog::readConfig() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group(config, "FilterLog"); const bool isEnabled = group.readEntry("Enabled", false); const bool isLogPatternDescription = group.readEntry("LogPatternDescription", false); const bool isLogRuleResult = group.readEntry("LogRuleResult", false); const bool isLogPatternResult = group.readEntry("LogPatternResult", false); const bool isLogAppliedAction = group.readEntry("LogAppliedAction", false); const int maxLogSize = group.readEntry("maxLogSize", -1); if (isEnabled != FilterLog::instance()->isLogging()) { FilterLog::instance()->setLogging(isEnabled); } if (isLogPatternDescription != FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternDescription)) { FilterLog::instance()->setContentTypeEnabled(FilterLog::PatternDescription, isLogPatternDescription); } if (isLogRuleResult != FilterLog::instance()->isContentTypeEnabled(FilterLog::RuleResult)) { FilterLog::instance()->setContentTypeEnabled(FilterLog::RuleResult, isLogRuleResult); } if (isLogPatternResult != FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternResult)) { FilterLog::instance()->setContentTypeEnabled(FilterLog::PatternResult, isLogPatternResult); } if (isLogAppliedAction != FilterLog::instance()->isContentTypeEnabled(FilterLog::AppliedAction)) { FilterLog::instance()->setContentTypeEnabled(FilterLog::AppliedAction, isLogAppliedAction); } if (FilterLog::instance()->maxLogSize() != maxLogSize) { FilterLog::instance()->setMaxLogSize(maxLogSize); } KConfigGroup geometryGroup(config, "Geometry"); const QSize size = geometryGroup.readEntry("filterLogSize", QSize(600, 400)); if (size.isValid()) { resize(size); } else { adjustSize(); } } FilterLogDialog::~FilterLogDialog() { disconnect(mTextEdit->editor(), &KPIMTextEdit::PlainTextEditor::textChanged, this, &FilterLogDialog::slotTextChanged); KConfigGroup myGroup(KSharedConfig::openConfig(), "Geometry"); myGroup.writeEntry("filterLogSize", size()); myGroup.sync(); } void FilterLogDialog::writeConfig() { if (!mIsInitialized) { return; } KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group(config, "FilterLog"); group.writeEntry("Enabled", FilterLog::instance()->isLogging()); group.writeEntry("LogPatternDescription", FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternDescription)); group.writeEntry("LogRuleResult", FilterLog::instance()->isContentTypeEnabled(FilterLog::RuleResult)); group.writeEntry("LogPatternResult", FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternResult)); group.writeEntry("LogAppliedAction", FilterLog::instance()->isContentTypeEnabled(FilterLog::AppliedAction)); group.writeEntry("maxLogSize", static_cast(FilterLog::instance()->maxLogSize())); group.sync(); } void FilterLogDialog::slotLogEntryAdded(const QString &logEntry) { mTextEdit->editor()->appendHtml(logEntry); } void FilterLogDialog::slotLogShrinked() { // limit the size of the shown log lines as soon as // the log has reached it's memory limit if (mTextEdit->editor()->document()->maximumBlockCount() <= 0) { mTextEdit->editor()->document()->setMaximumBlockCount(mTextEdit->editor()->document()->blockCount()); } } void FilterLogDialog::slotLogStateChanged() { mLogActiveBox->setChecked(FilterLog::instance()->isLogging()); mLogPatternDescBox->setChecked( FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternDescription)); mLogRuleEvaluationBox->setChecked( FilterLog::instance()->isContentTypeEnabled(FilterLog::RuleResult)); mLogPatternResultBox->setChecked( FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternResult)); mLogFilterActionBox->setChecked( FilterLog::instance()->isContentTypeEnabled(FilterLog::AppliedAction)); // value in the QSpinBox is in KB while it's in Byte in the FilterLog int newLogSize = FilterLog::instance()->maxLogSize() / 1024; if (mLogMemLimitSpin->value() != newLogSize) { if (newLogSize <= 0) { mLogMemLimitSpin->setValue(1); } else { mLogMemLimitSpin->setValue(newLogSize); } } writeConfig(); } void FilterLogDialog::slotChangeLogDetail() { if (mLogPatternDescBox->isChecked() != FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternDescription)) { FilterLog::instance()->setContentTypeEnabled(FilterLog::PatternDescription, mLogPatternDescBox->isChecked()); } if (mLogRuleEvaluationBox->isChecked() != FilterLog::instance()->isContentTypeEnabled(FilterLog::RuleResult)) { FilterLog::instance()->setContentTypeEnabled(FilterLog::RuleResult, mLogRuleEvaluationBox->isChecked()); } if (mLogPatternResultBox->isChecked() != FilterLog::instance()->isContentTypeEnabled(FilterLog::PatternResult)) { FilterLog::instance()->setContentTypeEnabled(FilterLog::PatternResult, mLogPatternResultBox->isChecked()); } if (mLogFilterActionBox->isChecked() != FilterLog::instance()->isContentTypeEnabled(FilterLog::AppliedAction)) { FilterLog::instance()->setContentTypeEnabled(FilterLog::AppliedAction, mLogFilterActionBox->isChecked()); } } void FilterLogDialog::slotSwitchLogState() { FilterLog::instance()->setLogging(mLogActiveBox->isChecked()); } void FilterLogDialog::slotChangeLogMemLimit(int value) { mTextEdit->editor()->document()->setMaximumBlockCount(0); //Reset value if (value == 1) { //unilimited FilterLog::instance()->setMaxLogSize(-1); } else { FilterLog::instance()->setMaxLogSize(value * 1024); } } void FilterLogDialog::slotUser1() { FilterLog::instance()->clear(); mTextEdit->editor()->clear(); } void FilterLogDialog::slotUser2() { QPointer fdlg(new QFileDialog(this)); fdlg->setAcceptMode(QFileDialog::AcceptSave); fdlg->setFileMode(QFileDialog::AnyFile); fdlg->selectFile(QStringLiteral("kmail-filter.html")); if (fdlg->exec() == QDialog::Accepted) { const QStringList fileName = fdlg->selectedFiles(); if (!fileName.isEmpty() && !FilterLog::instance()->saveToFile(fileName.at(0))) { KMessageBox::error(this, i18n("Could not write the file %1:\n" "\"%2\" is the detailed error description.", fileName.at(0), QString::fromLocal8Bit(strerror(errno))), i18n("KMail Error")); } } delete fdlg; } FilterLogTextEdit::FilterLogTextEdit(QWidget *parent) : KPIMTextEdit::PlainTextEditor(parent) { } void FilterLogTextEdit::addExtraMenuEntry(QMenu *menu, QPoint pos) { Q_UNUSED(pos); if (!document()->isEmpty()) { QAction *sep = new QAction(menu); sep->setSeparator(true); menu->addAction(sep); QAction *clearAllAction = KStandardAction::clear(this, &FilterLogTextEdit::clear, menu); menu->addAction(clearAllAction); } } diff --git a/agents/mailfilteragent/filtermanager.cpp b/agents/mailfilteragent/filtermanager.cpp index e849b72ac..389bf7c7e 100644 --- a/agents/mailfilteragent/filtermanager.cpp +++ b/agents/mailfilteragent/filtermanager.cpp @@ -1,663 +1,663 @@ // /* Copyright: before 2012: missing, see KMail copyrights * Copyright (C) 2012 Andras Mantia * * * 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 "filtermanager.h" #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include +#include +#include #include "mailfilteragent_debug.h" #include #include #include #include #include #include #include #include // other headers #include #include #include #include using namespace MailCommon; class FilterManager::Private { public: Private(FilterManager *qq) : q(qq) , mRequiredPartsBasedOnAll(SearchRule::Envelope) , mTotalProgressCount(0) , mCurrentProgressCount(0) , mInboundFiltersExist(false) { } void itemsFetchJobForFilterDone(KJob *job); void itemFetchJobForFilterDone(KJob *job); void moveJobResult(KJob *); void modifyJobResult(KJob *); void deleteJobResult(KJob *); void slotItemsFetchedForFilter(const Akonadi::Item::List &items); void showNotification(const QString &errorMsg, const QString &jobErrorString); bool isMatching(const Akonadi::Item &item, const MailCommon::MailFilter *filter); void beginFiltering(const Akonadi::Item &item) const; void endFiltering(const Akonadi::Item &item) const; bool atLeastOneFilterAppliesTo(const QString &accountId) const; bool atLeastOneIncomingFilterAppliesTo(const QString &accountId) const; FilterManager *q; QVector mFilters; QMap mRequiredParts; SearchRule::RequiredPart mRequiredPartsBasedOnAll; int mTotalProgressCount = 0; int mCurrentProgressCount = 0; bool mInboundFiltersExist = false; bool mAllFoldersFiltersExist = false; }; void FilterManager::Private::slotItemsFetchedForFilter(const Akonadi::Item::List &items) { FilterManager::FilterSet filterSet = FilterManager::Inbound; if (q->sender()->property("filterSet").isValid()) { filterSet = static_cast(q->sender()->property("filterSet").toInt()); } QVector listMailFilters; if (q->sender()->property("listFilters").isValid()) { const QStringList listFilters = q->sender()->property("listFilters").toStringList(); //TODO improve it for (const QString &filterId : listFilters) { for (MailCommon::MailFilter *filter : qAsConst(mFilters)) { if (filter->identifier() == filterId) { listMailFilters << filter; break; } } } } if (listMailFilters.isEmpty()) { listMailFilters = mFilters; } bool needsFullPayload = q->sender()->property("needsFullPayload").toBool(); for (const Akonadi::Item &item : qAsConst(items)) { ++mCurrentProgressCount; if ((mTotalProgressCount > 0) && (mCurrentProgressCount != mTotalProgressCount)) { const QString statusMsg = i18n("Filtering message %1 of %2", mCurrentProgressCount, mTotalProgressCount); Q_EMIT q->progressMessage(statusMsg); Q_EMIT q->percent(mCurrentProgressCount * 100 / mTotalProgressCount); } else { Q_EMIT q->percent(0); } const bool filterResult = q->process(listMailFilters, item, needsFullPayload, filterSet); if (mCurrentProgressCount == mTotalProgressCount) { mTotalProgressCount = 0; mCurrentProgressCount = 0; } if (!filterResult) { Q_EMIT q->filteringFailed(item); // something went horribly wrong (out of space?) //CommonKernel->emergencyExit( i18n( "Unable to process messages: " ) + QString::fromLocal8Bit( strerror( errno ) ) ); } } } void FilterManager::Private::itemsFetchJobForFilterDone(KJob *job) { if (job->error()) { qCCritical(MAILFILTERAGENT_LOG) << "Error while fetching items. " << job->error() << job->errorString(); } } void FilterManager::Private::itemFetchJobForFilterDone(KJob *job) { if (job->error()) { qCCritical(MAILFILTERAGENT_LOG) << "Error while fetching item. " << job->error() << job->errorString(); return; } const Akonadi::ItemFetchJob *fetchJob = qobject_cast(job); const Akonadi::Item::List items = fetchJob->items(); if (items.isEmpty()) { qCCritical(MAILFILTERAGENT_LOG) << "Error while fetching item: item not found"; return; } const QString resourceId = fetchJob->property("resourceId").toString(); bool needsFullPayload = q->requiredPart(resourceId) != SearchRule::Envelope; if (job->property("filterId").isValid()) { const QString filterId = job->property("filterId").toString(); // find correct filter object MailCommon::MailFilter *wantedFilter = nullptr; for (MailCommon::MailFilter *filter : qAsConst(mFilters)) { if (filter->identifier() == filterId) { wantedFilter = filter; break; } } if (!wantedFilter) { qCCritical(MAILFILTERAGENT_LOG) << "Cannot find filter object with id" << filterId; return; } if (!q->process(items.first(), needsFullPayload, wantedFilter)) { Q_EMIT q->filteringFailed(items.first()); } } else { const FilterManager::FilterSet set = static_cast(job->property("filterSet").toInt()); if (!q->process(items.first(), needsFullPayload, set, !resourceId.isEmpty(), resourceId)) { Q_EMIT q->filteringFailed(items.first()); } } } void FilterManager::Private::moveJobResult(KJob *job) { if (job->error()) { const Akonadi::ItemMoveJob *movejob = qobject_cast(job); if (movejob) { qCCritical(MAILFILTERAGENT_LOG) << "Error while moving items. " << job->error() << job->errorString() << " to destinationCollection.id() :" << movejob->destinationCollection().id(); } else { qCCritical(MAILFILTERAGENT_LOG) << "Error while moving items. " << job->error() << job->errorString(); } //Laurent: not real info and when we have 200 errors it's very long to click all the time on ok. showNotification(i18n("Error applying mail filter move"), job->errorString()); } } void FilterManager::Private::deleteJobResult(KJob *job) { if (job->error()) { qCCritical(MAILFILTERAGENT_LOG) << "Error while delete items. " << job->error() << job->errorString(); showNotification(i18n("Error applying mail filter delete"), job->errorString()); } } void FilterManager::Private::modifyJobResult(KJob *job) { if (job->error()) { qCCritical(MAILFILTERAGENT_LOG) << "Error while modifying items. " << job->error() << job->errorString(); showNotification(i18n("Error applying mail filter modifications"), job->errorString()); } } void FilterManager::Private::showNotification(const QString &errorMsg, const QString &jobErrorString) { KNotification *notify = new KNotification(QStringLiteral("mailfilterjoberror")); notify->setComponentName(QStringLiteral("akonadi_mailfilter_agent")); notify->setIconName(QStringLiteral("view-filter")); notify->setText(errorMsg + QLatin1Char('\n') + jobErrorString); notify->sendEvent(); } bool FilterManager::Private::isMatching(const Akonadi::Item &item, const MailCommon::MailFilter *filter) { bool result = false; if (FilterLog::instance()->isLogging()) { QString logText(i18n("Evaluating filter rules: ")); logText.append(filter->pattern()->asString()); FilterLog::instance()->add(logText, FilterLog::PatternDescription); } if (filter->pattern()->matches(item)) { if (FilterLog::instance()->isLogging()) { FilterLog::instance()->add(i18n("Filter rules have matched."), FilterLog::PatternResult); } result = true; } return result; } void FilterManager::Private::beginFiltering(const Akonadi::Item &item) const { if (FilterLog::instance()->isLogging()) { FilterLog::instance()->addSeparator(); if (item.hasPayload()) { KMime::Message::Ptr msg = item.payload(); const QString subject = msg->subject()->asUnicodeString(); const QString from = msg->from()->asUnicodeString(); const QDateTime dateTime = msg->date()->dateTime(); const QString date = QLocale().toString(dateTime, QLocale::LongFormat); const QString logText(i18n("Begin filtering on message \"%1\" from \"%2\" at \"%3\" :", subject, from, date)); FilterLog::instance()->add(logText, FilterLog::PatternDescription); } } } void FilterManager::Private::endFiltering(const Akonadi::Item & /*item*/) const { } bool FilterManager::Private::atLeastOneFilterAppliesTo(const QString &accountId) const { for (const MailCommon::MailFilter *filter : qAsConst(mFilters)) { if (filter->applyOnAccount(accountId)) { return true; } } return false; } bool FilterManager::Private::atLeastOneIncomingFilterAppliesTo(const QString &accountId) const { for (const MailCommon::MailFilter *filter : qAsConst(mFilters)) { if (filter->applyOnInbound() && filter->applyOnAccount(accountId)) { return true; } } return false; } FilterManager::FilterManager(QObject *parent) : QObject(parent) , d(new Private(this)) { readConfig(); } FilterManager::~FilterManager() { clear(); delete d; } void FilterManager::clear() { qDeleteAll(d->mFilters); d->mFilters.clear(); } void FilterManager::readConfig() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); // use akonadi_mailfilter_agentrc config->reparseConfiguration(); clear(); QStringList emptyFilters; d->mFilters = FilterImporterExporter::readFiltersFromConfig(config, emptyFilters); d->mRequiredParts.clear(); d->mRequiredPartsBasedOnAll = SearchRule::Envelope; if (!d->mFilters.isEmpty()) { const Akonadi::AgentInstance::List agents = Akonadi::AgentManager::self()->instances(); for (const Akonadi::AgentInstance &agent : agents) { const QString id = agent.identifier(); auto it = std::max_element(d->mFilters.constBegin(), d->mFilters.constEnd(), [id](MailCommon::MailFilter *lhs, MailCommon::MailFilter *rhs) { return lhs->requiredPart(id) < rhs->requiredPart(id); }); d->mRequiredParts[id] = (*it)->requiredPart(id); d->mRequiredPartsBasedOnAll = qMax(d->mRequiredPartsBasedOnAll, d->mRequiredParts[id]); } } // check if at least one filter is to be applied on inbound mail for (auto i = d->mFilters.cbegin(), e = d->mFilters.cend(); i != e && (!d->mInboundFiltersExist || !d->mAllFoldersFiltersExist); ++i) { if ((*i)->applyOnInbound()) { d->mInboundFiltersExist = true; } if ((*i)->applyOnAllFoldersInbound()) { d->mAllFoldersFiltersExist = true; } } Q_EMIT filterListUpdated(); } void FilterManager::mailCollectionRemoved(const Akonadi::Collection &collection) { QVector::const_iterator end(d->mFilters.constEnd()); for (QVector::const_iterator it = d->mFilters.constBegin(); it != end; ++it) { (*it)->folderRemoved(collection, Akonadi::Collection()); } } void FilterManager::agentRemoved(const QString &identifier) { QVector::const_iterator end(d->mFilters.constEnd()); for (QVector::const_iterator it = d->mFilters.constBegin(); it != end; ++it) { (*it)->agentRemoved(identifier); } } void FilterManager::filter(const Akonadi::Item &item, FilterManager::FilterSet set, const QString &resourceId) { Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item, this); job->setProperty("filterSet", static_cast(set)); job->setProperty("resourceId", resourceId); SearchRule::RequiredPart requestedPart = requiredPart(resourceId); if (requestedPart == SearchRule::CompleteMessage) { job->fetchScope().fetchFullPayload(true); } else if (requestedPart == SearchRule::Header) { job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Header, true); } else { job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Envelope, true); } job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); connect(job, &Akonadi::ItemFetchJob::result, this, [this](KJob *job) { d->itemFetchJobForFilterDone(job); }); } void FilterManager::filter(const Akonadi::Item &item, const QString &filterId, const QString &resourceId) { Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item, this); job->setProperty("filterId", filterId); SearchRule::RequiredPart requestedPart = requiredPart(resourceId); if (requestedPart == SearchRule::CompleteMessage) { job->fetchScope().fetchFullPayload(true); } else if (requestedPart == SearchRule::Header) { job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Header, true); } else { job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Envelope, true); } job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); connect(job, &Akonadi::ItemFetchJob::result, this, [this](KJob *job) { d->itemFetchJobForFilterDone(job); }); } bool FilterManager::process(const Akonadi::Item &item, bool needsFullPayload, const MailFilter *filter) { if (!filter) { return false; } if (!filter->isEnabled()) { return true; } if (!item.hasPayload()) { qCCritical(MAILFILTERAGENT_LOG) << "Filter is null or item doesn't have correct payload."; return false; } if (d->isMatching(item, filter)) { // do the actual filtering stuff d->beginFiltering(item); ItemContext context(item, needsFullPayload); bool stopIt = false; bool applyOnOutbound = false; if (filter->execActions(context, stopIt, applyOnOutbound) == MailCommon::MailFilter::CriticalError) { return false; } d->endFiltering(item); if (!processContextItem(context)) { return false; } } return true; } bool FilterManager::processContextItem(ItemContext context) { const KMime::Message::Ptr msg = context.item().payload(); msg->assemble(); auto col = Akonadi::EntityTreeModel::updatedCollection(MailCommon::Kernel::self()->kernelIf()->collectionModel(), context.item().parentCollection()); const bool itemCanDelete = (col.rights() & Akonadi::Collection::CanDeleteItem); if (context.deleteItem()) { if (itemCanDelete) { Akonadi::ItemDeleteJob *deleteJob = new Akonadi::ItemDeleteJob(context.item(), this); connect(deleteJob, &Akonadi::ItemDeleteJob::result, this, [this](KJob *job) { d->deleteJobResult(job); }); } else { return false; } } else { if (context.moveTargetCollection().isValid() && context.item().storageCollectionId() != context.moveTargetCollection().id()) { if (itemCanDelete) { Akonadi::ItemMoveJob *moveJob = new Akonadi::ItemMoveJob(context.item(), context.moveTargetCollection(), this); connect(moveJob, &Akonadi::ItemMoveJob::result, this, [this](KJob *job) { d->moveJobResult(job); }); } else { return false; } } if (context.needsPayloadStore() || context.needsFlagStore()) { Akonadi::Item item = context.item(); //the item might be in a new collection with a different remote id, so don't try to force on it //the previous remote id. Example: move to another collection on another resource => new remoteId, but our context.item() //remoteid still holds the old one. Without clearing it, we try to enforce that on the new location, which is //anything but good (and the server replies with "NO Only resources can modify remote identifiers" item.setRemoteId(QString()); Akonadi::ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob(item, this); modifyJob->disableRevisionCheck(); //no conflict handling for mails as no other process could change the mail body and we don't care about flag conflicts //The below is a safety check to ignore modifying payloads if it was not requested, //as in that case we might change the payload to an invalid one modifyJob->setIgnorePayload(!context.needsFullPayload()); connect(modifyJob, &Akonadi::ItemModifyJob::result, this, [this](KJob *job) { d->modifyJobResult(job); }); } } return true; } bool FilterManager::process(const QVector< MailFilter * > &mailFilters, const Akonadi::Item &item, bool needsFullPayload, FilterManager::FilterSet set, bool account, const QString &accountId) { if (set == NoSet) { qCDebug(MAILFILTERAGENT_LOG) << "FilterManager: process() called with not filter set selected"; return false; } if (!item.hasPayload()) { qCCritical(MAILFILTERAGENT_LOG) << "Filter is null or item doesn't have correct payload."; return false; } bool stopIt = false; d->beginFiltering(item); ItemContext context(item, needsFullPayload); QVector::const_iterator end(mailFilters.constEnd()); const bool applyOnOutbound = ((set & Outbound) || (set & BeforeOutbound)); for (QVector::const_iterator it = mailFilters.constBegin(); !stopIt && it != end; ++it) { if ((*it)->isEnabled()) { const bool inboundOk = ((set & Inbound) && (*it)->applyOnInbound()); const bool outboundOk = ((set & Outbound) && (*it)->applyOnOutbound()); const bool beforeOutboundOk = ((set & BeforeOutbound) && (*it)->applyBeforeOutbound()); const bool explicitOk = ((set & Explicit) && (*it)->applyOnExplicit()); const bool allFoldersOk = ((set & AllFolders) && (*it)->applyOnAllFoldersInbound()); const bool accountOk = (!account || (*it)->applyOnAccount(accountId)); if ((inboundOk && accountOk) || (allFoldersOk && accountOk) || outboundOk || beforeOutboundOk || explicitOk) { // filter is applicable if (d->isMatching(context.item(), *it)) { // execute actions: if ((*it)->execActions(context, stopIt, applyOnOutbound) == MailCommon::MailFilter::CriticalError) { return false; } } } } } d->endFiltering(item); if (!processContextItem(context)) { return false; } return true; } bool FilterManager::process(const Akonadi::Item &item, bool needsFullPayload, FilterSet set, bool account, const QString &accountId) { return process(d->mFilters, item, needsFullPayload, set, account, accountId); } QString FilterManager::createUniqueName(const QString &name) const { QString uniqueName = name; int counter = 0; bool found = true; while (found) { found = false; for (const MailCommon::MailFilter *filter : qAsConst(d->mFilters)) { if (!filter->name().compare(uniqueName)) { found = true; ++counter; uniqueName = name; uniqueName += QLatin1String(" (") + QString::number(counter) + QLatin1String(")"); break; } } } return uniqueName; } MailCommon::SearchRule::RequiredPart FilterManager::requiredPart(const QString &id) const { if (id.isEmpty()) { return d->mRequiredPartsBasedOnAll; } return d->mRequiredParts.contains(id) ? d->mRequiredParts[id] : SearchRule::Envelope; } void FilterManager::dump() const { for (const MailCommon::MailFilter *filter : qAsConst(d->mFilters)) { qCDebug(MAILFILTERAGENT_LOG) << filter->asString(); } } void FilterManager::applySpecificFilters(const Akonadi::Item::List &selectedMessages, SearchRule::RequiredPart requiredPart, const QStringList &listFilters, FilterSet filterSet) { Q_EMIT progressMessage(i18n("Filtering messages")); d->mTotalProgressCount = selectedMessages.size(); d->mCurrentProgressCount = 0; Akonadi::ItemFetchJob *itemFetchJob = new Akonadi::ItemFetchJob(selectedMessages, this); if (requiredPart == SearchRule::CompleteMessage) { itemFetchJob->fetchScope().fetchFullPayload(true); } else if (requiredPart == SearchRule::Header) { itemFetchJob->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Header, true); } else { itemFetchJob->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Envelope, true); } itemFetchJob->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); itemFetchJob->setProperty("listFilters", QVariant::fromValue(listFilters)); itemFetchJob->setProperty("filterSet", QVariant::fromValue(static_cast(filterSet))); itemFetchJob->setProperty("needsFullPayload", requiredPart != SearchRule::Envelope); connect(itemFetchJob, &Akonadi::ItemFetchJob::itemsReceived, this, [this](const Akonadi::Item::List &lst) { d->slotItemsFetchedForFilter(lst); }); connect(itemFetchJob, &Akonadi::ItemFetchJob::result, this, [this](KJob *job) { d->itemsFetchJobForFilterDone(job); }); } void FilterManager::applyFilters(const Akonadi::Item::List &selectedMessages, FilterSet filterSet) { Q_EMIT progressMessage(i18n("Filtering messages")); d->mTotalProgressCount = selectedMessages.size(); d->mCurrentProgressCount = 0; Akonadi::ItemFetchJob *itemFetchJob = new Akonadi::ItemFetchJob(selectedMessages, this); SearchRule::RequiredPart requiredParts = requiredPart(QString()); if (requiredParts == SearchRule::CompleteMessage) { itemFetchJob->fetchScope().fetchFullPayload(true); } else if (requiredParts == SearchRule::Header) { itemFetchJob->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Header, true); } else { itemFetchJob->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Envelope, true); } itemFetchJob->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); itemFetchJob->setProperty("filterSet", QVariant::fromValue(static_cast(filterSet))); itemFetchJob->setProperty("needsFullPayload", requiredParts != SearchRule::Envelope); connect(itemFetchJob, &Akonadi::ItemFetchJob::itemsReceived, this, [this](const Akonadi::Item::List &lst) { d->slotItemsFetchedForFilter(lst); }); connect(itemFetchJob, &Akonadi::ItemFetchJob::result, this, [this](KJob *job) { d->itemsFetchJobForFilterDone(job); }); } bool FilterManager::hasAllFoldersFilter() const { return d->mAllFoldersFiltersExist; } #include "moc_filtermanager.cpp" diff --git a/agents/mailfilteragent/mailfilteragent.cpp b/agents/mailfilteragent/mailfilteragent.cpp index e4cd81b4a..b060175b8 100644 --- a/agents/mailfilteragent/mailfilteragent.cpp +++ b/agents/mailfilteragent/mailfilteragent.cpp @@ -1,438 +1,438 @@ /* Copyright (c) 2011 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mailfilteragent.h" #include "mailcommon/dbusoperators.h" #include "dummykernel.h" #include "filterlogdialog.h" #include "filtermanager.h" #include "mailfilteragentadaptor.h" #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include "mailfilteragent_debug.h" #include #include #include #include #include #include #include #include #include -#include +#include bool MailFilterAgent::isFilterableCollection(const Akonadi::Collection &collection) const { if (!collection.contentMimeTypes().contains(KMime::Message::mimeType())) { return false; } return m_filterManager->hasAllFoldersFilter() || MailCommon::Kernel::folderIsInbox(collection); //TODO: check got filter attribute here } MailFilterAgent::MailFilterAgent(const QString &id) : Akonadi::AgentBase(id) , m_filterLogDialog(nullptr) { Kdelibs4ConfigMigrator migrate(QStringLiteral("mailfilteragent")); migrate.setConfigFiles(QStringList() << QStringLiteral("akonadi_mailfilter_agentrc") << QStringLiteral("akonadi_mailfilter_agent.notifyrc")); migrate.migrate(); Akonadi::AttributeFactory::registerAttribute(); mMailFilterKernel = new DummyKernel(this); CommonKernel->registerKernelIf(mMailFilterKernel); //register KernelIf early, it is used by the Filter classes CommonKernel->registerSettingsIf(mMailFilterKernel); //SettingsIf is used in FolderTreeWidget m_filterManager = new FilterManager(this); connect(m_filterManager, &FilterManager::percent, this, &MailFilterAgent::emitProgress); connect(m_filterManager, &FilterManager::progressMessage, this, &MailFilterAgent::emitProgressMessage); Akonadi::Monitor *collectionMonitor = new Akonadi::Monitor(this); collectionMonitor->setObjectName(QStringLiteral("MailFilterCollectionMonitor")); collectionMonitor->fetchCollection(true); collectionMonitor->ignoreSession(Akonadi::Session::defaultSession()); collectionMonitor->collectionFetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All); collectionMonitor->setMimeTypeMonitored(KMime::Message::mimeType()); connect(collectionMonitor, &Akonadi::Monitor::collectionAdded, this, &MailFilterAgent::mailCollectionAdded); connect(collectionMonitor, qOverload(&Akonadi::Monitor::collectionChanged), this, &MailFilterAgent::mailCollectionChanged); connect(collectionMonitor, &Akonadi::Monitor::collectionRemoved, this, &MailFilterAgent::mailCollectionRemoved); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceRemoved, this, &MailFilterAgent::slotInstanceRemoved); QTimer::singleShot(0, this, &MailFilterAgent::initializeCollections); qDBusRegisterMetaType >(); new MailFilterAgentAdaptor(this); KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/MailFilterAgent"), this, QDBusConnection::ExportAdaptors); const QString service = Akonadi::ServerManager::self()->agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_mailfilter_agent")); KDBusConnectionPool::threadConnection().registerService(service); //Enabled or not filterlogdialog KSharedConfig::Ptr config = KSharedConfig::openConfig(); if (config->hasGroup("FilterLog")) { KConfigGroup group(config, "FilterLog"); if (group.readEntry("Enabled", false)) { KNotification *notify = new KNotification(QStringLiteral("mailfilterlogenabled")); notify->setComponentName(QApplication::applicationDisplayName()); notify->setIconName(QStringLiteral("view-filter")); notify->setText(i18nc("Notification when the filter log was enabled", "Mail Filter Log Enabled")); notify->sendEvent(); } } changeRecorder()->itemFetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); changeRecorder()->itemFetchScope().setCacheOnly(true); changeRecorder()->fetchCollection(true); changeRecorder()->setChangeRecordingEnabled(false); mProgressCounter = 0; mProgressTimer = new QTimer(this); connect(mProgressTimer, &QTimer::timeout, this, [this]() { emitProgress(); }); itemMonitor = new Akonadi::Monitor(this); itemMonitor->setObjectName(QStringLiteral("MailFilterItemMonitor")); itemMonitor->itemFetchScope().setFetchRemoteIdentification(true); itemMonitor->itemFetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); connect(itemMonitor, &Akonadi::Monitor::itemChanged, this, &MailFilterAgent::slotItemChanged); } MailFilterAgent::~MailFilterAgent() { delete m_filterLogDialog; } void MailFilterAgent::configure(WId windowId) { Q_UNUSED(windowId); } void MailFilterAgent::initializeCollections() { m_filterManager->readConfig(); Akonadi::CollectionFetchJob *job = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, this); job->fetchScope().setContentMimeTypes({ KMime::Message::mimeType() }); connect(job, &Akonadi::CollectionFetchJob::result, this, &MailFilterAgent::initialCollectionFetchingDone); } void MailFilterAgent::initialCollectionFetchingDone(KJob *job) { if (job->error()) { qCWarning(MAILFILTERAGENT_LOG) << job->errorString(); return; //TODO: proper error handling } const auto fetchJob = qobject_cast(job); const auto pop3ResourceMap = MailCommon::Kernel::pop3ResourceTargetCollection(); const auto lstCols = fetchJob->collections(); for (const Akonadi::Collection &collection : lstCols) { if (isFilterableCollection(collection)) { changeRecorder()->setCollectionMonitored(collection, true); } else { for (auto pop3ColId : pop3ResourceMap) { if (collection.id() == pop3ColId) { changeRecorder()->setCollectionMonitored(collection, true); break; } } } } Q_EMIT status(AgentBase::Idle, i18n("Ready")); Q_EMIT percent(100); QTimer::singleShot(2000, this, &MailFilterAgent::clearMessage); } void MailFilterAgent::clearMessage() { Q_EMIT status(AgentBase::Idle, QString()); } void MailFilterAgent::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) { /* The monitor mimetype filter would override the collection filter, therefor we have to check * for the mimetype of the item here. */ if (item.mimeType() != KMime::Message::mimeType()) { qCDebug(MAILFILTERAGENT_LOG) << "MailFilterAgent::itemAdded called for a non-message item!"; return; } if (item.remoteId().isEmpty()) { itemMonitor->setItemMonitored(item); } else { filterItem(item, collection); } } void MailFilterAgent::slotItemChanged(const Akonadi::Item &item) { if (item.remoteId().isEmpty()) { return; } // now we have the remoteId itemMonitor->setItemMonitored(item, false); filterItem(item, item.parentCollection()); } void MailFilterAgent::filterItem(const Akonadi::Item &item, const Akonadi::Collection &collection) { MailCommon::SearchRule::RequiredPart requiredPart = m_filterManager->requiredPart(collection.resource()); Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item); connect(job, &Akonadi::ItemFetchJob::itemsReceived, this, &MailFilterAgent::itemsReceiviedForFiltering); if (requiredPart == MailCommon::SearchRule::CompleteMessage) { job->fetchScope().fetchFullPayload(); } else if (requiredPart == MailCommon::SearchRule::Header) { job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Header, true); } else { job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Envelope, true); } job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); job->fetchScope().fetchAttribute(); job->setProperty("resource", collection.resource()); //TODO: Error handling? } void MailFilterAgent::itemsReceiviedForFiltering(const Akonadi::Item::List &items) { if (items.isEmpty()) { qCDebug(MAILFILTERAGENT_LOG) << "MailFilterAgent::itemsReceiviedForFiltering items is empty!"; return; } Akonadi::Item item = items.first(); /* * happens when item no longer exists etc, and queue compression didn't happen yet */ if (!item.hasPayload()) { qCDebug(MAILFILTERAGENT_LOG) << "MailFilterAgent::itemsReceiviedForFiltering item has no payload!"; return; } Akonadi::MessageStatus status; status.setStatusFromFlags(item.flags()); if (status.isRead() || status.isSpam() || status.isIgnored()) { return; } QString resource = sender()->property("resource").toString(); const Akonadi::Pop3ResourceAttribute *pop3ResourceAttribute = item.attribute(); if (pop3ResourceAttribute) { resource = pop3ResourceAttribute->pop3AccountName(); } emitProgressMessage(i18n("Filtering in %1", Akonadi::AgentManager::self()->instance(resource).name())); if (!m_filterManager->process(item, m_filterManager->requiredPart(resource), FilterManager::Inbound, true, resource)) { qCWarning(MAILFILTERAGENT_LOG) << "Impossible to process mails"; } emitProgress(++mProgressCounter); mProgressTimer->start(1000); } void MailFilterAgent::mailCollectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &) { if (isFilterableCollection(collection)) { changeRecorder()->setCollectionMonitored(collection, true); } } void MailFilterAgent::mailCollectionChanged(const Akonadi::Collection &collection) { changeRecorder()->setCollectionMonitored(collection, isFilterableCollection(collection)); } void MailFilterAgent::mailCollectionRemoved(const Akonadi::Collection &collection) { changeRecorder()->setCollectionMonitored(collection, false); m_filterManager->mailCollectionRemoved(collection); } QString MailFilterAgent::createUniqueName(const QString &nameTemplate) { return m_filterManager->createUniqueName(nameTemplate); } void MailFilterAgent::filterItems(const QList< qint64 > &itemIds, int filterSet) { Akonadi::Item::List items; items.reserve(itemIds.count()); for (qint64 id : itemIds) { items << Akonadi::Item(id); } m_filterManager->applyFilters(items, static_cast(filterSet)); } void MailFilterAgent::filterCollections(const QList &collections, int filterSet) { for (qint64 id: collections) { auto ifj = new Akonadi::ItemFetchJob{ Akonadi::Collection{ id }, this }; ifj->setDeliveryOption(Akonadi::ItemFetchJob::EmitItemsInBatches); connect(ifj, &Akonadi::ItemFetchJob::itemsReceived, this, [=](const Akonadi::Item::List &items) { m_filterManager->applyFilters(items, static_cast(filterSet)); }); } } void MailFilterAgent::applySpecificFilters(const QList< qint64 > &itemIds, int requiresPart, const QStringList &listFilters) { Akonadi::Item::List items; items.reserve(itemIds.count()); for (qint64 id : itemIds) { items << Akonadi::Item(id); } m_filterManager->applySpecificFilters(items, static_cast(requiresPart), listFilters); } void MailFilterAgent::applySpecificFiltersOnCollections(const QList &colIds, const QStringList &listFilters, int filterSet) { // TODO: Actually calculate this based on the listFilters' requirements const auto requiresParts = MailCommon::SearchRule::CompleteMessage; for (qint64 id : colIds) { auto ifj = new Akonadi::ItemFetchJob{ Akonadi::Collection{ id }, this }; ifj->setDeliveryOption(Akonadi::ItemFetchJob::EmitItemsInBatches); connect(ifj, &Akonadi::ItemFetchJob::itemsReceived, this, [=](const Akonadi::Item::List &items) { m_filterManager->applySpecificFilters(items, requiresParts, listFilters, static_cast(filterSet)); }); } } void MailFilterAgent::filterItem(qint64 item, int filterSet, const QString &resourceId) { m_filterManager->filter(Akonadi::Item(item), static_cast(filterSet), resourceId); } void MailFilterAgent::filter(qint64 item, const QString &filterIdentifier, const QString &resourceId) { m_filterManager->filter(Akonadi::Item(item), filterIdentifier, resourceId); } void MailFilterAgent::reload() { const Akonadi::Collection::List collections = changeRecorder()->collectionsMonitored(); for (const Akonadi::Collection &collection : collections) { changeRecorder()->setCollectionMonitored(collection, false); } initializeCollections(); } void MailFilterAgent::showFilterLogDialog(qlonglong windowId) { if (!m_filterLogDialog) { m_filterLogDialog = new FilterLogDialog(nullptr); m_filterLogDialog->setAttribute(Qt::WA_NativeWindow, true); } KWindowSystem::setMainWindow(m_filterLogDialog->windowHandle(), windowId); m_filterLogDialog->show(); m_filterLogDialog->raise(); m_filterLogDialog->activateWindow(); m_filterLogDialog->setModal(false); } void MailFilterAgent::emitProgress(int p) { if (p == 0) { mProgressTimer->stop(); Q_EMIT status(AgentBase::Idle, QString()); } mProgressCounter = p; Q_EMIT percent(p); } void MailFilterAgent::emitProgressMessage(const QString &message) { Q_EMIT status(AgentBase::Running, message); } QString MailFilterAgent::printCollectionMonitored() const { QString printDebugCollection; const Akonadi::Collection::List collections = changeRecorder()->collectionsMonitored(); if (collections.isEmpty()) { printDebugCollection = QStringLiteral("No collection is monitored!"); } else { for (const Akonadi::Collection &collection : collections) { if (!printDebugCollection.isEmpty()) { printDebugCollection += QLatin1Char('\n'); } printDebugCollection += QStringLiteral("Collection name: %1\n").arg(collection.name()); printDebugCollection += QStringLiteral("Collection id: %1\n").arg(collection.id()); } } return printDebugCollection; } void MailFilterAgent::expunge(qint64 collectionId) { mMailFilterKernel->expunge(collectionId, false); } void MailFilterAgent::slotInstanceRemoved(const Akonadi::AgentInstance &instance) { m_filterManager->agentRemoved(instance.identifier()); } AKONADI_AGENT_MAIN(MailFilterAgent) diff --git a/agents/sendlateragent/sendlateragent.cpp b/agents/sendlateragent/sendlateragent.cpp index 20ad1d29c..608d44d11 100644 --- a/agents/sendlateragent/sendlateragent.cpp +++ b/agents/sendlateragent/sendlateragent.cpp @@ -1,196 +1,196 @@ /* Copyright (C) 2013-2019 Montel Laurent 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sendlateragent.h" #include "sendlatermanager.h" #include "sendlaterconfiguredialog.h" #include "sendlaterinfo.h" #include "sendlaterutil.h" #include "sendlateragentadaptor.h" #include "sendlateragentsettings.h" #include "sendlaterremovemessagejob.h" #include "sendlateragent_debug.h" #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include //#define DEBUG_SENDLATERAGENT 1 SendLaterAgent::SendLaterAgent(const QString &id) : Akonadi::AgentBase(id) , mAgentInitialized(false) { Kdelibs4ConfigMigrator migrate(QStringLiteral("sendlateragent")); migrate.setConfigFiles(QStringList() << QStringLiteral("akonadi_sendlater_agentrc") << QStringLiteral("akonadi_sendlater_agent.notifyrc")); migrate.migrate(); mManager = new SendLaterManager(this); connect(mManager, &SendLaterManager::needUpdateConfigDialogBox, this, &SendLaterAgent::needUpdateConfigDialogBox); new SendLaterAgentAdaptor(this); KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/SendLaterAgent"), this, QDBusConnection::ExportAdaptors); const QString service = Akonadi::ServerManager::self()->agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_sendlater_agent")); KDBusConnectionPool::threadConnection().registerService(service); changeRecorder()->setMimeTypeMonitored(KMime::Message::mimeType()); changeRecorder()->itemFetchScope().setCacheOnly(true); changeRecorder()->itemFetchScope().setFetchModificationTime(false); changeRecorder()->setChangeRecordingEnabled(false); changeRecorder()->ignoreSession(Akonadi::Session::defaultSession()); setNeedsNetwork(true); if (SendLaterAgentSettings::enabled()) { #ifdef DEBUG_SENDLATERAGENT QTimer::singleShot(1000, this, &SendLaterAgent::slotStartAgent); #else QTimer::singleShot(1000 * 60 * 4, this, &SendLaterAgent::slotStartAgent); #endif } // For extra safety, check list every hour, in case we didn't properly get // notified about the network going up or down. QTimer *reloadListTimer = new QTimer(this); connect(reloadListTimer, &QTimer::timeout, this, &SendLaterAgent::reload); reloadListTimer->start(1000 * 60 * 60); //1 hour } SendLaterAgent::~SendLaterAgent() { } void SendLaterAgent::slotStartAgent() { mAgentInitialized = true; if (isOnline()) { mManager->load(); } } void SendLaterAgent::doSetOnline(bool online) { if (mAgentInitialized) { if (online) { reload(); } else { mManager->stopAll(); } } } void SendLaterAgent::reload() { qCDebug(SENDLATERAGENT_LOG) << " void SendLaterAgent::reload()"; if (SendLaterAgentSettings::enabled()) { mManager->load(true); } } void SendLaterAgent::setEnableAgent(bool enabled) { if (SendLaterAgentSettings::enabled() == enabled) { return; } SendLaterAgentSettings::setEnabled(enabled); SendLaterAgentSettings::self()->save(); if (enabled) { mManager->load(); } else { mManager->stopAll(); } } bool SendLaterAgent::enabledAgent() const { return SendLaterAgentSettings::enabled(); } void SendLaterAgent::configure(WId windowId) { QPointer dialog = new SendLaterConfigureDialog(); if (windowId) { dialog->setAttribute(Qt::WA_NativeWindow, true); KWindowSystem::setMainWindow(dialog->windowHandle(), windowId); } connect(this, &SendLaterAgent::needUpdateConfigDialogBox, dialog.data(), &SendLaterConfigureDialog::slotNeedToReloadConfig); connect(dialog.data(), &SendLaterConfigureDialog::sendNow, this, &SendLaterAgent::slotSendNow); if (dialog->exec()) { mManager->load(); const QVector listMessage = dialog->messagesToRemove(); if (!listMessage.isEmpty()) { //Will delete in specific job when done. SendLaterRemoveMessageJob *sendlaterremovejob = new SendLaterRemoveMessageJob(listMessage, this); sendlaterremovejob->start(); } } delete dialog; } void SendLaterAgent::removeItem(qint64 item) { if (mManager->itemRemoved(item)) { reload(); } } void SendLaterAgent::slotSendNow(Akonadi::Item::Id id) { mManager->sendNow(id); } void SendLaterAgent::itemsRemoved(const Akonadi::Item::List &items) { bool needToReload = false; for (const Akonadi::Item &item : items) { if (mManager->itemRemoved(item.id())) { needToReload = true; } } if (needToReload) { reload(); } } void SendLaterAgent::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection & /*sourceCollection*/, const Akonadi::Collection &destinationCollection) { if (Akonadi::SpecialMailCollections::self()->specialCollectionType(destinationCollection) != Akonadi::SpecialMailCollections::Trash) { return; } itemsRemoved(items); } QString SendLaterAgent::printDebugInfo() const { return mManager->printDebugInfo(); } AKONADI_AGENT_MAIN(SendLaterAgent) diff --git a/agents/sendlateragent/sendlaterconfiguredialog.cpp b/agents/sendlateragent/sendlaterconfiguredialog.cpp index 35635236b..9545bb33f 100644 --- a/agents/sendlateragent/sendlaterconfiguredialog.cpp +++ b/agents/sendlateragent/sendlaterconfiguredialog.cpp @@ -1,113 +1,113 @@ /* Copyright (C) 2013-2019 Montel Laurent 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sendlaterconfiguredialog.h" #include "sendlaterconfigurewidget.h" #include "kmail-version.h" #include #include #include #include #include -#include +#include #include #include #include SendLaterConfigureDialog::SendLaterConfigureDialog(QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("Configure")); setWindowIcon(QIcon::fromTheme(QStringLiteral("kmail"))); QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::rejected, this, &SendLaterConfigureDialog::reject); mWidget = new SendLaterWidget(this); mWidget->setObjectName(QStringLiteral("sendlaterwidget")); connect(mWidget, &SendLaterWidget::sendNow, this, &SendLaterConfigureDialog::sendNow); mainLayout->addWidget(mWidget); mainLayout->addWidget(buttonBox); connect(okButton, &QPushButton::clicked, this, &SendLaterConfigureDialog::slotSave); readConfig(); KAboutData aboutData = KAboutData( QStringLiteral("sendlateragent"), i18n("Send Later Agent"), QStringLiteral(KDEPIM_VERSION), i18n("Send emails later agent."), KAboutLicense::GPL_V2, i18n("Copyright (C) 2013-2019 Laurent Montel")); aboutData.addAuthor(i18n("Laurent Montel"), i18n("Maintainer"), QStringLiteral("montel@kde.org")); QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("kmail"))); aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails")); KHelpMenu *helpMenu = new KHelpMenu(this, aboutData, true); //Initialize menu QMenu *menu = helpMenu->menu(); helpMenu->action(KHelpMenu::menuAboutApp)->setIcon(QIcon::fromTheme(QStringLiteral("kmail"))); buttonBox->button(QDialogButtonBox::Help)->setMenu(menu); } SendLaterConfigureDialog::~SendLaterConfigureDialog() { writeConfig(); } QVector SendLaterConfigureDialog::messagesToRemove() const { return mWidget->messagesToRemove(); } void SendLaterConfigureDialog::slotSave() { mWidget->save(); accept(); } void SendLaterConfigureDialog::slotNeedToReloadConfig() { mWidget->needToReload(); } void SendLaterConfigureDialog::readConfig() { KConfigGroup group(KSharedConfig::openConfig(), "SendLaterConfigureDialog"); const QSize sizeDialog = group.readEntry("Size", QSize(800, 600)); if (sizeDialog.isValid()) { resize(sizeDialog); } mWidget->restoreTreeWidgetHeader(group.readEntry("HeaderState", QByteArray())); } void SendLaterConfigureDialog::writeConfig() { KConfigGroup group(KSharedConfig::openConfig(), "SendLaterConfigureDialog"); group.writeEntry("Size", size()); mWidget->saveTreeWidgetHeader(group); } diff --git a/kmail-refresh-settings/main.cpp b/kmail-refresh-settings/main.cpp index 5c07c7251..19e15a68c 100644 --- a/kmail-refresh-settings/main.cpp +++ b/kmail-refresh-settings/main.cpp @@ -1,67 +1,67 @@ /* Copyright (C) 2019 Montel Laurent 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "refreshsettingsassistant.h" #include -#include +#include #include #include #include #include #include #include #include int main(int argc, char **argv) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); QApplication app(argc, argv); KLocalizedString::setApplicationDomain("kmail-refresh-settings"); KCrash::initialize(); KAboutData aboutData(QStringLiteral("kmail-refresh-settings"), i18n("KMail Assistant for refreshing settings"), QStringLiteral("0.1"), i18n("KMail Assistant for refreshing settings"), KAboutLicense::LGPL, i18n("(c) 2019 Laurent Montel ")); aboutData.addAuthor(i18n("Laurent Montel"), i18n("Author"), QStringLiteral("montel@kde.org")); app.setOrganizationDomain(QStringLiteral("kde.org")); app.setWindowIcon(QIcon::fromTheme(QStringLiteral("kontact"))); QCommandLineParser parser; KAboutData::setApplicationData(aboutData); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); KDBusService service(KDBusService::Unique); Akonadi::ControlGui::start(nullptr); RefreshSettingsAssistant dlg(nullptr); dlg.show(); return app.exec(); } diff --git a/ktnef/src/main.cpp b/ktnef/src/main.cpp index db64d0ff7..a5eeefd45 100644 --- a/ktnef/src/main.cpp +++ b/ktnef/src/main.cpp @@ -1,78 +1,78 @@ /* This file is part of KTnef. Copyright (C) 2002 Michael Goffioul Copyright (c) 2012 Allen Winter 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. 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 "ktnefmain.h" #include "ktnef-version.h" #include -#include +#include #include #include #include #include #include int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); QApplication app(argc, argv); KLocalizedString::setApplicationDomain("ktnef"); KCrash::initialize(); Kdelibs4ConfigMigrator migrate(QStringLiteral("ktnef")); migrate.setConfigFiles(QStringList() << QStringLiteral("ktnefrc")); migrate.setUiFiles(QStringList() << QStringLiteral("ktnefui.rc")); migrate.migrate(); KAboutData aboutData(QStringLiteral("ktnef"), i18n("KTnef"), QStringLiteral(KTNEF_VERSION), i18n("Viewer for mail attachments using TNEF format"), KAboutLicense::GPL, i18n("Copyright 2000 Michael Goffioul\nCopyright 2012 Allen Winter")); aboutData.addAuthor( i18n("Michael Goffioul"), i18n("Author"), QStringLiteral("kdeprint@swing.be")); aboutData.addAuthor( i18n("Allen Winter"), i18n("Author, Ported to Qt4/KDE4"), QStringLiteral("winter@kde.org")); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; parser.setApplicationDescription(QApplication::applicationDisplayName()); parser.addPositionalArgument(QStringLiteral("file"), i18n("An optional argument 'file' "), QStringLiteral("[file]")); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); KDBusService service; KTNEFMain *tnef = new KTNEFMain(); tnef->show(); const QStringList &args = parser.positionalArguments(); if (!args.isEmpty()) { tnef->loadFile(args.constFirst()); } return app.exec(); } diff --git a/src/configuredialog/configmodule.h b/src/configuredialog/configmodule.h index 8cc6db832..5fd8a61e6 100644 --- a/src/configuredialog/configmodule.h +++ b/src/configuredialog/configmodule.h @@ -1,50 +1,50 @@ /* -*- mode: C++; c-file-style: "gnu" -*- * kmail: KDE mail client * Copyright (C) 2000 Espen Sand, espen@kde.org * Copyright (C) 2001-2003 Marc Mutz, mutz@kde.org * Contains code segments and ideas from earlier kmail dialog code. * Copyright (C) 2010 Volker Krause * * 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 KMAIL_CONFIGMODULE_H #define KMAIL_CONFIGMODULE_H #include "kmail_export.h" -#include +#include class ConfigModule : public KCModule { public: explicit ConfigModule(QWidget *parent = nullptr) : KCModule(parent) { } ~ConfigModule() override { } void defaults() override { } /** Should return the help anchor for this page or tab */ virtual QString helpAnchor() const = 0; }; #endif diff --git a/src/configuredialog/configuredialog.h b/src/configuredialog/configuredialog.h index ce077ed12..df2f34b86 100644 --- a/src/configuredialog/configuredialog.h +++ b/src/configuredialog/configuredialog.h @@ -1,56 +1,56 @@ /* -*- c++ -*- * kmail: KDE mail client * Copyright (C) 2000 Espen Sand, espen@kde.org * Copyright (C) 2001-2002 Marc Mutz * * 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 CONFIGURE_DIALOG_H #define CONFIGURE_DIALOG_H -#include +#include #include class ConfigureDialog : public KCMultiDialog { Q_OBJECT public: explicit ConfigureDialog(QWidget *parent = nullptr, bool modal = true); ~ConfigureDialog() override; Q_SIGNALS: void configChanged(); protected: void hideEvent(QHideEvent *i) override; QSize sizeHint() const override; protected Q_SLOTS: /** @b reimplemented * Saves the GlobalSettings stuff before passing on to KCMultiDialog. */ void slotApply(); /** @b reimplemented * Saves the GlobalSettings stuff before passing on to KCMultiDialog. */ void slotOk(); }; #endif diff --git a/src/configuredialog/configuredialog_p.h b/src/configuredialog/configuredialog_p.h index c816b6add..9d280893a 100644 --- a/src/configuredialog/configuredialog_p.h +++ b/src/configuredialog/configuredialog_p.h @@ -1,88 +1,88 @@ // -*- c++ -*- // configuredialog_p.h: classes internal to ConfigureDialog // see configuredialog.h for details. #ifndef CONFIGURE_DIALOG_PRIVATE_H #define CONFIGURE_DIALOG_PRIVATE_H #include "kmail_export.h" #include "configmodule.h" -#include +#include class QTabWidget; class ConfigureDialog; // Individual tab of a ConfigModuleWithTabs class ConfigModuleTab : public QWidget { Q_OBJECT public: explicit ConfigModuleTab(QWidget *parent = nullptr) : QWidget(parent) , mEmitChanges(true) { } ~ConfigModuleTab() { } virtual void save() = 0; void defaults(); Q_SIGNALS: // forwarded to the ConfigModule void changed(bool); public Q_SLOTS: void slotEmitChanged(); void load(); protected: bool mEmitChanges; private: // reimplement this for loading values of settings which are available // via GlobalSettings virtual void doLoadFromGlobalSettings() { } // reimplement this for loading values of settings which are not available // via GlobalSettings virtual void doLoadOther() { } // reimplement this for loading default values of settings which are // not available via GlobalSettings (KConfigXT). virtual void doResetToDefaultsOther() { } }; /* * ConfigModuleWithTabs represents a kcm with several tabs. * It simply forwards load and save operations to all tabs. */ class KMAIL_EXPORT ConfigModuleWithTabs : public ConfigModule { Q_OBJECT public: explicit ConfigModuleWithTabs(QWidget *parent = nullptr); ~ConfigModuleWithTabs() override { } // don't reimplement any of those methods void load() override; void save() override; void defaults() override; protected: void showEvent(QShowEvent *event) override; void addTab(ConfigModuleTab *tab, const QString &title); private: QTabWidget *mTabWidget = nullptr; bool mWasInitialized = false; }; #endif // _CONFIGURE_DIALOG_PRIVATE_H_ diff --git a/src/dialog/archivefolderdialog.cpp b/src/dialog/archivefolderdialog.cpp index 8ec1edd8c..f88a35107 100644 --- a/src/dialog/archivefolderdialog.cpp +++ b/src/dialog/archivefolderdialog.cpp @@ -1,228 +1,228 @@ /* Copyright 2009 Klarälvdalens Datakonsult AB 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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, see . */ #include "archivefolderdialog.h" #include "MailCommon/BackupJob" #include "kmkernel.h" #include "kmmainwidget.h" #include "MailCommon/FolderRequester" #include "MessageViewer/MessageViewerUtil" #include #include -#include -#include -#include +#include +#include +#include #include #include #include #include #include #include #include #include #include using namespace KMail; using namespace MailCommon; QString ArchiveFolderDialog::standardArchivePath(const QString &folderName) { QString currentPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QDir dir(currentPath); if (!dir.exists()) { currentPath = QDir::homePath(); } return currentPath + QLatin1Char('/') +i18nc("Start of the filename for a mail archive file", "Archive") + QLatin1Char('_') + folderName + QLatin1Char('_') + QDate::currentDate().toString(Qt::ISODate) + QLatin1String(".tar.bz2"); } ArchiveFolderDialog::ArchiveFolderDialog(QWidget *parent) : QDialog(parent) , mParentWidget(parent) { setObjectName(QStringLiteral("archive_folder_dialog")); setWindowTitle(i18nc("@title:window for archiving a folder", "Archive Folder")); QVBoxLayout *topLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); mOkButton = buttonBox->button(QDialogButtonBox::Ok); mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &ArchiveFolderDialog::slotAccepted); connect(buttonBox, &QDialogButtonBox::rejected, this, &ArchiveFolderDialog::reject); mOkButton->setDefault(true); mOkButton->setText(i18nc("@action", "Archive")); setModal(true); QWidget *mainWidget = new QWidget(this); topLayout->addWidget(mainWidget); topLayout->addWidget(buttonBox); QGridLayout *mainLayout = new QGridLayout(mainWidget); mainLayout->setContentsMargins(0, 0, 0, 0); int row = 0; // TODO: Explanation label QLabel *folderLabel = new QLabel(i18n("&Folder:"), mainWidget); mainLayout->addWidget(folderLabel, row, 0); mFolderRequester = new FolderRequester(mainWidget); mFolderRequester->setMustBeReadWrite(false); mFolderRequester->setNotAllowToCreateNewFolder(true); connect(mFolderRequester, &FolderRequester::folderChanged, this, &ArchiveFolderDialog::slotFolderChanged); folderLabel->setBuddy(mFolderRequester); mainLayout->addWidget(mFolderRequester, row, 1); row++; QLabel *formatLabel = new QLabel(i18n("F&ormat:"), mainWidget); mainLayout->addWidget(formatLabel, row, 0); mFormatComboBox = new KComboBox(mainWidget); formatLabel->setBuddy(mFormatComboBox); // These combobox values have to stay in sync with the ArchiveType enum from BackupJob! mFormatComboBox->addItem(i18n("Compressed Zip Archive (.zip)")); mFormatComboBox->addItem(i18n("Uncompressed Archive (.tar)")); mFormatComboBox->addItem(i18n("BZ2-Compressed Tar Archive (.tar.bz2)")); mFormatComboBox->addItem(i18n("GZ-Compressed Tar Archive (.tar.gz)")); mFormatComboBox->setCurrentIndex(2); connect(mFormatComboBox, qOverload(&KComboBox::activated), this, &ArchiveFolderDialog::slotFixFileExtension); mainLayout->addWidget(mFormatComboBox, row, 1); row++; QLabel *fileNameLabel = new QLabel(i18n("&Archive File:"), mainWidget); mainLayout->addWidget(fileNameLabel, row, 0); mUrlRequester = new KUrlRequester(mainWidget); mUrlRequester->setMode(KFile::LocalOnly | KFile::File); mUrlRequester->setFilter(QStringLiteral("*.tar *.zip *.tar.gz *.tar.bz2")); fileNameLabel->setBuddy(mUrlRequester); connect(mUrlRequester, &KUrlRequester::urlSelected, this, &ArchiveFolderDialog::slotFixFileExtension); connect(mUrlRequester, &KUrlRequester::textChanged, this, &ArchiveFolderDialog::slotUrlChanged); mainLayout->addWidget(mUrlRequester, row, 1); row++; // TODO: Make this appear more dangerous! mDeleteCheckBox = new QCheckBox(i18n("&Delete folder and subfolders after completion"), mainWidget); mainLayout->addWidget(mDeleteCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; mRecursiveCheckBox = new QCheckBox(i18n("Archive all subfolders"), mainWidget); connect(mRecursiveCheckBox, &QCheckBox::clicked, this, &ArchiveFolderDialog::slotRecursiveCheckboxClicked); mainLayout->addWidget(mRecursiveCheckBox, row, 0, 1, 2, Qt::AlignLeft); mRecursiveCheckBox->setChecked(true); row++; // TODO: what's this, tooltips // TODO: Warn that user should do mail check for online IMAP and possibly cached IMAP as well mainLayout->addWidget(new KSeparator(), row, 0, 1, 2); row++; mainLayout->setColumnStretch(1, 1); mainLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding), row, 0); // Make it a bit bigger, else the folder requester cuts off the text too early resize(500, minimumSize().height()); } bool canRemoveFolder(const Akonadi::Collection &col) { const QSharedPointer folder = FolderSettings::forCollection(col, false); return folder && col.isValid() && !col.isVirtual() && (col.rights() & Akonadi::Collection::CanDeleteCollection) && !folder->isStructural() && !folder->isSystemFolder(); } void ArchiveFolderDialog::slotRecursiveCheckboxClicked() { slotFolderChanged(mFolderRequester->collection()); } void ArchiveFolderDialog::slotFolderChanged(const Akonadi::Collection &folder) { mDeleteCheckBox->setEnabled(allowToDeleteFolders(folder)); } bool ArchiveFolderDialog::allowToDeleteFolders(const Akonadi::Collection &folder) const { return canRemoveFolder(folder) && mRecursiveCheckBox->isChecked(); } void ArchiveFolderDialog::setFolder(const Akonadi::Collection &defaultCollection) { mFolderRequester->setCollection(defaultCollection); // TODO: what if the file already exists? mUrlRequester->setUrl(QUrl::fromLocalFile(standardArchivePath(defaultCollection.name()))); const QSharedPointer folder = FolderSettings::forCollection(defaultCollection, false); mDeleteCheckBox->setEnabled(allowToDeleteFolders(defaultCollection)); mOkButton->setEnabled(defaultCollection.isValid() && folder && !folder->isStructural()); } void ArchiveFolderDialog::slotAccepted() { if (!MessageViewer::Util::checkOverwrite(mUrlRequester->url(), this)) { return; } if (!mFolderRequester->hasCollection()) { KMessageBox::information(this, i18n("Please select the folder that should be archived."), i18n("No folder selected")); return; } MailCommon::BackupJob *backupJob = new MailCommon::BackupJob(mParentWidget); backupJob->setRootFolder(mFolderRequester->collection()); backupJob->setSaveLocation(mUrlRequester->url()); backupJob->setArchiveType(static_cast(mFormatComboBox->currentIndex())); backupJob->setDeleteFoldersAfterCompletion(mDeleteCheckBox->isEnabled() && mDeleteCheckBox->isChecked()); backupJob->setRecursive(mRecursiveCheckBox->isChecked()); backupJob->start(); accept(); } void ArchiveFolderDialog::slotFixFileExtension() { const int numExtensions = 4; // The extensions here are also sorted, like the enum order of BackupJob::ArchiveType const char *extensions[numExtensions] = { ".zip", ".tar", ".tar.bz2", ".tar.gz" }; QString fileName = mUrlRequester->url().path(); if (fileName.isEmpty()) { fileName = standardArchivePath(mFolderRequester->hasCollection() ? mFolderRequester->collection().name() : QString()); } QMimeDatabase db; const QString extension = db.suffixForFileName(fileName); if (!extension.isEmpty()) { fileName.truncate(fileName.length() - extension.length() - 1); } // Now, we've got a filename without an extension, simply append the correct one fileName += QLatin1String(extensions[mFormatComboBox->currentIndex()]); mUrlRequester->setUrl(QUrl::fromLocalFile(fileName)); } void ArchiveFolderDialog::slotUrlChanged(const QString &url) { mOkButton->setEnabled(!url.isEmpty()); } diff --git a/src/editor/codec/codecaction.h b/src/editor/codec/codecaction.h index 5dc08437b..c333d1d3c 100644 --- a/src/editor/codec/codecaction.h +++ b/src/editor/codec/codecaction.h @@ -1,60 +1,60 @@ /* * This file is part of KMail. * Copyright (c) 2009 Constantin Berzan * * 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 CODECACTION_H #define CODECACTION_H // Qt #include // KDE -#include +#include // TODO since the reader is now in a separate lib, we can probably have this // class for the composer only. The reader can use KCodecAction directly anyway. class CodecAction : public KCodecAction { Q_OBJECT public: enum Mode { ComposerMode, ///< Used in the composer. Show a 'Default' menu entry, /// which uses one of the preferred codecs. Also show 'us-ascii'. ReaderMode ///< Used in the reader. Show an 'Auto' entry for each language, /// and detect any charset. }; explicit CodecAction(Mode mode, QObject *parent = nullptr); ~CodecAction(); /** The name of the charset, if a specific encoding was selected, or a list containing the names of the preferred charsets, if 'Default' was selected in Composer mode. In Reader mode it probably makes more sense to use KCodecAction::currentCodec() and KCodecAction::currentAutoDetectScript(). */ Q_REQUIRED_RESULT QVector mimeCharsets() const; void setAutoCharset(); void setCharset(const QByteArray &charset); private: const CodecAction::Mode mMode; }; #endif diff --git a/src/editor/codec/codecmanager.cpp b/src/editor/codec/codecmanager.cpp index 705fc16ab..67b8a8ef0 100644 --- a/src/editor/codec/codecmanager.cpp +++ b/src/editor/codec/codecmanager.cpp @@ -1,82 +1,82 @@ /* * This file is part of KMail. * Copyright (c) 2009 Constantin Berzan * * Based on KMMsgBase code by: * Copyright (c) 1996-1998 Stefan Taferner * * 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. */ // Own #include "codecmanager.h" // KMail #include "kmkernel.h" // Qt #include // KDE libs -#include +#include #include CodecManager::CodecManager() { updatePreferredCharsets(); } // static CodecManager *CodecManager::self() { static CodecManager instance; return &instance; } QVector CodecManager::preferredCharsets() const { return mPreferredCharsets; } void CodecManager::updatePreferredCharsets() { const QStringList prefCharsets = MessageComposer::MessageComposerSettings::self()->preferredCharsets(); mPreferredCharsets.clear(); for (const QString &str : prefCharsets) { QByteArray charset = str.toLatin1().toLower(); if (charset == "locale") { charset = QTextCodec::codecForLocale()->name(); // Special case for Japanese: // (Introduction to i18n, 6.6 Limit of Locale technology): // EUC-JP is the de-facto standard for UNIX systems, ISO 2022-JP // is the standard for Internet, and Shift-JIS is the encoding // for Windows and Macintosh. if (charset == "jisx0208.1983-0" || charset == "eucjp" || charset == "shift-jis") { charset = "iso-2022-jp"; // TODO wtf is "jis7"? } // Special case for Korean: if (charset == "ksc5601.1987-0") { charset = "euc-kr"; } } mPreferredCharsets << charset; } } diff --git a/src/identity/identitydialog.cpp b/src/identity/identitydialog.cpp index 2a36cbce4..1f33c3f30 100644 --- a/src/identity/identitydialog.cpp +++ b/src/identity/identitydialog.cpp @@ -1,1153 +1,1153 @@ /* identitydialog.cpp This file is part of KMail, the KDE mail client. Copyright (c) 2002 Marc Mutz Copyright (C) 2014-2019 Laurent Montel 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 In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "identitydialog.h" #include "identityeditvcarddialog.h" #include "identityaddvcarddialog.h" #include "identityinvalidfolder.h" #include "identityfolderrequester.h" #include #include #include "MessageComposer/MessageComposerSettings" #include // other KMail headers: #include "xfaceconfigurator.h" #include #include "mailcommon/folderrequester.h" #ifndef KCM_KPIMIDENTITIES_STANDALONE #include "settings/kmailsettings.h" #include "kmkernel.h" #endif #include "mailcommon/mailkernel.h" #include "job/addressvalidationjob.h" #include #include #include #include "TemplateParser/TemplatesConfiguration" #include "templatesconfiguration_kfg.h" // other kdepim headers: #include #include #include "PimCommon/AutoCorrectionLanguage" #include #include // libkleopatra: #include #include #include #include // gpgme++ #include #include #include #include #include #include using MailTransport::TransportManager; // other KDE headers: #include -#include -#include +#include +#include #include "kmail_debug.h" #include #include #include #include // Qt headers: #include #include #include #include #include #include #include #include #include // other headers: #include #include #include #include #include #include #include #include #include using namespace KPIM; using namespace MailTransport; using namespace MailCommon; namespace KMail { class KeySelectionCombo : public Kleo::KeySelectionCombo { Q_OBJECT public: enum KeyType { SigningKey, EncryptionKey }; KeySelectionCombo(KeyType keyType, GpgME::Protocol protocol, QWidget *parent); ~KeySelectionCombo() override; void setIdentity(const QString &name, const QString &email); void init() override; private: void onCustomItemSelected(const QVariant &type); QString mEmail; QString mName; KeyType mKeyType; GpgME::Protocol mProtocol; }; class KeyGenerationJob : public QGpgME::Job { Q_OBJECT public: KeyGenerationJob(const QString &name, const QString &email, KeySelectionCombo *parent); ~KeyGenerationJob() override; void slotCancel() override; void start(); private: void keyGenerated(const GpgME::KeyGenerationResult &result); QString mName; QString mEmail; QGpgME::Job *mJob = nullptr; }; KeyGenerationJob::KeyGenerationJob(const QString &name, const QString &email, KeySelectionCombo *parent) : QGpgME::Job(parent) , mName(name) , mEmail(email) , mJob(nullptr) { } KeyGenerationJob::~KeyGenerationJob() { } void KeyGenerationJob::slotCancel() { if (mJob) { mJob->slotCancel(); } } void KeyGenerationJob::start() { auto job = new Kleo::DefaultKeyGenerationJob(this); connect(job, &Kleo::DefaultKeyGenerationJob::result, this, &KeyGenerationJob::keyGenerated); job->start(mEmail, mName); mJob = job; } void KeyGenerationJob::keyGenerated(const GpgME::KeyGenerationResult &result) { mJob = nullptr; if (result.error()) { KMessageBox::error(qobject_cast(parent()), i18n("Error while generating new key pair: %1", QString::fromUtf8(result.error().asString())), i18n("Key Generation Error")); Q_EMIT done(); return; } KeySelectionCombo *combo = qobject_cast(parent()); combo->setDefaultKey(QLatin1String(result.fingerprint())); connect(combo, &KeySelectionCombo::keyListingFinished, this, &KeyGenerationJob::done); combo->refreshKeys(); } KeySelectionCombo::KeySelectionCombo(KeyType keyType, GpgME::Protocol protocol, QWidget *parent) : Kleo::KeySelectionCombo(parent) , mKeyType(keyType) , mProtocol(protocol) { } KeySelectionCombo::~KeySelectionCombo() { } void KeySelectionCombo::setIdentity(const QString &name, const QString &email) { mName = name; mEmail = email; setIdFilter(email); } void KeySelectionCombo::init() { Kleo::KeySelectionCombo::init(); std::shared_ptr keyFilter(new Kleo::DefaultKeyFilter); keyFilter->setIsOpenPGP(mProtocol == GpgME::OpenPGP ? Kleo::DefaultKeyFilter::Set : Kleo::DefaultKeyFilter::NotSet); if (mKeyType == SigningKey) { keyFilter->setCanSign(Kleo::DefaultKeyFilter::Set); } else { keyFilter->setCanEncrypt(Kleo::DefaultKeyFilter::Set); } keyFilter->setHasSecret(Kleo::DefaultKeyFilter::Set); setKeyFilter(keyFilter); prependCustomItem(QIcon(), i18n("No key"), QStringLiteral("no-key")); if (mProtocol == GpgME::OpenPGP) { appendCustomItem(QIcon::fromTheme(QStringLiteral("password-generate")), i18n("Generate a new key pair"), QStringLiteral("generate-new-key")); } connect(this, &KeySelectionCombo::customItemSelected, this, &KeySelectionCombo::onCustomItemSelected); } void KeySelectionCombo::onCustomItemSelected(const QVariant &type) { if (type == QLatin1String("no-key")) { return; } else if (type == QLatin1String("generate-new-key")) { auto job = new KeyGenerationJob(mName, mEmail, this); auto dlg = new Kleo::ProgressDialog(job, i18n("Generating new key pair..."), parentWidget()); dlg->setModal(true); setEnabled(false); connect(job, &KeyGenerationJob::done, this, [this]() { setEnabled(true); }); job->start(); } } IdentityDialog::IdentityDialog(QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("Edit Identity")); QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, this); connect(buttonBox->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &IdentityDialog::slotHelp); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &IdentityDialog::slotAccepted); connect(buttonBox, &QDialogButtonBox::rejected, this, &IdentityDialog::reject); // // Tab Widget: General // int row = -1; QWidget *page = new QWidget(this); mainLayout->addWidget(page); mainLayout->addWidget(buttonBox); QVBoxLayout *vlay = new QVBoxLayout(page); vlay->setContentsMargins(0, 0, 0, 0); mTabWidget = new QTabWidget(page); mTabWidget->setObjectName(QStringLiteral("config-identity-tab")); vlay->addWidget(mTabWidget); QWidget *tab = new QWidget(mTabWidget); mTabWidget->addTab(tab, i18nc("@title:tab General identity settings.", "General")); QGridLayout *glay = new QGridLayout(tab); glay->setRowStretch(3, 1); glay->setColumnStretch(1, 1); // "Name" line edit and label: ++row; mNameEdit = new KLineEdit(tab); glay->addWidget(mNameEdit, row, 1); QLabel *label = new QLabel(i18n("&Your name:"), tab); label->setBuddy(mNameEdit); glay->addWidget(label, row, 0); QString msg = i18n("

Your name

" "

This field should contain your name as you would like " "it to appear in the email header that is sent out;

" "

if you leave this blank your real name will not " "appear, only the email address.

"); label->setWhatsThis(msg); mNameEdit->setWhatsThis(msg); // "Organization" line edit and label: ++row; mOrganizationEdit = new KLineEdit(tab); glay->addWidget(mOrganizationEdit, row, 1); label = new QLabel(i18n("Organi&zation:"), tab); label->setBuddy(mOrganizationEdit); glay->addWidget(label, row, 0); msg = i18n("

Organization

" "

This field should have the name of your organization " "if you would like it to be shown in the email header that " "is sent out.

" "

It is safe (and normal) to leave this blank.

"); label->setWhatsThis(msg); mOrganizationEdit->setWhatsThis(msg); // "Email Address" line edit and label: // (row 3: spacer) ++row; mEmailEdit = new KLineEdit(tab); glay->addWidget(mEmailEdit, row, 1); label = new QLabel(i18n("&Email address:"), tab); label->setBuddy(mEmailEdit); glay->addWidget(label, row, 0); msg = i18n("

Email address

" "

This field should have your full email address.

" "

This address is the primary one, used for all outgoing mail. " "If you have more than one address, either create a new identity, " "or add additional alias addresses in the field below.

" "

If you leave this blank, or get it wrong, people " "will have trouble replying to you.

"); label->setWhatsThis(msg); mEmailEdit->setWhatsThis(msg); KPIM::EmailValidator *emailValidator = new KPIM::EmailValidator(this); mEmailEdit->setValidator(emailValidator); // "Email Aliases" string text edit and label: ++row; mAliasEdit = new KEditListWidget(tab); KPIM::EmailValidator *emailValidator1 = new KPIM::EmailValidator(this); mAliasEdit->lineEdit()->setValidator(emailValidator1); glay->addWidget(mAliasEdit, row, 1); label = new QLabel(i18n("Email a&liases:"), tab); label->setBuddy(mAliasEdit); glay->addWidget(label, row, 0, Qt::AlignTop); msg = i18n("

Email aliases

" "

This field contains alias addresses that should also " "be considered as belonging to this identity (as opposed " "to representing a different identity).

" "

Example:

" "" "" "" "
Primary address:first.last@example.org
Aliases:first@example.org
last@example.org
" "

Type one alias address per line.

"); label->setToolTip(msg); mAliasEdit->setWhatsThis(msg); // // Tab Widget: Cryptography // row = -1; mCryptographyTab = tab = new QWidget(mTabWidget); mTabWidget->addTab(tab, i18n("Cryptography")); glay = new QGridLayout(tab); glay->setColumnStretch(1, 1); // "OpenPGP Signature Key" requester and label: ++row; mPGPSigningKeyRequester = new KeySelectionCombo(KeySelectionCombo::SigningKey, GpgME::OpenPGP, tab); msg = i18n("

The OpenPGP key you choose here will be used " "to digitally sign messages. You can also use GnuPG keys.

" "

You can leave this blank, but KMail will not be able " "to digitally sign emails using OpenPGP; " "normal mail functions will not be affected.

" "

You can find out more about keys at https://www.gnupg.org

"); label = new QLabel(i18n("OpenPGP signing key:"), tab); label->setBuddy(mPGPSigningKeyRequester); mPGPSigningKeyRequester->setWhatsThis(msg); label->setWhatsThis(msg); glay->addWidget(label, row, 0); glay->addWidget(mPGPSigningKeyRequester, row, 1); // "OpenPGP Encryption Key" requester and label: ++row; mPGPEncryptionKeyRequester = new KeySelectionCombo(KeySelectionCombo::EncryptionKey, GpgME::OpenPGP, tab); msg = i18n("

The OpenPGP key you choose here will be used " "to encrypt messages to yourself and for the \"Attach My Public Key\" " "feature in the composer. You can also use GnuPG keys.

" "

You can leave this blank, but KMail will not be able " "to encrypt copies of outgoing messages to you using OpenPGP; " "normal mail functions will not be affected.

" "

You can find out more about keys at https://www.gnupg.org

"); label = new QLabel(i18n("OpenPGP encryption key:"), tab); label->setBuddy(mPGPEncryptionKeyRequester); mPGPEncryptionKeyRequester->setWhatsThis(msg); label->setWhatsThis(msg); glay->addWidget(label, row, 0); glay->addWidget(mPGPEncryptionKeyRequester, row, 1); // "S/MIME Signature Key" requester and label: ++row; mSMIMESigningKeyRequester = new KeySelectionCombo(KeySelectionCombo::SigningKey, GpgME::CMS, tab); msg = i18n("

The S/MIME (X.509) certificate you choose here will be used " "to digitally sign messages.

" "

You can leave this blank, but KMail will not be able " "to digitally sign emails using S/MIME; " "normal mail functions will not be affected.

"); label = new QLabel(i18n("S/MIME signing certificate:"), tab); label->setBuddy(mSMIMESigningKeyRequester); mSMIMESigningKeyRequester->setWhatsThis(msg); label->setWhatsThis(msg); glay->addWidget(label, row, 0); glay->addWidget(mSMIMESigningKeyRequester, row, 1); const QGpgME::Protocol *smimeProtocol = QGpgME::smime(); label->setEnabled(smimeProtocol); mSMIMESigningKeyRequester->setEnabled(smimeProtocol); // "S/MIME Encryption Key" requester and label: ++row; mSMIMEEncryptionKeyRequester = new KeySelectionCombo(KeySelectionCombo::EncryptionKey, GpgME::CMS, tab); msg = i18n("

The S/MIME certificate you choose here will be used " "to encrypt messages to yourself and for the \"Attach My Certificate\" " "feature in the composer.

" "

You can leave this blank, but KMail will not be able " "to encrypt copies of outgoing messages to you using S/MIME; " "normal mail functions will not be affected.

"); label = new QLabel(i18n("S/MIME encryption certificate:"), tab); label->setBuddy(mSMIMEEncryptionKeyRequester); mSMIMEEncryptionKeyRequester->setWhatsThis(msg); label->setWhatsThis(msg); glay->addWidget(label, row, 0); glay->addWidget(mSMIMEEncryptionKeyRequester, row, 1); label->setEnabled(smimeProtocol); mSMIMEEncryptionKeyRequester->setEnabled(smimeProtocol); // "Preferred Crypto Message Format" combobox and label: ++row; mPreferredCryptoMessageFormat = new QComboBox(tab); QStringList l; l << Kleo::cryptoMessageFormatToLabel(Kleo::AutoFormat) << Kleo::cryptoMessageFormatToLabel(Kleo::InlineOpenPGPFormat) << Kleo::cryptoMessageFormatToLabel(Kleo::OpenPGPMIMEFormat) << Kleo::cryptoMessageFormatToLabel(Kleo::SMIMEFormat) << Kleo::cryptoMessageFormatToLabel(Kleo::SMIMEOpaqueFormat); mPreferredCryptoMessageFormat->addItems(l); label = new QLabel(i18nc("preferred format of encrypted messages", "Preferred format:"), tab); label->setBuddy(mPreferredCryptoMessageFormat); glay->addWidget(label, row, 0); glay->addWidget(mPreferredCryptoMessageFormat, row, 1); ++row; mAutoSign = new QCheckBox(i18n("Automatically sign messages")); glay->addWidget(mAutoSign, row, 0); ++row; mAutoEncrypt = new QCheckBox(i18n("Automatically encrypt messages when possible")); glay->addWidget(mAutoEncrypt, row, 0); ++row; glay->setRowStretch(row, 1); // // Tab Widget: Advanced // row = -1; tab = new QWidget(mTabWidget); QVBoxLayout *advancedMainLayout = new QVBoxLayout(tab); mIdentityInvalidFolder = new IdentityInvalidFolder(tab); advancedMainLayout->addWidget(mIdentityInvalidFolder); mTabWidget->addTab(tab, i18nc("@title:tab Advanced identity settings.", "Advanced")); glay = new QGridLayout; advancedMainLayout->addLayout(glay); // the last (empty) row takes all the remaining space glay->setColumnStretch(1, 1); // "Reply-To Address" line edit and label: ++row; mReplyToEdit = new KPIM::AddresseeLineEdit(tab, true); mReplyToEdit->setClearButtonEnabled(true); mReplyToEdit->setObjectName(QStringLiteral("mReplyToEdit")); glay->addWidget(mReplyToEdit, row, 1); label = new QLabel(i18n("&Reply-To address:"), tab); label->setBuddy(mReplyToEdit); glay->addWidget(label, row, 0); msg = i18n("

Reply-To addresses

" "

This sets the Reply-to: header to contain a " "different email address to the normal From: " "address.

" "

This can be useful when you have a group of people " "working together in similar roles. For example, you " "might want any emails sent to have your email in the " "From: field, but any responses to go to " "a group address.

" "

If in doubt, leave this field blank.

"); label->setWhatsThis(msg); mReplyToEdit->setWhatsThis(msg); // "CC addresses" line edit and label: ++row; mCcEdit = new KPIM::AddresseeLineEdit(tab, true); mCcEdit->setClearButtonEnabled(true); mCcEdit->setObjectName(QStringLiteral("mCcEdit")); glay->addWidget(mCcEdit, row, 1); label = new QLabel(i18n("&CC addresses:"), tab); label->setBuddy(mCcEdit); glay->addWidget(label, row, 0); msg = i18n("

CC (Carbon Copy) addresses

" "

The addresses that you enter here will be added to each " "outgoing mail that is sent with this identity.

" "

This is commonly used to send a copy of each sent message to " "another account of yours.

" "

To specify more than one address, use commas to separate " "the list of CC recipients.

" "

If in doubt, leave this field blank.

"); label->setWhatsThis(msg); mCcEdit->setWhatsThis(msg); // "BCC addresses" line edit and label: ++row; mBccEdit = new KPIM::AddresseeLineEdit(tab, true); mBccEdit->setClearButtonEnabled(true); mBccEdit->setObjectName(QStringLiteral("mBccEdit")); glay->addWidget(mBccEdit, row, 1); label = new QLabel(i18n("&BCC addresses:"), tab); label->setBuddy(mBccEdit); glay->addWidget(label, row, 0); msg = i18n("

BCC (Blind Carbon Copy) addresses

" "

The addresses that you enter here will be added to each " "outgoing mail that is sent with this identity. They will not " "be visible to other recipients.

" "

This is commonly used to send a copy of each sent message to " "another account of yours.

" "

To specify more than one address, use commas to separate " "the list of BCC recipients.

" "

If in doubt, leave this field blank.

"); label->setWhatsThis(msg); mBccEdit->setWhatsThis(msg); // "Dictionary" combo box and label: ++row; mDictionaryCombo = new Sonnet::DictionaryComboBox(tab); glay->addWidget(mDictionaryCombo, row, 1); label = new QLabel(i18n("D&ictionary:"), tab); label->setBuddy(mDictionaryCombo); glay->addWidget(label, row, 0); // "Sent-mail Folder" combo box and label: ++row; mFccFolderRequester = new IdentityFolderRequester(tab); mFccFolderRequester->setShowOutbox(false); glay->addWidget(mFccFolderRequester, row, 1); mSentMailFolderCheck = new QCheckBox(i18n("Sent-mail &folder:"), tab); glay->addWidget(mSentMailFolderCheck, row, 0); connect(mSentMailFolderCheck, &QCheckBox::toggled, mFccFolderRequester, &MailCommon::FolderRequester::setEnabled); // "Drafts Folder" combo box and label: ++row; mDraftsFolderRequester = new IdentityFolderRequester(tab); mDraftsFolderRequester->setShowOutbox(false); glay->addWidget(mDraftsFolderRequester, row, 1); label = new QLabel(i18n("&Drafts folder:"), tab); label->setBuddy(mDraftsFolderRequester); glay->addWidget(label, row, 0); // "Templates Folder" combo box and label: ++row; mTemplatesFolderRequester = new IdentityFolderRequester(tab); mTemplatesFolderRequester->setShowOutbox(false); glay->addWidget(mTemplatesFolderRequester, row, 1); label = new QLabel(i18n("&Templates folder:"), tab); label->setBuddy(mTemplatesFolderRequester); glay->addWidget(label, row, 0); // "Special transport" combobox and label: ++row; mTransportCheck = new QCheckBox(i18n("Outgoing Account:"), tab); glay->addWidget(mTransportCheck, row, 0); mTransportCombo = new TransportComboBox(tab); mTransportCombo->setEnabled(false); // since !mTransportCheck->isChecked() glay->addWidget(mTransportCombo, row, 1); connect(mTransportCheck, &QCheckBox::toggled, mTransportCombo, &MailTransport::TransportComboBox::setEnabled); ++row; mAttachMyVCard = new QCheckBox(i18n("Attach my vCard to message"), tab); glay->addWidget(mAttachMyVCard, row, 0); mEditVCard = new QPushButton(i18n("Create..."), tab); connect(mEditVCard, &QPushButton::clicked, this, &IdentityDialog::slotEditVcard); glay->addWidget(mEditVCard, row, 1); ++row; mAutoCorrectionLanguage = new PimCommon::AutoCorrectionLanguage(tab); glay->addWidget(mAutoCorrectionLanguage, row, 1); label = new QLabel(i18n("Autocorrection language:"), tab); label->setBuddy(mAutoCorrectionLanguage); glay->addWidget(label, row, 0); // "default domain" input field: ++row; QHBoxLayout *hbox = new QHBoxLayout; mDefaultDomainEdit = new KLineEdit(tab); mDefaultDomainEdit->setClearButtonEnabled(true); hbox->addWidget(mDefaultDomainEdit); QToolButton *restoreDefaultDomainName = new QToolButton; restoreDefaultDomainName->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); restoreDefaultDomainName->setToolTip(i18n("Restore default domain name")); hbox->addWidget(restoreDefaultDomainName); connect(restoreDefaultDomainName, &QToolButton::clicked, this, &IdentityDialog::slotRefreshDefaultDomainName); glay->addLayout(hbox, row, 1); label = new QLabel(i18n("Defaul&t domain:"), tab); label->setBuddy(mDefaultDomainEdit); glay->addWidget(label, row, 0); // and now: add QWhatsThis: msg = i18n("

The default domain is used to complete email " "addresses that only consist of the user's name." "

"); label->setWhatsThis(msg); mDefaultDomainEdit->setWhatsThis(msg); ++row; glay->setRowStretch(row, 1); // the last row is a spacer // // Tab Widget: Templates // tab = new QWidget(mTabWidget); vlay = new QVBoxLayout(tab); QHBoxLayout *tlay = new QHBoxLayout(); vlay->addLayout(tlay); mCustom = new QCheckBox(i18n("&Use custom message templates for this identity"), tab); tlay->addWidget(mCustom, Qt::AlignLeft); mWidget = new TemplateParser::TemplatesConfiguration(tab, QStringLiteral("identity-templates")); mWidget->setEnabled(false); // Move the help label outside of the templates configuration widget, // so that the help can be read even if the widget is not enabled. tlay->addStretch(9); tlay->addWidget(mWidget->helpLabel(), Qt::AlignRight); vlay->addWidget(mWidget); QHBoxLayout *btns = new QHBoxLayout(); mCopyGlobal = new QPushButton(i18n("&Copy Global Templates"), tab); mCopyGlobal->setEnabled(false); btns->addWidget(mCopyGlobal); vlay->addLayout(btns); connect(mCustom, &QCheckBox::toggled, mWidget, &TemplateParser::TemplatesConfiguration::setEnabled); connect(mCustom, &QCheckBox::toggled, mCopyGlobal, &QPushButton::setEnabled); connect(mCopyGlobal, &QPushButton::clicked, this, &IdentityDialog::slotCopyGlobal); mTabWidget->addTab(tab, i18n("Templates")); // // Tab Widget: Signature // mSignatureConfigurator = new KIdentityManagement::SignatureConfigurator(mTabWidget); mTabWidget->addTab(mSignatureConfigurator, i18n("Signature")); // // Tab Widget: Picture // mXFaceConfigurator = new XFaceConfigurator(mTabWidget); mTabWidget->addTab(mXFaceConfigurator, i18n("Picture")); #ifndef KCM_KPIMIDENTITIES_STANDALONE resize(KMailSettings::self()->identityDialogSize()); #endif mNameEdit->setFocus(); connect(mTabWidget, &QTabWidget::currentChanged, this, &IdentityDialog::slotAboutToShow); } IdentityDialog::~IdentityDialog() { #ifndef KCM_KPIMIDENTITIES_STANDALONE KMailSettings::self()->setIdentityDialogSize(size()); #endif } void IdentityDialog::slotHelp() { PimCommon::Util::invokeHelp(QStringLiteral("kmail2/configure-identity.html")); } void IdentityDialog::slotAboutToShow(int index) { QWidget *w = mTabWidget->widget(index); if (w == mCryptographyTab) { // set the configured email address as initial query of the key // requesters: const QString name = mNameEdit->text().trimmed(); const QString email = mEmailEdit->text().trimmed(); mPGPEncryptionKeyRequester->setIdentity(name, email); mPGPSigningKeyRequester->setIdentity(name, email); mSMIMEEncryptionKeyRequester->setIdentity(name, email); mSMIMESigningKeyRequester->setIdentity(name, email); } } void IdentityDialog::slotCopyGlobal() { mWidget->loadFromGlobal(); } void IdentityDialog::slotRefreshDefaultDomainName() { mDefaultDomainEdit->setText(QHostInfo::localHostName()); } void IdentityDialog::slotAccepted() { const QStringList aliases = mAliasEdit->items(); for (const QString &alias : aliases) { if (alias.trimmed().isEmpty()) { continue; } if (!KEmailAddress::isValidSimpleAddress(alias)) { const QString errorMsg(KEmailAddress::simpleEmailAddressErrorMsg()); KMessageBox::sorry(this, errorMsg, i18n("Invalid Email Alias \"%1\"", alias)); return; } } // Validate email addresses const QString email = mEmailEdit->text().trimmed(); if (!KEmailAddress::isValidSimpleAddress(email)) { const QString errorMsg(KEmailAddress::simpleEmailAddressErrorMsg()); KMessageBox::sorry(this, errorMsg, i18n("Invalid Email Address")); return; } // Check if the 'Reply to' and 'BCC' recipients are valid const QString recipients = mReplyToEdit->text().trimmed() + QLatin1String(", ") + mBccEdit->text().trimmed() + QLatin1String(", ") + mCcEdit->text().trimmed(); AddressValidationJob *job = new AddressValidationJob(recipients, this, this); //Use default Value job->setDefaultDomain(mDefaultDomainEdit->text()); job->setProperty("email", email); connect(job, &AddressValidationJob::result, this, &IdentityDialog::slotDelayedButtonClicked); job->start(); } bool IdentityDialog::keyMatchesEmailAddress(const GpgME::Key &key, const QString &email_) { if (key.isNull()) { return true; } const QString email = email_.trimmed().toLower(); const auto uids = key.userIDs(); for (const auto &uid : uids) { QString em = QString::fromUtf8(uid.email() ? uid.email() : uid.id()); if (em.isEmpty()) { continue; } if (em[0] == QLatin1Char('<')) { em = em.mid(1, em.length() - 2); } if (em.toLower() == email) { return true; } } return false; } void IdentityDialog::slotDelayedButtonClicked(KJob *job) { const AddressValidationJob *validationJob = qobject_cast(job); // Abort if one of the recipient addresses is invalid if (!validationJob->isValid()) { return; } const QString email = validationJob->property("email").toString(); const GpgME::Key &pgpSigningKey = mPGPSigningKeyRequester->currentKey(); const GpgME::Key &pgpEncryptionKey = mPGPEncryptionKeyRequester->currentKey(); const GpgME::Key &smimeSigningKey = mSMIMESigningKeyRequester->currentKey(); const GpgME::Key &smimeEncryptionKey = mSMIMEEncryptionKeyRequester->currentKey(); QString msg; bool err = false; if (!keyMatchesEmailAddress(pgpSigningKey, email)) { msg = i18n("One of the configured OpenPGP signing keys does not contain " "any user ID with the configured email address for this " "identity (%1).\n" "This might result in warning messages on the receiving side " "when trying to verify signatures made with this configuration.", email); err = true; } else if (!keyMatchesEmailAddress(pgpEncryptionKey, email)) { msg = i18n("One of the configured OpenPGP encryption keys does not contain " "any user ID with the configured email address for this " "identity (%1).", email); err = true; } else if (!keyMatchesEmailAddress(smimeSigningKey, email)) { msg = i18n("One of the configured S/MIME signing certificates does not contain " "the configured email address for this " "identity (%1).\n" "This might result in warning messages on the receiving side " "when trying to verify signatures made with this configuration.", email); err = true; } else if (!keyMatchesEmailAddress(smimeEncryptionKey, email)) { msg = i18n("One of the configured S/MIME encryption certificates does not contain " "the configured email address for this " "identity (%1).", email); err = true; } if (err) { if (KMessageBox::warningContinueCancel(this, msg, i18n("Email Address Not Found in Key/Certificates"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("warn_email_not_in_certificate")) != KMessageBox::Continue) { return; } } if (mSignatureConfigurator->isSignatureEnabled() && mSignatureConfigurator->signatureType() == Signature::FromFile) { QFileInfo file(mSignatureConfigurator->filePath()); if (!file.isReadable()) { KMessageBox::error(this, i18n("The signature file is not valid")); return; } } accept(); } bool IdentityDialog::checkFolderExists(const QString &folderID) { const Akonadi::Collection folder = CommonKernel->collectionFromId(folderID.toLongLong()); return folder.isValid(); } void IdentityDialog::setIdentity(KIdentityManagement::Identity &ident) { setWindowTitle(i18n("Edit Identity \"%1\"", ident.identityName())); // "General" tab: mNameEdit->setText(ident.fullName()); mOrganizationEdit->setText(ident.organization()); mEmailEdit->setText(ident.primaryEmailAddress()); mAliasEdit->insertStringList(ident.emailAliases()); // "Cryptography" tab: mPGPSigningKeyRequester->setDefaultKey(QLatin1String(ident.pgpSigningKey())); mPGPEncryptionKeyRequester->setDefaultKey(QLatin1String(ident.pgpEncryptionKey())); mSMIMESigningKeyRequester->setDefaultKey(QLatin1String(ident.smimeSigningKey())); mSMIMEEncryptionKeyRequester->setDefaultKey(QLatin1String(ident.smimeEncryptionKey())); mPreferredCryptoMessageFormat->setCurrentIndex(format2cb( Kleo::stringToCryptoMessageFormat(ident.preferredCryptoMessageFormat()))); mAutoSign->setChecked(ident.pgpAutoSign()); mAutoEncrypt->setChecked(ident.pgpAutoEncrypt()); // "Advanced" tab: mReplyToEdit->setText(ident.replyToAddr()); mBccEdit->setText(ident.bcc()); mCcEdit->setText(ident.cc()); const int transportId = ident.transport().isEmpty() ? -1 : ident.transport().toInt(); const Transport *transport = TransportManager::self()->transportById(transportId, true); mTransportCheck->setChecked(transportId != -1); mTransportCombo->setEnabled(transportId != -1); if (transport) { mTransportCombo->setCurrentTransport(transport->id()); } mDictionaryCombo->setCurrentByDictionaryName(ident.dictionary()); mSentMailFolderCheck->setChecked(!ident.disabledFcc()); mFccFolderRequester->setEnabled(mSentMailFolderCheck->isChecked()); bool foundNoExistingFolder = false; if (ident.fcc().isEmpty() || !checkFolderExists(ident.fcc())) { foundNoExistingFolder = true; mFccFolderRequester->setIsInvalidFolder(CommonKernel->sentCollectionFolder()); } else { mFccFolderRequester->setCollection(Akonadi::Collection(ident.fcc().toLongLong())); } if (ident.drafts().isEmpty() || !checkFolderExists(ident.drafts())) { foundNoExistingFolder = true; mDraftsFolderRequester->setIsInvalidFolder(CommonKernel->draftsCollectionFolder()); } else { mDraftsFolderRequester->setCollection(Akonadi::Collection(ident.drafts().toLongLong())); } if (ident.templates().isEmpty() || !checkFolderExists(ident.templates())) { foundNoExistingFolder = true; mTemplatesFolderRequester->setIsInvalidFolder(CommonKernel->templatesCollectionFolder()); } else { mTemplatesFolderRequester->setCollection(Akonadi::Collection(ident.templates().toLongLong())); } if (foundNoExistingFolder) { mIdentityInvalidFolder->setErrorMessage(i18n("Some custom folder for identity does not exist (anymore); therefore, default folders will be used.")); } mVcardFilename = ident.vCardFile(); mAutoCorrectionLanguage->setLanguage(ident.autocorrectionLanguage()); updateVcardButton(); if (mVcardFilename.isEmpty()) { mVcardFilename = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1Char('/') + ident.identityName() + QLatin1String(".vcf"); QFileInfo fileInfo(mVcardFilename); QDir().mkpath(fileInfo.absolutePath()); } else { //Convert path. const QString path = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1Char('/') + ident.identityName() + QLatin1String(".vcf"); if (QFileInfo::exists(path) && (mVcardFilename != path)) { mVcardFilename = path; } } mAttachMyVCard->setChecked(ident.attachVcard()); QString defaultDomainName = ident.defaultDomainName(); if (defaultDomainName.isEmpty()) { defaultDomainName = QHostInfo::localHostName(); } mDefaultDomainEdit->setText(defaultDomainName); // "Templates" tab: uint identity = ident.uoid(); QString iid = TemplateParser::TemplatesConfiguration::configIdString(identity); TemplateParser::Templates t(iid); mCustom->setChecked(t.useCustomTemplates()); mWidget->loadFromIdentity(identity); // "Signature" tab: mSignatureConfigurator->setImageLocation(ident); mSignatureConfigurator->setSignature(ident.signature()); mXFaceConfigurator->setXFace(ident.xface()); mXFaceConfigurator->setXFaceEnabled(ident.isXFaceEnabled()); } void IdentityDialog::unregisterSpecialCollection(qint64 colId) { // ID is not enough to unregister a special collection, we need the // resource set as well. auto fetch = new Akonadi::CollectionFetchJob(Akonadi::Collection(colId), Akonadi::CollectionFetchJob::Base, this); connect(fetch, &Akonadi::CollectionFetchJob::collectionsReceived, this, [](const Akonadi::Collection::List &cols) { if (cols.count() != 1) { return; } Akonadi::SpecialMailCollections::self()->unregisterCollection(cols.first()); }); } void IdentityDialog::updateIdentity(KIdentityManagement::Identity &ident) { // "General" tab: ident.setFullName(mNameEdit->text()); ident.setOrganization(mOrganizationEdit->text()); QString email = mEmailEdit->text().trimmed(); ident.setPrimaryEmailAddress(email); const QStringList aliases = mAliasEdit->items(); QStringList result; for (const QString &alias : aliases) { const QString aliasTrimmed = alias.trimmed(); if (aliasTrimmed.isEmpty()) { continue; } if (aliasTrimmed == email) { continue; } result.append(alias); } ident.setEmailAliases(result); // "Cryptography" tab: ident.setPGPSigningKey(mPGPSigningKeyRequester->currentKey().primaryFingerprint()); ident.setPGPEncryptionKey(mPGPEncryptionKeyRequester->currentKey().primaryFingerprint()); ident.setSMIMESigningKey(mSMIMESigningKeyRequester->currentKey().primaryFingerprint()); ident.setSMIMEEncryptionKey(mSMIMEEncryptionKeyRequester->currentKey().primaryFingerprint()); ident.setPreferredCryptoMessageFormat( QLatin1String(Kleo::cryptoMessageFormatToString(cb2format(mPreferredCryptoMessageFormat->currentIndex())))); ident.setPgpAutoSign(mAutoSign->isChecked()); ident.setPgpAutoEncrypt(mAutoEncrypt->isChecked()); // "Advanced" tab: ident.setReplyToAddr(mReplyToEdit->text()); ident.setBcc(mBccEdit->text()); ident.setCc(mCcEdit->text()); ident.setTransport(mTransportCheck->isChecked() ? QString::number(mTransportCombo->currentTransportId()) : QString()); ident.setDictionary(mDictionaryCombo->currentDictionaryName()); ident.setDisabledFcc(!mSentMailFolderCheck->isChecked()); Akonadi::Collection collection = mFccFolderRequester->collection(); if (!ident.fcc().isEmpty()) { unregisterSpecialCollection(ident.fcc().toLongLong()); } if (collection.isValid()) { ident.setFcc(QString::number(collection.id())); Akonadi::EntityDisplayAttribute *attribute = collection.attribute(Akonadi::Collection::AddIfMissing); attribute->setIconName(QStringLiteral("mail-folder-sent")); // It will also start a CollectionModifyJob Akonadi::SpecialMailCollections::self()->registerCollection(Akonadi::SpecialMailCollections::SentMail, collection); } else { ident.setFcc(QString()); } collection = mDraftsFolderRequester->collection(); if (!ident.drafts().isEmpty()) { unregisterSpecialCollection(ident.drafts().toLongLong()); } if (collection.isValid()) { ident.setDrafts(QString::number(collection.id())); Akonadi::EntityDisplayAttribute *attribute = collection.attribute(Akonadi::Collection::AddIfMissing); attribute->setIconName(QStringLiteral("document-properties")); // It will also start a CollectionModifyJob Akonadi::SpecialMailCollections::self()->registerCollection(Akonadi::SpecialMailCollections::Drafts, collection); } else { ident.setDrafts(QString()); } collection = mTemplatesFolderRequester->collection(); if (ident.templates().isEmpty()) { unregisterSpecialCollection(ident.templates().toLongLong()); } if (collection.isValid()) { ident.setTemplates(QString::number(collection.id())); Akonadi::EntityDisplayAttribute *attribute = collection.attribute(Akonadi::Collection::AddIfMissing); attribute->setIconName(QStringLiteral("document-new")); // It will also start a CollectionModifyJob Akonadi::SpecialMailCollections::self()->registerCollection(Akonadi::SpecialMailCollections::Templates, collection); new Akonadi::CollectionModifyJob(collection); } else { ident.setTemplates(QString()); } ident.setVCardFile(mVcardFilename); ident.setAutocorrectionLanguage(mAutoCorrectionLanguage->language()); updateVcardButton(); ident.setAttachVcard(mAttachMyVCard->isChecked()); //Add default ? ident.setDefaultDomainName(mDefaultDomainEdit->text()); // "Templates" tab: uint identity = ident.uoid(); QString iid = TemplateParser::TemplatesConfiguration::configIdString(identity); TemplateParser::Templates t(iid); qCDebug(KMAIL_LOG) << "use custom templates for identity" << identity << ":" << mCustom->isChecked(); t.setUseCustomTemplates(mCustom->isChecked()); t.save(); mWidget->saveToIdentity(identity); // "Signature" tab: ident.setSignature(mSignatureConfigurator->signature()); ident.setXFace(mXFaceConfigurator->xface()); ident.setXFaceEnabled(mXFaceConfigurator->isXFaceEnabled()); } void IdentityDialog::slotEditVcard() { if (QFileInfo::exists(mVcardFilename)) { editVcard(mVcardFilename); } else { if (!MailCommon::Kernel::self()->kernelIsRegistered()) { return; } KIdentityManagement::IdentityManager *manager = KernelIf->identityManager(); QPointer dlg = new IdentityAddVcardDialog(manager->shadowIdentities(), this); if (dlg->exec()) { IdentityAddVcardDialog::DuplicateMode mode = dlg->duplicateMode(); switch (mode) { case IdentityAddVcardDialog::Empty: editVcard(mVcardFilename); break; case IdentityAddVcardDialog::ExistingEntry: { KIdentityManagement::Identity ident = manager->modifyIdentityForName(dlg->duplicateVcardFromIdentity()); const QString filename = ident.vCardFile(); if (!filename.isEmpty()) { QFile::copy(filename, mVcardFilename); } editVcard(mVcardFilename); break; } case IdentityAddVcardDialog::FromExistingVCard: { const QString filename = dlg->existingVCard().path(); if (!filename.isEmpty()) { mVcardFilename = filename; } editVcard(mVcardFilename); break; } } } delete dlg; } } void IdentityDialog::editVcard(const QString &filename) { QPointer dlg = new IdentityEditVcardDialog(filename, this); connect(dlg.data(), &IdentityEditVcardDialog::vcardRemoved, this, &IdentityDialog::slotVCardRemoved); if (dlg->exec()) { mVcardFilename = dlg->saveVcard(); } updateVcardButton(); delete dlg; } void IdentityDialog::slotVCardRemoved() { mVcardFilename.clear(); } void IdentityDialog::updateVcardButton() { if (mVcardFilename.isEmpty() || !QFileInfo::exists(mVcardFilename)) { mEditVCard->setText(i18n("Create...")); } else { mEditVCard->setText(i18n("Edit...")); } } } #include "identitydialog.moc" diff --git a/src/identity/xfaceconfigurator.cpp b/src/identity/xfaceconfigurator.cpp index 2af77b14f..eebd07069 100644 --- a/src/identity/xfaceconfigurator.cpp +++ b/src/identity/xfaceconfigurator.cpp @@ -1,288 +1,288 @@ /* This file is part of KMail. Copyright (c) 2004 Jakob Schr�er This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "xfaceconfigurator.h" #include #include #include #include "kpimtextedit/plaintexteditor.h" #include "kpimtextedit/plaintexteditorwidget.h" #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include using namespace KContacts; using namespace KIO; using namespace KMail; using namespace MessageViewer; using namespace KMail; XFaceConfigurator::XFaceConfigurator(QWidget *parent) : QWidget(parent) { QVBoxLayout *vlay = new QVBoxLayout(this); vlay->setObjectName(QStringLiteral("main layout")); QHBoxLayout *hlay = new QHBoxLayout(); vlay->addLayout(hlay); // "enable X-Face" checkbox: mEnableCheck = new QCheckBox(i18n("&Send picture with every message"), this); mEnableCheck->setWhatsThis( i18n("Check this box if you want KMail to add a so-called X-Face header to messages " "written with this identity. An X-Face is a small (48x48 pixels) black and " "white image that some mail clients are able to display.")); hlay->addWidget(mEnableCheck, Qt::AlignLeft | Qt::AlignVCenter); mXFaceLabel = new QLabel(this); mXFaceLabel->setWhatsThis( i18n("This is a preview of the picture selected/entered below.")); mXFaceLabel->setFixedSize(48, 48); mXFaceLabel->setFrameShape(QFrame::Box); hlay->addWidget(mXFaceLabel); // label1 = new QLabel( "X-Face:", this ); // vlay->addWidget( label1 ); // "obtain X-Face from" combo and label: hlay = new QHBoxLayout(); // inherits spacing vlay->addLayout(hlay); QComboBox *sourceCombo = new QComboBox(this); sourceCombo->setWhatsThis( i18n("Click on the widgets below to obtain help on the input methods.")); sourceCombo->setEnabled(false); // since !mEnableCheck->isChecked() sourceCombo->addItems(QStringList() << i18nc("continuation of \"obtain picture from\"", "External Source") << i18nc("continuation of \"obtain picture from\"", "Input Field Below")); QLabel *label = new QLabel(i18n("Obtain pic&ture from:"), this); label->setBuddy(sourceCombo); label->setEnabled(false); // since !mEnableCheck->isChecked() hlay->addWidget(label); hlay->addWidget(sourceCombo, 1); // widget stack that is controlled by the source combo: QStackedWidget *widgetStack = new QStackedWidget(this); widgetStack->setEnabled(false); // since !mEnableCheck->isChecked() vlay->addWidget(widgetStack, 1); connect(sourceCombo, qOverload(&QComboBox::highlighted), widgetStack, &QStackedWidget::setCurrentIndex); connect(sourceCombo, qOverload(&QComboBox::activated), widgetStack, &QStackedWidget::setCurrentIndex); connect(mEnableCheck, &QCheckBox::toggled, sourceCombo, &QComboBox::setEnabled); connect(mEnableCheck, &QCheckBox::toggled, widgetStack, &QStackedWidget::setEnabled); connect(mEnableCheck, &QCheckBox::toggled, label, &QLabel::setEnabled); // The focus might be still in the widget that is disabled connect(mEnableCheck, &QAbstractButton::clicked, mEnableCheck, qOverload<>(&QWidget::setFocus)); int pageno = 0; // page 0: create X-Face from image file or address book entry QWidget *page = new QWidget(widgetStack); widgetStack->insertWidget(pageno, page); // force sequential numbers (play safe) QVBoxLayout *page_vlay = new QVBoxLayout(page); page_vlay->setContentsMargins(0, 0, 0, 0); hlay = new QHBoxLayout(); // inherits spacing ??? FIXME really? page_vlay->addLayout(hlay); QPushButton *mFromFileBtn = new QPushButton(i18n("Select File..."), page); mFromFileBtn->setWhatsThis( i18n("Use this to select an image file to create the picture from. " "The image should be of high contrast and nearly quadratic shape. " "A light background helps improve the result.")); mFromFileBtn->setAutoDefault(false); page_vlay->addWidget(mFromFileBtn, 1); connect(mFromFileBtn, &QPushButton::released, this, &XFaceConfigurator::slotSelectFile); QPushButton *mFromAddrbkBtn = new QPushButton(i18n("Set From Address Book"), page); mFromAddrbkBtn->setWhatsThis( i18n("You can use a scaled-down version of the picture " "you have set in your address book entry.")); mFromAddrbkBtn->setAutoDefault(false); page_vlay->addWidget(mFromAddrbkBtn, 1); connect(mFromAddrbkBtn, &QPushButton::released, this, &XFaceConfigurator::slotSelectFromAddressbook); QLabel *label1 = new QLabel(i18n("KMail can send a small (48x48 pixels), low-quality, " "monochrome picture with every message. " "For example, this could be a picture of you or a glyph. " "It is shown in the recipient's mail client (if supported)."), page); label1->setAlignment(Qt::AlignVCenter); label1->setWordWrap(true); page_vlay->addWidget(label1); page_vlay->addStretch(); widgetStack->setCurrentIndex(0); // since sourceCombo->currentItem() == 0 // page 1: input field for direct entering ++pageno; page = new QWidget(widgetStack); widgetStack->insertWidget(pageno, page); page_vlay = new QVBoxLayout(page); page_vlay->setContentsMargins(0, 0, 0, 0); mTextEdit = new KPIMTextEdit::PlainTextEditorWidget(page); mTextEdit->editor()->setSpellCheckingSupport(false); page_vlay->addWidget(mTextEdit); mTextEdit->editor()->setWhatsThis(i18n("Use this field to enter an arbitrary X-Face string.")); mTextEdit->editor()->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); mTextEdit->editor()->setWordWrapMode(QTextOption::WrapAnywhere); mTextEdit->editor()->setSearchSupport(false); QLabel *label2 = new QLabel(i18n("Examples are available at " "https://ace.home.xs4all.nl/X-Faces/."), page); label2->setOpenExternalLinks(true); label2->setTextInteractionFlags(Qt::TextBrowserInteraction); page_vlay->addWidget(label2); connect(mTextEdit->editor(), &KPIMTextEdit::PlainTextEditor::textChanged, this, &XFaceConfigurator::slotUpdateXFace); } XFaceConfigurator::~XFaceConfigurator() { } bool XFaceConfigurator::isXFaceEnabled() const { return mEnableCheck->isChecked(); } void XFaceConfigurator::setXFaceEnabled(bool enable) { mEnableCheck->setChecked(enable); } QString XFaceConfigurator::xface() const { return mTextEdit->editor()->toPlainText(); } void XFaceConfigurator::setXFace(const QString &text) { mTextEdit->editor()->setPlainText(text); } void XFaceConfigurator::setXfaceFromFile(const QUrl &url) { auto job = KIO::storedGet(url); KJobWidgets::setWindow(job, this); if (job->exec()) { KXFace xf; mTextEdit->editor()->setPlainText(xf.fromImage(QImage::fromData(job->data()))); } else { KMessageBox::error(this, job->errorString()); } } void XFaceConfigurator::slotSelectFile() { QString filter; const QList supportedImage = QImageReader::supportedImageFormats(); for (const QByteArray &ba : supportedImage) { if (!filter.isEmpty()) { filter += QLatin1Char(' '); } filter += QLatin1String("*.") + QString::fromLatin1(ba); } filter = QStringLiteral("%1 (%2)").arg(i18n("Image"), filter); const QUrl url = QFileDialog::getOpenFileUrl(this, QString(), QUrl(), filter); if (!url.isEmpty()) { setXfaceFromFile(url); } } void XFaceConfigurator::slotSelectFromAddressbook() { using namespace KIdentityManagement; IdentityManager manager(true); const Identity defaultIdentity = manager.defaultIdentity(); const QString email = defaultIdentity.primaryEmailAddress().toLower(); Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob(this); job->setLimit(1); job->setQuery(Akonadi::ContactSearchJob::Email, email, Akonadi::ContactSearchJob::ExactMatch); connect(job, &Akonadi::ContactSearchJob::result, this, &XFaceConfigurator::slotDelayedSelectFromAddressbook); } void XFaceConfigurator::slotDelayedSelectFromAddressbook(KJob *job) { const Akonadi::ContactSearchJob *searchJob = qobject_cast(job); if (searchJob->contacts().isEmpty()) { KMessageBox::information(this, i18n("You do not have your own contact defined in the address book."), i18n("No Picture")); return; } const Addressee contact = searchJob->contacts().at(0); if (contact.photo().isIntern()) { const QImage photo = contact.photo().data(); if (!photo.isNull()) { KXFace xf; mTextEdit->editor()->setPlainText(xf.fromImage(photo)); } else { KMessageBox::information(this, i18n("No picture set for your address book entry."), i18n("No Picture")); } } else { const QUrl url(contact.photo().url()); if (!url.isEmpty()) { setXfaceFromFile(url); } else { KMessageBox::information(this, i18n("No picture set for your address book entry."), i18n("No Picture")); } } } void XFaceConfigurator::slotUpdateXFace() { QString str = mTextEdit->editor()->toPlainText(); if (!str.isEmpty()) { if (str.startsWith(QLatin1String("x-face:"), Qt::CaseInsensitive)) { str.remove(QStringLiteral("x-face:"), Qt::CaseInsensitive); mTextEdit->editor()->setPlainText(str); } KXFace xf; const QPixmap p = QPixmap::fromImage(xf.toImage(str)); mXFaceLabel->setPixmap(p); } else { mXFaceLabel->clear(); } } diff --git a/src/job/addressvalidationjob.cpp b/src/job/addressvalidationjob.cpp index 04d2bbce7..533780fa3 100644 --- a/src/job/addressvalidationjob.cpp +++ b/src/job/addressvalidationjob.cpp @@ -1,108 +1,108 @@ /* * This file is part of KMail. * * Copyright (c) 2010 KDAB * * Author: Tobias Koenig * * 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 "addressvalidationjob.h" #include using MessageComposer::AliasesExpandJob; #include "messagecomposer/messagecomposersettings.h" #include -#include +#include #include AddressValidationJob::AddressValidationJob(const QString &emailAddresses, QWidget *parentWidget, QObject *parent) : KJob(parent) , mEmailAddresses(emailAddresses) , mParentWidget(parentWidget) { } AddressValidationJob::~AddressValidationJob() { } void AddressValidationJob::setDefaultDomain(const QString &domainName) { mDomainDefaultName = domainName; } void AddressValidationJob::start() { AliasesExpandJob *job = new AliasesExpandJob(mEmailAddresses, mDomainDefaultName, this); connect(job, &AliasesExpandJob::result, this, &AddressValidationJob::slotAliasExpansionDone); job->start(); } bool AddressValidationJob::isValid() const { return mIsValid; } void AddressValidationJob::slotAliasExpansionDone(KJob *job) { mIsValid = true; if (job->error()) { setError(job->error()); setErrorText(job->errorText()); mIsValid = false; emitResult(); return; } const AliasesExpandJob *expandJob = qobject_cast(job); const QStringList emptyDistributionLists = expandJob->emptyDistributionLists(); QString brokenAddress; const KEmailAddress::EmailParseResult errorCode = KEmailAddress::isValidAddressList(expandJob->addresses(), brokenAddress); if (!emptyDistributionLists.isEmpty()) { QString errorMsg; const int numberOfDistributionList(emptyDistributionLists.count()); QString listOfDistributionList; for (int i = 0; i < numberOfDistributionList; ++i) { if (i != 0) { listOfDistributionList.append(QLatin1String(", ")); } listOfDistributionList.append(QStringLiteral("\"%1\"").arg(emptyDistributionLists.at(i))); } errorMsg = i18np("Distribution list %2 is empty, it cannot be used.", "Distribution lists %2 are empty, they cannot be used.", numberOfDistributionList, listOfDistributionList); KMessageBox::sorry(mParentWidget, errorMsg, i18n("Invalid Email Address")); mIsValid = false; } else { if (!(errorCode == KEmailAddress::AddressOk || errorCode == KEmailAddress::AddressEmpty)) { const QString errorMsg(QLatin1String("

") + brokenAddress +QLatin1String("

") +KEmailAddress::emailParseResultToString(errorCode) +QLatin1String("

")); KMessageBox::sorry(mParentWidget, errorMsg, i18n("Invalid Email Address")); mIsValid = false; } } emitResult(); } diff --git a/src/job/addressvalidationjob.h b/src/job/addressvalidationjob.h index 399b4184b..b3d0da58e 100644 --- a/src/job/addressvalidationjob.h +++ b/src/job/addressvalidationjob.h @@ -1,50 +1,50 @@ /* * This file is part of KMail. * * Copyright (c) 2010 KDAB * * Author: Tobias Koenig * * 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 ADDRESSVALIDATIONJOB_H #define ADDRESSVALIDATIONJOB_H -#include +#include class AddressValidationJob : public KJob { Q_OBJECT public: explicit AddressValidationJob(const QString &emailAddresses, QWidget *parentWidget, QObject *parent = nullptr); ~AddressValidationJob() override; void start() override; Q_REQUIRED_RESULT bool isValid() const; void setDefaultDomain(const QString &domainName); private: void slotAliasExpansionDone(KJob *); QString mEmailAddresses; QString mDomainDefaultName; bool mIsValid = false; QWidget *mParentWidget = nullptr; }; #endif diff --git a/src/kcm_kmail.cpp b/src/kcm_kmail.cpp index bf2fdff28..1a78db185 100644 --- a/src/kcm_kmail.cpp +++ b/src/kcm_kmail.cpp @@ -1,97 +1,97 @@ /* * kmail: KDE mail client * Copyright (C) 2000 Espen Sand, espen@kde.org * Copyright (C) 2001-2003 Marc Mutz, mutz@kde.org * Contains code segments and ideas from earlier kmail dialog code. * * 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. * */ // This must be first #include "configuredialog/configuredialog.h" #include "configuredialog/configuredialog_p.h" #include "configuredialog/configuremiscpage.h" #include "configuredialog/configuresecuritypage.h" #include "configuredialog/configurecomposerpage.h" #include "configuredialog/configureappearancepage.h" #include "configuredialog/configureaccountpage.h" #include "configuredialog/configurepluginpage.h" #include "identity/identitypage.h" -#include +#include //---------------------------- // KCM stuff //---------------------------- extern "C" { Q_DECL_EXPORT KCModule *create_kmail_config_misc(QWidget *parent, const char *) { MiscPage *page = new MiscPage(parent); page->setObjectName(QStringLiteral("kcmkmail_config_misc")); return page; } } extern "C" { Q_DECL_EXPORT KCModule *create_kmail_config_appearance(QWidget *parent, const char *) { AppearancePage *page = new AppearancePage(parent); page->setObjectName(QStringLiteral("kcmkmail_config_appearance")); return page; } } extern "C" { Q_DECL_EXPORT KCModule *create_kmail_config_composer(QWidget *parent, const char *) { ComposerPage *page = new ComposerPage(parent); page->setObjectName(QStringLiteral("kcmkmail_config_composer")); return page; } } extern "C" { Q_DECL_EXPORT KCModule *create_kmail_config_accounts(QWidget *parent, const char *) { AccountsPage *page = new AccountsPage(parent); page->setObjectName(QStringLiteral("kcmkmail_config_accounts")); return page; } } extern "C" { Q_DECL_EXPORT KCModule *create_kmail_config_security(QWidget *parent, const char *) { SecurityPage *page = new SecurityPage(parent); page->setObjectName(QStringLiteral("kcmkmail_config_security")); return page; } } extern "C" { Q_DECL_EXPORT KCModule *create_kmail_config_plugins(QWidget *parent, const char *) { ConfigurePluginPage *page = new ConfigurePluginPage(parent); page->setObjectName(QStringLiteral("kcmkmail_config_plugins")); return page; } } diff --git a/src/kmail_part.cpp b/src/kmail_part.cpp index e322d03bf..041675852 100644 --- a/src/kmail_part.cpp +++ b/src/kmail_part.cpp @@ -1,161 +1,161 @@ /* This file is part of KMail. Copyright (c) 2002-2003 Don Sanders , Copyright (c) 2003 Zack Rusin , Based on the work of Cornelius Schumacher 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. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "kmail_part.h" #include "kmmainwin.h" #include "kmmainwidget.h" #include "aboutdata.h" #include #include #include #include -#include +#include #include "kmail_debug.h" #include #include #include #include #include #include "MailCommon/FolderTreeView" #include "tag/tagactionmanager.h" #include "foldershortcutactionmanager.h" #include "kmmigrateapplication.h" #include #include "messageviewer/config-messageviewer.h" #include K_PLUGIN_FACTORY(KMailFactory, registerPlugin(); ) using namespace KMail; KMailPart::KMailPart(QWidget *parentWidget, QObject *parent, const QVariantList &) : KParts::ReadOnlyPart(parent) , mParentWidget(parentWidget) { setComponentName(QStringLiteral("kmail2"), i18n("KMail2")); KMMigrateApplication migrate; migrate.migrate(); //local, do the init KMKernel *mKMailKernel = new KMKernel(); mKMailKernel->init(); mKMailKernel->setXmlGuiInstanceName(QStringLiteral("kmail2")); // and session management mKMailKernel->doSessionManagement(); // any dead letters? mKMailKernel->recoverDeadLetters(); kmkernel->setupDBus(); // Ok. We are ready for D-Bus requests. (void)new KmailpartAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/KMailPart"), this); // create a canvas to insert our widget QWidget *canvas = new QWidget(parentWidget); canvas->setFocusPolicy(Qt::ClickFocus); canvas->setObjectName(QStringLiteral("canvas")); setWidget(canvas); mainWidget = new KMMainWidget(canvas, this, actionCollection(), KSharedConfig::openConfig()); mainWidget->setObjectName(QStringLiteral("partmainwidget")); QVBoxLayout *topLayout = new QVBoxLayout(canvas); topLayout->addWidget(mainWidget); topLayout->setContentsMargins(0, 0, 0, 0); mainWidget->setFocusPolicy(Qt::ClickFocus); KParts::StatusBarExtension *statusBar = new KParts::StatusBarExtension(this); statusBar->addStatusBarItem(mainWidget->vacationScriptIndicator(), 2, false); statusBar->addStatusBarItem(mainWidget->zoomLabelIndicator(), 3, false); #ifdef USE_DKIM_CHECKER statusBar->addStatusBarItem(mainWidget->dkimWidgetInfo(), 4, false); #endif setXMLFile(QStringLiteral("kmail_part.rc"), true); KSettings::Dispatcher::registerComponent(QStringLiteral("kmail2"), mKMailKernel, "slotConfigChanged"); connect(mainWidget, &KMMainWidget::captionChangeRequest, this, &KMailPart::setWindowCaption); } KMailPart::~KMailPart() { qCDebug(KMAIL_LOG) << "Closing last KMMainWin: stopping mail check"; // Running KIO jobs prevent kapp from exiting, so we need to kill them // if they are only about checking mail (not important stuff like moving messages) mainWidget->destruct(); kmkernel->cleanup(); delete kmkernel; } void KMailPart::updateQuickSearchText() { mainWidget->updateQuickSearchLineText(); } bool KMailPart::openFile() { mainWidget->show(); return true; } //----------------------------------------------------------------------------- void KMailPart::guiActivateEvent(KParts::GUIActivateEvent *e) { KParts::ReadOnlyPart::guiActivateEvent(e); if (e->activated()) { mainWidget->initializeFilterActions(true); mainWidget->tagActionManager()->createActions(); mainWidget->folderShortcutActionManager()->createActions(); mainWidget->populateMessageListStatusFilterCombo(); mainWidget->initializePluginActions(); const QString title = mainWidget->fullCollectionPath(); if (!title.isEmpty()) { Q_EMIT setWindowCaption(title); } } } void KMailPart::exit() { delete this; } QWidget *KMailPart::parentWidget() const { return mParentWidget; } void KMailPart::save() { /*TODO*/ } #include "kmail_part.moc" diff --git a/src/kmkernel.cpp b/src/kmkernel.cpp index 7b493db4c..07f46bac7 100644 --- a/src/kmkernel.cpp +++ b/src/kmkernel.cpp @@ -1,1953 +1,1953 @@ /* */ #include "kmkernel.h" #include "settings/kmailsettings.h" #include "libkdepim/broadcaststatus.h" #include "job/opencomposerjob.h" #include "job/newmessagejob.h" #include "job/opencomposerhiddenjob.h" #include "job/fillcomposerjob.h" #include #include using KPIM::BroadcastStatus; #include "kmmainwin.h" #include "editor/composer.h" #include "kmreadermainwin.h" #include "undostack.h" #include "kmmainwidget.h" #include "search/checkindexingmanager.h" #include "libkdepim/recentaddresses.h" using KPIM::RecentAddresses; #include "configuredialog/configuredialog.h" #include "kmcommands.h" #include "unityservicemanager.h" #include #include "mailcommon/mailutil.h" #include "pop3settings.h" #include "MailCommon/FolderTreeView" #include "MailCommon/KMFilterDialog" #include "mailcommonsettings_base.h" #include "mailfilteragentinterface.h" #include "PimCommon/PimUtil" #include "folderarchive/folderarchivemanager.h" #include "sieveimapinterface/kmailsieveimapinstanceinterface.h" // kdepim includes #include "kmail-version.h" // kdepimlibs includes #include #include #include #include #include #include #include "mailserviceimpl.h" using KMail::MailServiceImpl; #include "mailcommon/jobscheduler.h" #include #include "messagelistsettings.h" #include "gravatarsettings.h" #include "messagelist/messagelistutil.h" #include "messageviewer/messageviewersettings.h" #include "MessageComposer/AkonadiSender" #include "messagecomposer/messagecomposersettings.h" #include "MessageComposer/MessageHelper" #include "MessageComposer/MessageComposerSettings" #include "PimCommon/PimCommonSettings" #include "PimCommon/AutoCorrection" #include #include "globalsettings_templateparser.h" #include "TemplateParser/TemplatesUtil" #include "mailcommon/foldersettings.h" #include "editor/codec/codecmanager.h" -#include -#include +#include +#include #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 #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include "kmailinterface.h" #include "mailcommon/foldercollectionmonitor.h" #include "imapresourcesettings.h" #include "util.h" #include "MailCommon/MailKernel" #include "searchdialog/searchdescriptionattribute.h" #include "kmail_options.h" using namespace MailCommon; static KMKernel *mySelf = nullptr; static bool s_askingToGoOnline = false; /********************************************************************/ /* Constructor and destructor */ /********************************************************************/ KMKernel::KMKernel(QObject *parent) : QObject(parent) { //Initialize kmail sieveimap interface KSieveUi::SieveImapInstanceInterfaceManager::self()->setSieveImapInstanceInterface(new KMailSieveImapInstanceInterface); mDebug = !qEnvironmentVariableIsEmpty("KDEPIM_DEBUGGING"); mSystemNetworkStatus = PimCommon::NetworkManager::self()->networkConfigureManager()->isOnline(); Akonadi::AttributeFactory::registerAttribute(); QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kmail")); qCDebug(KMAIL_LOG) << "Starting up..."; mySelf = this; the_firstInstance = true; the_undoStack = nullptr; the_msgSender = nullptr; mFilterEditDialog = nullptr; // make sure that we check for config updates before doing anything else KMKernel::config(); // this shares the kmailrc parsing too (via KSharedConfig), and reads values from it // so better do it here, than in some code where changing the group of config() // would be unexpected KMailSettings::self(); mJobScheduler = new JobScheduler(this); mAutoCorrection = new PimCommon::AutoCorrection(); KMime::setUseOutlookAttachmentEncoding(MessageComposer::MessageComposerSettings::self()->outlookCompatibleAttachments()); // cberzan: this crap moved to CodecManager ====================== mNetCodec = QTextCodec::codecForLocale(); // In the case of Japan. Japanese locale name is "eucjp" but // The Japanese mail systems normally used "iso-2022-jp" of locale name. // We want to change locale name from eucjp to iso-2022-jp at KMail only. // (Introduction to i18n, 6.6 Limit of Locale technology): // EUC-JP is the de-facto standard for UNIX systems, ISO 2022-JP // is the standard for Internet, and Shift-JIS is the encoding // for Windows and Macintosh. const QByteArray netCodecLower = mNetCodec->name().toLower(); if (netCodecLower == "eucjp" #if defined Q_OS_WIN || defined Q_OS_MACX || netCodecLower == "shift-jis" // OK? #endif ) { mNetCodec = QTextCodec::codecForName("jis7"); } // until here ================================================ Akonadi::Session *session = new Akonadi::Session("KMail Kernel ETM", this); mFolderCollectionMonitor = new FolderCollectionMonitor(session, this); connect(mFolderCollectionMonitor->monitor(), &Akonadi::Monitor::collectionRemoved, this, &KMKernel::slotCollectionRemoved); mEntityTreeModel = new Akonadi::EntityTreeModel(folderCollectionMonitor(), this); mEntityTreeModel->setListFilter(Akonadi::CollectionFetchScope::Enabled); mEntityTreeModel->setItemPopulationStrategy(Akonadi::EntityTreeModel::LazyPopulation); mCollectionModel = new Akonadi::EntityMimeTypeFilterModel(this); mCollectionModel->setSourceModel(mEntityTreeModel); mCollectionModel->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType()); mCollectionModel->setHeaderGroup(Akonadi::EntityTreeModel::CollectionTreeHeaders); mCollectionModel->setDynamicSortFilter(true); mCollectionModel->setSortCaseSensitivity(Qt::CaseInsensitive); connect(folderCollectionMonitor(), qOverload &>(&Akonadi::ChangeRecorder::collectionChanged), this, &KMKernel::slotCollectionChanged); connect(MailTransport::TransportManager::self(), &MailTransport::TransportManager::transportRemoved, this, &KMKernel::transportRemoved); connect(MailTransport::TransportManager::self(), &MailTransport::TransportManager::transportRenamed, this, &KMKernel::transportRenamed); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/MailDispatcherAgent"), QStringLiteral("org.freedesktop.Akonadi.MailDispatcherAgent"), QStringLiteral( "itemDispatchStarted"), this, SLOT(itemDispatchStarted())); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceStatusChanged, this, &KMKernel::instanceStatusChanged); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceError, this, &KMKernel::slotInstanceError); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceWarning, this, &KMKernel::slotInstanceWarning); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceRemoved, this, &KMKernel::slotInstanceRemoved); connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceAdded, this, &KMKernel::slotInstanceAdded); connect(PimCommon::NetworkManager::self()->networkConfigureManager(), &QNetworkConfigurationManager::onlineStateChanged, this, &KMKernel::slotSystemNetworkStatusChanged); connect(KPIM::ProgressManager::instance(), &KPIM::ProgressManager::progressItemCompleted, this, &KMKernel::slotProgressItemCompletedOrCanceled); connect(KPIM::ProgressManager::instance(), &KPIM::ProgressManager::progressItemCanceled, this, &KMKernel::slotProgressItemCompletedOrCanceled); connect(identityManager(), &KIdentityManagement::IdentityManager::deleted, this, &KMKernel::slotDeleteIdentity); CommonKernel->registerKernelIf(this); CommonKernel->registerSettingsIf(this); CommonKernel->registerFilterIf(this); mFolderArchiveManager = new FolderArchiveManager(this); mIndexedItems = new Akonadi::Search::PIM::IndexedItems(this); mCheckIndexingManager = new CheckIndexingManager(mIndexedItems, this); mUnityServiceManager = new KMail::UnityServiceManager(this); } KMKernel::~KMKernel() { delete mMailService; mMailService = nullptr; stopAgentInstance(); saveConfig(); delete mAutoCorrection; delete mMailCommonSettings; mySelf = nullptr; } Akonadi::ChangeRecorder *KMKernel::folderCollectionMonitor() const { return mFolderCollectionMonitor->monitor(); } Akonadi::EntityTreeModel *KMKernel::entityTreeModel() const { return mEntityTreeModel; } Akonadi::EntityMimeTypeFilterModel *KMKernel::collectionModel() const { return mCollectionModel; } void KMKernel::setupDBus() { (void)new KmailAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/KMail"), this); mMailService = new MailServiceImpl(); } static QUrl makeAbsoluteUrl(const QString &str, const QString &cwd) { return QUrl::fromUserInput(str, cwd, QUrl::AssumeLocalFile); } bool KMKernel::handleCommandLine(bool noArgsOpensReader, const QStringList &args, const QString &workingDir) { QString to, cc, bcc, subj, body, inReplyTo, replyTo; QStringList customHeaders; QUrl messageFile; QList attachURLs; QString identity; bool startInTray = false; bool mailto = false; bool checkMail = false; bool viewOnly = false; bool calledWithSession = false; // for ignoring '-session foo' // process args: QCommandLineParser parser; kmail_options(&parser); QStringList newargs; bool addAttachmentAttribute = false; for (const QString &argument : qAsConst(args)) { if (argument == QLatin1String("--attach")) { addAttachmentAttribute = true; } else { if (argument.startsWith(QLatin1String("--"))) { addAttachmentAttribute = false; } if (argument.contains(QLatin1Char('@')) || argument.startsWith(QLatin1String("mailto:"))) { //address mustn't be trade as a attachment addAttachmentAttribute = false; } if (addAttachmentAttribute) { newargs.append(QStringLiteral("--attach")); newargs.append(argument); } else { newargs.append(argument); } } } parser.process(newargs); if (parser.isSet(QStringLiteral("subject"))) { subj = parser.value(QStringLiteral("subject")); // if kmail is called with 'kmail -session abc' then this doesn't mean // that the user wants to send a message with subject "ession" but // (most likely) that the user clicked on KMail's system tray applet // which results in KMKernel::raise() calling "kmail kmail newInstance" // via D-Bus which apparently executes the application with the original // command line arguments and those include "-session ..." if // kmail/kontact was restored by session management if (subj == QLatin1String("ession")) { subj.clear(); calledWithSession = true; } else { mailto = true; } } if (parser.isSet(QStringLiteral("cc"))) { mailto = true; cc = parser.value(QStringLiteral("cc")); } if (parser.isSet(QStringLiteral("bcc"))) { mailto = true; bcc = parser.value(QStringLiteral("bcc")); } if (parser.isSet(QStringLiteral("replyTo"))) { mailto = true; replyTo = parser.value(QStringLiteral("replyTo")); } if (parser.isSet(QStringLiteral("msg"))) { mailto = true; const QString file = parser.value(QStringLiteral("msg")); messageFile = makeAbsoluteUrl(file, workingDir); } if (parser.isSet(QStringLiteral("body"))) { mailto = true; body = parser.value(QStringLiteral("body")); } const QStringList attachList = parser.values(QStringLiteral("attach")); if (!attachList.isEmpty()) { mailto = true; QStringList::ConstIterator end = attachList.constEnd(); for (QStringList::ConstIterator it = attachList.constBegin(); it != end; ++it) { if (!(*it).isEmpty()) { if ((*it) != QLatin1String("--")) { attachURLs.append(makeAbsoluteUrl(*it, workingDir)); } } } } customHeaders = parser.values(QStringLiteral("header")); if (parser.isSet(QStringLiteral("composer"))) { mailto = true; } if (parser.isSet(QStringLiteral("check"))) { checkMail = true; } if (parser.isSet(QStringLiteral("startintray"))) { KMailSettings::self()->setSystemTrayEnabled(true); startInTray = true; } if (parser.isSet(QStringLiteral("identity"))) { identity = parser.value(QStringLiteral("identity")); } if (parser.isSet(QStringLiteral("view"))) { viewOnly = true; const QString filename = parser.value(QStringLiteral("view")); messageFile = QUrl::fromUserInput(filename, workingDir); } if (!calledWithSession) { // only read additional command line arguments if kmail/kontact is // not called with "-session foo" for (const QString &arg : parser.positionalArguments()) { if (arg.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) { const QUrl urlDecoded(QUrl::fromPercentEncoding(arg.toUtf8())); const QVector > values = MessageCore::StringUtil::parseMailtoUrl(urlDecoded); QString previousKey; for (int i = 0; i < values.count(); ++i) { const QPair element = values.at(i); const QString key = element.first.toLower(); if (key == QLatin1String("to")) { if (!element.second.isEmpty()) { to += element.second + QStringLiteral(", "); } previousKey.clear(); } else if (key == QLatin1String("cc")) { if (!element.second.isEmpty()) { cc += element.second + QStringLiteral(", "); } previousKey.clear(); } else if (key == QLatin1String("bcc")) { if (!element.second.isEmpty()) { bcc += element.second + QStringLiteral(", "); } previousKey.clear(); } else if (key == QLatin1String("subject")) { subj = element.second; previousKey.clear(); } else if (key == QLatin1String("body")) { body = element.second; previousKey = key; } else if (key == QLatin1String("in-reply-to")) { inReplyTo = element.second; previousKey.clear(); } else if (key == QLatin1String("attachment") || key == QLatin1String("attach")) { if (!element.second.isEmpty()) { attachURLs << makeAbsoluteUrl(element.second, workingDir); } previousKey.clear(); } else { qCWarning(KMAIL_LOG) << "unknown key" << key; //Workaround: https://bugs.kde.org/show_bug.cgi?id=390939 //QMap parseMailtoUrl(const QUrl &url) parses correctly url //But if we have a "&" unknown key we lost it. if (previousKey == QLatin1String("body")) { body += QLatin1Char('&') + key + QLatin1Char('=') + element.second; } //Don't clear previous key. } } } else { QUrl url(arg); if (url.isValid() && !url.scheme().isEmpty()) { attachURLs += url; } else { to += arg + QStringLiteral(", "); } } mailto = true; } if (!to.isEmpty()) { // cut off the superfluous trailing ", " to.chop(2); } } if (!noArgsOpensReader && !mailto && !checkMail && !viewOnly) { return false; } if (viewOnly) { viewMessage(messageFile); } else { action(mailto, checkMail, startInTray, to, cc, bcc, subj, body, messageFile, attachURLs, customHeaders, replyTo, inReplyTo, identity); } return true; } /********************************************************************/ /* D-Bus-callable, and command line actions */ /********************************************************************/ void KMKernel::checkMail() //might create a new reader but won't show!! { if (!kmkernel->askToGoOnline()) { return; } const QString resourceGroupPattern(QStringLiteral("Resource %1")); const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (Akonadi::AgentInstance type : lst) { const QString id = type.identifier(); KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(id)); if (group.readEntry("IncludeInManualChecks", true)) { if (!type.isOnline()) { type.setIsOnline(true); } if (mResourcesBeingChecked.isEmpty()) { qCDebug(KMAIL_LOG) << "Starting manual mail check"; Q_EMIT startCheckMail(); } if (!mResourcesBeingChecked.contains(id)) { mResourcesBeingChecked.append(id); } type.synchronize(); } } } void KMKernel::openReader() { openReader(false, false); } QStringList KMKernel::accounts() const { QStringList accountLst; const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); accountLst.reserve(lst.count()); for (const Akonadi::AgentInstance &type : lst) { // Explicitly make a copy, as we're not changing values of the list but only // the local copy which is passed to action. accountLst << type.identifier(); } return accountLst; } void KMKernel::checkAccount(const QString &account) //might create a new reader but won't show!! { if (account.isEmpty()) { checkMail(); } else { Akonadi::AgentInstance agent = Akonadi::AgentManager::self()->instance(account); if (agent.isValid()) { agent.synchronize(); } else { qCDebug(KMAIL_LOG) << "- account with name '" << account << "' not found"; } } } void KMKernel::openReader(bool onlyCheck, bool startInTray) { KMainWindow *ktmw = nullptr; const auto lst = KMainWindow::memberList(); for (KMainWindow *window : lst) { if (::qobject_cast(window)) { ktmw = window; break; } } bool activate; if (ktmw) { KMMainWin *win = static_cast(ktmw); activate = !onlyCheck; // existing window: only activate if not --check if (activate) { win->show(); } } else { KMMainWin *win = new KMMainWin; if (!startInTray && !KMailSettings::self()->startInTray()) { win->show(); } activate = false; // new window: no explicit activation (#73591) } } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, bool hidden, const QString &messageFile, const QStringList &attachmentPaths, const QStringList &customHeaders, const QString &replyTo, const QString &inReplyTo, const QString &identity) { const OpenComposerSettings settings(to, cc, bcc, subject, body, hidden, messageFile, attachmentPaths, customHeaders, replyTo, inReplyTo, identity); OpenComposerJob *job = new OpenComposerJob(this); job->setOpenComposerSettings(settings); job->start(); } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, bool hidden, const QString &attachName, const QByteArray &attachCte, const QByteArray &attachData, const QByteArray &attachType, const QByteArray &attachSubType, const QByteArray &attachParamAttr, const QString &attachParamValue, const QByteArray &attachContDisp, const QByteArray &attachCharset, unsigned int identity) { fillComposer(hidden, to, cc, bcc, subject, body, attachName, attachCte, attachData, attachType, attachSubType, attachParamAttr, attachParamValue, attachContDisp, attachCharset, identity, false); } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QString &attachName, const QByteArray &attachCte, const QByteArray &attachData, const QByteArray &attachType, const QByteArray &attachSubType, const QByteArray &attachParamAttr, const QString &attachParamValue, const QByteArray &attachContDisp, const QByteArray &attachCharset, unsigned int identity) { fillComposer(false, to, cc, bcc, subject, body, attachName, attachCte, attachData, attachType, attachSubType, attachParamAttr, attachParamValue, attachContDisp, attachCharset, identity, true); } void KMKernel::fillComposer(bool hidden, const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QString &attachName, const QByteArray &attachCte, const QByteArray &attachData, const QByteArray &attachType, const QByteArray &attachSubType, const QByteArray &attachParamAttr, const QString &attachParamValue, const QByteArray &attachContDisp, const QByteArray &attachCharset, unsigned int identity, bool forceShowWindow) { const FillComposerJobSettings settings(hidden, to, cc, bcc, subject, body, attachName, attachCte, attachData, attachType, attachSubType, attachParamAttr, attachParamValue, attachContDisp, attachCharset, identity, forceShowWindow); FillComposerJob *job = new FillComposerJob; job->setSettings(settings); job->start(); } void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, bool hidden) { const OpenComposerHiddenJobSettings settings(to, cc, bcc, subject, body, hidden); OpenComposerHiddenJob *job = new OpenComposerHiddenJob(this); job->setSettings(settings); job->start(); } void KMKernel::newMessage(const QString &to, const QString &cc, const QString &bcc, bool hidden, bool useFolderId, const QString & /*messageFile*/, const QString &_attachURL) { QSharedPointer folder; Akonadi::Collection col; uint id = 0; if (useFolderId) { //create message with required folder identity folder = currentFolderCollection(); id = folder ? folder->identity() : 0; col = currentCollection(); } const NewMessageJobSettings settings(to, cc, bcc, hidden, _attachURL, folder, id, col); NewMessageJob *job = new NewMessageJob(this); job->setNewMessageJobSettings(settings); job->start(); } void KMKernel::viewMessage(const QUrl &url) { KMOpenMsgCommand *openCommand = new KMOpenMsgCommand(nullptr, url); openCommand->start(); } int KMKernel::viewMessage(const QString &messageFile) { KMOpenMsgCommand *openCommand = new KMOpenMsgCommand(nullptr, QUrl::fromLocalFile(messageFile)); openCommand->start(); return 1; } void KMKernel::raise() { QDBusInterface iface(QStringLiteral("org.kde.kmail"), QStringLiteral("/MainApplication"), QStringLiteral("org.kde.PIMUniqueApplication"), QDBusConnection::sessionBus()); QDBusReply reply; if (!iface.isValid() || !(reply = iface.call(QStringLiteral("newInstance"))).isValid()) { QDBusError err = iface.lastError(); qCritical() << "Communication problem with KMail. " << "Error message was:" << err.name() << ": \"" << err.message() << "\""; } } bool KMKernel::showMail(qint64 serialNumber) { KMMainWidget *mainWidget = nullptr; // First look for a KMainWindow. const auto lst = KMainWindow::memberList(); for (KMainWindow *window : lst) { // Then look for a KMMainWidget. QList l = window->findChildren(); if (!l.isEmpty() && l.first()) { mainWidget = l.first(); if (window->isActiveWindow()) { break; } } } if (mainWidget) { Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(Akonadi::Item(serialNumber), this); job->fetchScope().fetchFullPayload(); job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); if (job->exec()) { if (job->items().count() >= 1) { KMReaderMainWin *win = new KMReaderMainWin(MessageViewer::Viewer::UseGlobalSetting, false); const auto item = job->items().at(0); win->showMessage(MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding(), item, item.parentCollection()); win->show(); return true; } } } return false; } void KMKernel::pauseBackgroundJobs() { mBackgroundTasksTimer->stop(); mJobScheduler->pause(); } void KMKernel::resumeBackgroundJobs() { mJobScheduler->resume(); mBackgroundTasksTimer->start(4 * 60 * 60 * 1000); } void KMKernel::stopNetworkJobs() { if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) { return; } setAccountStatus(false); KMailSettings::setNetworkState(KMailSettings::EnumNetworkState::Offline); BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be offline; all network jobs are suspended")); Q_EMIT onlineStatusChanged((KMailSettings::EnumNetworkState::type)KMailSettings::networkState()); } void KMKernel::setAccountStatus(bool goOnline) { const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(false); for (Akonadi::AgentInstance type : lst) { const QString identifier(type.identifier()); if (PimCommon::Util::isImapResource(identifier) || identifier.contains(POP3_RESOURCE_IDENTIFIER) || identifier.contains(QLatin1String("akonadi_maildispatcher_agent")) || type.type().capabilities().contains(QLatin1String("NeedsNetwork"))) { type.setIsOnline(goOnline); } } if (goOnline && MessageComposer::MessageComposerSettings::self()->sendImmediate()) { const auto col = CommonKernel->collectionFromId(CommonKernel->outboxCollectionFolder().id()); const qint64 nbMsgOutboxCollection = col.statistics().count(); if (nbMsgOutboxCollection > 0) { if (!kmkernel->msgSender()->sendQueued()) { KNotification::event(QStringLiteral("sent-mail-error"), i18n("Send Email"), i18n("Impossible to send email"), QStringLiteral("kmail"), KMKernel::self()->mainWin(), KNotification::CloseOnTimeout); } } } } const QString KMKernel::xmlGuiInstanceName() const { return mXmlGuiInstance; } void KMKernel::setXmlGuiInstanceName(const QString &instance) { mXmlGuiInstance = instance; } KMail::UndoStack *KMKernel::undoStack() const { return the_undoStack; } void KMKernel::resumeNetworkJobs() { if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Online) { return; } if (mSystemNetworkStatus) { setAccountStatus(true); BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be online; all network jobs resumed")); } else { BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be online; all network jobs will resume when a network connection is detected")); } KMailSettings::setNetworkState(KMailSettings::EnumNetworkState::Online); Q_EMIT onlineStatusChanged((KMailSettings::EnumNetworkState::type)KMailSettings::networkState()); KMMainWidget *widget = getKMMainWidget(); if (widget) { widget->refreshMessageListSelection(); } } bool KMKernel::isOffline() { if ((KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) || !PimCommon::NetworkManager::self()->networkConfigureManager()->isOnline()) { return true; } else { return false; } } void KMKernel::verifyAccount() { const QString resourceGroupPattern(QStringLiteral("Resource %1")); const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (Akonadi::AgentInstance type : lst) { KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(type.identifier())); if (group.readEntry("CheckOnStartup", false)) { if (!type.isOnline()) { type.setIsOnline(true); } type.synchronize(); } // "false" is also hardcoded in ConfigureDialog, don't forget to change there. if (group.readEntry("OfflineOnShutdown", false)) { if (!type.isOnline()) { type.setIsOnline(true); } } } } void KMKernel::slotCheckAccount(Akonadi::ServerManager::State state) { if (state == Akonadi::ServerManager::Running) { disconnect(Akonadi::ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State))); verifyAccount(); } } void KMKernel::checkMailOnStartup() { if (!kmkernel->askToGoOnline()) { return; } if (Akonadi::ServerManager::state() != Akonadi::ServerManager::Running) { connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, &KMKernel::slotCheckAccount); } else { verifyAccount(); } } bool KMKernel::askToGoOnline() { // already asking means we are offline and need to wait anyhow if (s_askingToGoOnline) { return false; } if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) { s_askingToGoOnline = true; int rc = KMessageBox::questionYesNo(KMKernel::self()->mainWin(), i18n("KMail is currently in offline mode. " "How do you want to proceed?"), i18n("Online/Offline"), KGuiItem(i18n("Work Online")), KGuiItem(i18n("Work Offline"))); s_askingToGoOnline = false; if (rc == KMessageBox::No) { return false; } else { kmkernel->resumeNetworkJobs(); } } if (kmkernel->isOffline()) { return false; } return true; } void KMKernel::slotSystemNetworkStatusChanged(bool isOnline) { mSystemNetworkStatus = isOnline; if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) { return; } if (isOnline) { BroadcastStatus::instance()->setStatusMsg(i18n( "Network connection detected, all network jobs resumed")); kmkernel->setAccountStatus(true); } else { BroadcastStatus::instance()->setStatusMsg(i18n( "No network connection detected, all network jobs are suspended")); kmkernel->setAccountStatus(false); } } /********************************************************************/ /* Kernel methods */ /********************************************************************/ void KMKernel::quit() { // Called when all windows are closed. Will take care of compacting, // sending... should handle session management too!! } /* TODO later: Assuming that: - msgsender is nonblocking (our own, QSocketNotifier based. Pops up errors and sends signal senderFinished when done) o If we are getting mail, stop it (but don't lose something!) [Done already, see mailCheckAborted] o If we are sending mail, go on UNLESS this was called by SM, in which case stop ASAP that too (can we warn? should we continue on next start?) o If we are compacting, or expunging, go on UNLESS this was SM call. In that case stop compacting ASAP and continue on next start, before touching any folders. [Not needed anymore with CompactionJob] KMKernel::quit () { SM call? if compacting, stop; if sending, stop; if receiving, stop; Windows will take care of themselves (composer should dump its messages, if any but not in deadMail) declare us ready for the End of the Session No, normal quit call All windows are off. Anything to do, should compact or sender sends? Yes, maybe put an icon in panel as a sign of life if sender sending, connect us to his finished slot, declare us ready for quit and wait for senderFinished if not, Folder manager, go compact sent-mail and outbox } (= call slotFinished()) void KMKernel::slotSenderFinished() { good, Folder manager go compact sent-mail and outbox clean up stage1 (release folders and config, unregister from dcop) -- another kmail may start now --- qApp->quit(); } */ /********************************************************************/ /* Init, Exit, and handler methods */ /********************************************************************/ //----------------------------------------------------------------------------- // Open a composer for each message found in the dead.letter folder void KMKernel::recoverDeadLetters() { const QString pathName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kmail2/"); QDir dir(pathName); if (!dir.exists(QStringLiteral("autosave"))) { return; } dir.cd(pathName + QLatin1String("autosave")); const QFileInfoList autoSaveFiles = dir.entryInfoList(); for (const QFileInfo &file : autoSaveFiles) { // Disregard the '.' and '..' folders const QString filename = file.fileName(); if (filename == QLatin1Char('.') || filename == QLatin1String("..") || file.isDir()) { continue; } qCDebug(KMAIL_LOG) << "Opening autosave file:" << file.absoluteFilePath(); QFile autoSaveFile(file.absoluteFilePath()); if (autoSaveFile.open(QIODevice::ReadOnly)) { const KMime::Message::Ptr autoSaveMessage(new KMime::Message()); const QByteArray msgData = autoSaveFile.readAll(); autoSaveMessage->setContent(msgData); autoSaveMessage->parse(); // Show the a new composer dialog for the message KMail::Composer *autoSaveWin = KMail::makeComposer(); autoSaveWin->setMessage(autoSaveMessage, false, false, false); autoSaveWin->setAutoSaveFileName(filename); autoSaveWin->show(); autoSaveFile.close(); } else { KMessageBox::sorry(nullptr, i18n("Failed to open autosave file at %1.\nReason: %2", file.absoluteFilePath(), autoSaveFile.errorString()), i18n("Opening Autosave File Failed")); } } } void KMKernel::akonadiStateChanged(Akonadi::ServerManager::State state) { qCDebug(KMAIL_LOG) << "KMKernel has akonadi state changed to:" << int(state); if (state == Akonadi::ServerManager::Running) { CommonKernel->initFolders(); } } static void kmCrashHandler(int sigId) { fprintf(stderr, "*** KMail got signal %d (Exiting)\n", sigId); // try to cleanup all windows if (kmkernel) { kmkernel->dumpDeadLetters(); fprintf(stderr, "*** Dead letters dumped.\n"); kmkernel->stopAgentInstance(); kmkernel->cleanupTemporaryFiles(); } } void KMKernel::init() { the_shuttingDown = false; the_firstStart = KMailSettings::self()->firstStart(); KMailSettings::self()->setFirstStart(false); the_undoStack = new KMail::UndoStack(20); the_msgSender = new MessageComposer::AkonadiSender; // filterMgr->dump(); mBackgroundTasksTimer = new QTimer(this); mBackgroundTasksTimer->setSingleShot(true); connect(mBackgroundTasksTimer, &QTimer::timeout, this, &KMKernel::slotRunBackgroundTasks); #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h mBackgroundTasksTimer->start(10000); // 10s, singleshot #else mBackgroundTasksTimer->start(5 * 60000); // 5 minutes, singleshot #endif KCrash::setEmergencySaveFunction(kmCrashHandler); qCDebug(KMAIL_LOG) << "KMail init with akonadi server state:" << int(Akonadi::ServerManager::state()); if (Akonadi::ServerManager::state() == Akonadi::ServerManager::Running) { CommonKernel->initFolders(); } connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, &KMKernel::akonadiStateChanged); } bool KMKernel::doSessionManagement() { // Do session management if (qApp->isSessionRestored()) { int n = 1; while (KMMainWin::canBeRestored(n)) { //only restore main windows! (Matthias); if (KMMainWin::classNameOfToplevel(n) == QLatin1String("KMMainWin")) { (new KMMainWin)->restoreDockedState(n); } ++n; } return true; // we were restored by SM } return false; // no, we were not restored } bool KMKernel::firstInstance() const { return the_firstInstance; } void KMKernel::setFirstInstance(bool value) { the_firstInstance = value; } void KMKernel::closeAllKMailWindows() { const auto lst = KMainWindow::memberList(); for (KMainWindow *window : lst) { if (::qobject_cast(window) || ::qobject_cast(window)) { // close and delete the window window->setAttribute(Qt::WA_DeleteOnClose); window->close(); } } } void KMKernel::cleanup() { disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceStatusChanged(Akonadi::AgentInstance))); disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceError(Akonadi::AgentInstance,QString))); disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceWarning(Akonadi::AgentInstance,QString))); disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceRemoved(Akonadi::AgentInstance))); disconnect(KPIM::ProgressManager::instance(), SIGNAL(progressItemCompleted(KPIM::ProgressItem*))); disconnect(KPIM::ProgressManager::instance(), SIGNAL(progressItemCanceled(KPIM::ProgressItem*))); dumpDeadLetters(); the_shuttingDown = true; closeAllKMailWindows(); // Flush the cache of foldercollection objects. This results // in configuration writes, so we need to do it early enough. MailCommon::FolderSettings::clearCache(); // Write the config while all other managers are alive delete the_msgSender; the_msgSender = nullptr; delete the_undoStack; the_undoStack = nullptr; delete mConfigureDialog; mConfigureDialog = nullptr; KSharedConfig::Ptr config = KMKernel::config(); if (RecentAddresses::exists()) { RecentAddresses::self(config.data())->save(config.data()); } Akonadi::Collection trashCollection = CommonKernel->trashCollectionFolder(); if (trashCollection.isValid()) { if (KMailSettings::self()->emptyTrashOnExit()) { const auto service = Akonadi::ServerManager::self()->agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_mailfilter_agent")); OrgFreedesktopAkonadiMailFilterAgentInterface mailFilterInterface(service, QStringLiteral("/MailFilterAgent"), QDBusConnection::sessionBus(), this); if (mailFilterInterface.isValid()) { mailFilterInterface.expunge(static_cast(trashCollection.id())); } else { qCWarning(KMAIL_LOG) << "Mailfilter is not active"; } } } } void KMKernel::dumpDeadLetters() { if (shuttingDown()) { return; //All documents should be saved before shutting down is set! } // make all composer windows autosave their contents const auto lst = KMainWindow::memberList(); for (KMainWindow *window : lst) { if (KMail::Composer *win = ::qobject_cast(window)) { win->autoSaveMessage(true); while (win->isComposing()) { qCWarning(KMAIL_LOG) << "Danger, using an event loop, this should no longer be happening!"; qApp->processEvents(); } } } } void KMKernel::action(bool mailto, bool check, bool startInTray, const QString &to, const QString &cc, const QString &bcc, const QString &subj, const QString &body, const QUrl &messageFile, const QList &attachURLs, const QStringList &customHeaders, const QString &replyTo, const QString &inReplyTo, const QString &identity) { if (mailto) { openComposer(to, cc, bcc, subj, body, 0, messageFile.toLocalFile(), QUrl::toStringList(attachURLs), customHeaders, replyTo, inReplyTo, identity); } else { openReader(check, startInTray); } if (check) { checkMail(); } //Anything else? } void KMKernel::slotRequestConfigSync() { // ### FIXME: delay as promised in the kdoc of this function ;-) slotSyncConfig(); } void KMKernel::slotSyncConfig() { saveConfig(); //Laurent investigate why we need to reload them. PimCommon::PimCommonSettings::self()->load(); MessageCore::MessageCoreSettings::self()->load(); MessageViewer::MessageViewerSettings::self()->load(); MessageComposer::MessageComposerSettings::self()->load(); TemplateParser::TemplateParserSettings::self()->load(); MessageList::MessageListSettings::self()->load(); mMailCommonSettings->load(); Gravatar::GravatarSettings::self()->load(); KMailSettings::self()->load(); KMKernel::config()->reparseConfiguration(); mUnityServiceManager->updateCount(); } void KMKernel::saveConfig() { PimCommon::PimCommonSettings::self()->save(); MessageCore::MessageCoreSettings::self()->save(); MessageViewer::MessageViewerSettings::self()->save(); MessageComposer::MessageComposerSettings::self()->save(); TemplateParser::TemplateParserSettings::self()->save(); MessageList::MessageListSettings::self()->save(); mMailCommonSettings->save(); Gravatar::GravatarSettings::self()->save(); KMailSettings::self()->save(); } void KMKernel::updateConfig() { slotConfigChanged(); } void KMKernel::slotShowConfigurationDialog() { if (KMKernel::getKMMainWidget() == nullptr) { // ensure that there is a main widget available // as parts of the configure dialog (identity) rely on this // and this slot can be called when there is only a KMComposeWin showing KMMainWin *win = new KMMainWin; win->show(); } if (!mConfigureDialog) { mConfigureDialog = new ConfigureDialog(nullptr, false); mConfigureDialog->setObjectName(QStringLiteral("configure")); connect(mConfigureDialog, &ConfigureDialog::configChanged, this, &KMKernel::slotConfigChanged); } // Save all current settings. if (getKMMainWidget()) { getKMMainWidget()->writeReaderConfig(); } if (mConfigureDialog->isHidden()) { mConfigureDialog->show(); } else { mConfigureDialog->raise(); } } void KMKernel::slotConfigChanged() { CodecManager::self()->updatePreferredCharsets(); Q_EMIT configChanged(); } //------------------------------------------------------------------------------- bool KMKernel::haveSystemTrayApplet() const { return mUnityServiceManager->haveSystemTrayApplet(); } QTextCodec *KMKernel::networkCodec() const { return mNetCodec; } void KMKernel::updateSystemTray() { if (!the_shuttingDown) { mUnityServiceManager->initListOfCollection(); } } KIdentityManagement::IdentityManager *KMKernel::identityManager() { return KIdentityManagement::IdentityManager::self(); } JobScheduler *KMKernel::jobScheduler() const { return mJobScheduler; } KMainWindow *KMKernel::mainWin() { // First look for a KMMainWin. const auto lst = KMainWindow::memberList(); for (KMainWindow *window : lst) { if (::qobject_cast(window)) { return window; } } // There is no KMMainWin. Use any other KMainWindow instead (e.g. in // case we are running inside Kontact) because we anyway only need // it for modal message boxes and for KNotify events. if (!KMainWindow::memberList().isEmpty()) { KMainWindow *kmWin = KMainWindow::memberList().constFirst(); if (kmWin) { return kmWin; } } // There's not a single KMainWindow. Create a KMMainWin. // This could happen if we want to pop up an error message // while we are still doing the startup wizard and no other // KMainWindow is running. return new KMMainWin; } KMKernel *KMKernel::self() { return mySelf; } KSharedConfig::Ptr KMKernel::config() { assert(mySelf); if (!mySelf->mConfig) { mySelf->mConfig = KSharedConfig::openConfig(QStringLiteral("kmail2rc")); // Check that all updates have been run on the config file: MessageList::MessageListSettings::self()->setSharedConfig(mySelf->mConfig); MessageList::MessageListSettings::self()->load(); TemplateParser::TemplateParserSettings::self()->setSharedConfig(mySelf->mConfig); TemplateParser::TemplateParserSettings::self()->load(); MessageComposer::MessageComposerSettings::self()->setSharedConfig(mySelf->mConfig); MessageComposer::MessageComposerSettings::self()->load(); MessageCore::MessageCoreSettings::self()->setSharedConfig(mySelf->mConfig); MessageCore::MessageCoreSettings::self()->load(); MessageViewer::MessageViewerSettings::self()->setSharedConfig(mySelf->mConfig); MessageViewer::MessageViewerSettings::self()->load(); mMailCommonSettings = new MailCommon::MailCommonSettings; mMailCommonSettings->setSharedConfig(mySelf->mConfig); mMailCommonSettings->load(); PimCommon::PimCommonSettings::self()->setSharedConfig(mySelf->mConfig); PimCommon::PimCommonSettings::self()->load(); Gravatar::GravatarSettings::self()->setSharedConfig(mySelf->mConfig); Gravatar::GravatarSettings::self()->load(); } return mySelf->mConfig; } void KMKernel::syncConfig() { slotRequestConfigSync(); } void KMKernel::selectCollectionFromId(Akonadi::Collection::Id id) { KMMainWidget *widget = getKMMainWidget(); Q_ASSERT(widget); if (!widget) { return; } Akonadi::Collection colFolder = CommonKernel->collectionFromId(id); if (colFolder.isValid()) { widget->slotSelectCollectionFolder(colFolder); } } bool KMKernel::selectFolder(const QString &folder) { KMMainWidget *widget = getKMMainWidget(); Q_ASSERT(widget); if (!widget) { return false; } const Akonadi::Collection colFolder = CommonKernel->collectionFromId(folder.toLongLong()); if (colFolder.isValid()) { widget->slotSelectCollectionFolder(colFolder); return true; } return false; } KMMainWidget *KMKernel::getKMMainWidget() { //This could definitely use a speadup const QWidgetList l = QApplication::topLevelWidgets(); for (QWidget *wid : l) { QList l2 = wid->window()->findChildren(); if (!l2.isEmpty() && l2.first()) { return l2.first(); } } return nullptr; } void KMKernel::slotRunBackgroundTasks() // called regularly by timer { // Hidden KConfig keys. Not meant to be used, but a nice fallback in case // a stable kmail release goes out with a nasty bug in CompactionJob... if (KMailSettings::self()->autoExpiring()) { mFolderCollectionMonitor->expireAllFolders(false /*scheduled, not immediate*/, entityTreeModel()); } if (KMailSettings::self()->checkCollectionsIndexing()) { mCheckIndexingManager->start(entityTreeModel()); } #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h mBackgroundTasksTimer->start(60 * 1000); // check again in 1 minute #else mBackgroundTasksTimer->start(4 * 60 * 60 * 1000); // check again in 4 hours #endif } static Akonadi::Collection::List collect_collections(const QAbstractItemModel *model, const QModelIndex &parent) { Akonadi::Collection::List collections; QStack stack; stack.push(parent); while (!stack.isEmpty()) { const QModelIndex idx = stack.pop(); if (idx.isValid()) { collections << model->data(idx, Akonadi::EntityTreeModel::CollectionRole).value(); for (int i = model->rowCount(idx) - 1; i >= 0; --i) { stack.push(model->index(i, 0, idx)); } } } return collections; } Akonadi::Collection::List KMKernel::allFolders() const { return collect_collections(collectionModel(), QModelIndex()); } Akonadi::Collection::List KMKernel::subfolders(const Akonadi::Collection &col) const { const auto idx = collectionModel()->match({}, Akonadi::EntityTreeModel::CollectionRole, QVariant::fromValue(col), 1, Qt::MatchExactly); if (!idx.isEmpty()) { return collect_collections(collectionModel(), idx[0]); } return {}; } void KMKernel::expireAllFoldersNow() // called by the GUI { mFolderCollectionMonitor->expireAllFolders(true /*immediate*/, entityTreeModel()); } bool KMKernel::canQueryClose() { if (KMMainWidget::mainWidgetList() && KMMainWidget::mainWidgetList()->count() > 1) { return true; } return mUnityServiceManager->canQueryClose(); } Akonadi::Collection KMKernel::currentCollection() { KMMainWidget *widget = getKMMainWidget(); Akonadi::Collection col; if (widget) { col = widget->currentCollection(); } return col; } QSharedPointer KMKernel::currentFolderCollection() { KMMainWidget *widget = getKMMainWidget(); QSharedPointer folder; if (widget) { folder = widget->currentFolder(); } return folder; } MailCommon::MailCommonSettings *KMKernel::mailCommonSettings() const { return mMailCommonSettings; } Akonadi::Search::PIM::IndexedItems *KMKernel::indexedItems() const { return mIndexedItems; } // can't be inline, since KMSender isn't known to implement // KMail::MessageSender outside this .cpp file MessageComposer::MessageSender *KMKernel::msgSender() { return the_msgSender; } void KMKernel::transportRemoved(int id, const QString &name) { Q_UNUSED(id); // reset all identities using the deleted transport QStringList changedIdents; KIdentityManagement::IdentityManager *im = identityManager(); KIdentityManagement::IdentityManager::Iterator end = im->modifyEnd(); for (KIdentityManagement::IdentityManager::Iterator it = im->modifyBegin(); it != end; ++it) { if (name == (*it).transport()) { (*it).setTransport(QString()); changedIdents += (*it).identityName(); } } // if the deleted transport is the currently used transport reset it to default const QString ¤tTransport = KMailSettings::self()->currentTransport(); if (name == currentTransport) { KMailSettings::self()->setCurrentTransport(QString()); } if (!changedIdents.isEmpty()) { QString information = i18np("This identity has been changed to use the default transport:", "These %1 identities have been changed to use the default transport:", changedIdents.count()); //Don't set parent otherwise we will switch to current KMail and we configure it. So not good KMessageBox::informationList(nullptr, information, changedIdents); im->commit(); } } void KMKernel::transportRenamed(int id, const QString &oldName, const QString &newName) { Q_UNUSED(id); QStringList changedIdents; KIdentityManagement::IdentityManager *im = identityManager(); KIdentityManagement::IdentityManager::Iterator end = im->modifyEnd(); for (KIdentityManagement::IdentityManager::Iterator it = im->modifyBegin(); it != end; ++it) { if (oldName == (*it).transport()) { (*it).setTransport(newName); changedIdents << (*it).identityName(); } } if (!changedIdents.isEmpty()) { const QString information = i18np("This identity has been changed to use the modified transport:", "These %1 identities have been changed to use the modified transport:", changedIdents.count()); //Don't set parent otherwise we will swith to current KMail and we configure it. So not good KMessageBox::informationList(nullptr, information, changedIdents); im->commit(); } } void KMKernel::itemDispatchStarted() { // Watch progress of the MDA. KPIM::ProgressManagerAkonadi::createProgressItem(nullptr, MailTransport::DispatcherInterface().dispatcherInstance(), QStringLiteral("Sender"), i18n("Sending messages"), i18n("Initiating sending process..."), true, KPIM::ProgressItem::Unknown); } void KMKernel::instanceStatusChanged(const Akonadi::AgentInstance &instance) { if (instance.identifier() == QLatin1String("akonadi_mailfilter_agent")) { // Creating a progress item twice is ok, it will simply return the already existing // item KPIM::ProgressItem *progress = KPIM::ProgressManagerAkonadi::createProgressItem(nullptr, instance, instance.identifier(), instance.name(), instance.statusMessage(), false, KPIM::ProgressItem::Encrypted); progress->setProperty("AgentIdentifier", instance.identifier()); return; } if (MailCommon::Util::agentInstances(true).contains(instance)) { if (instance.status() == Akonadi::AgentInstance::Running) { if (mResourcesBeingChecked.isEmpty()) { qCDebug(KMAIL_LOG) << "A Resource started to synchronize, starting a mail check."; Q_EMIT startCheckMail(); } const QString identifier(instance.identifier()); if (!mResourcesBeingChecked.contains(identifier)) { mResourcesBeingChecked.append(identifier); } KPIM::ProgressItem::CryptoStatus cryptoStatus = KPIM::ProgressItem::Unencrypted; if (mResourceCryptoSettingCache.contains(identifier)) { cryptoStatus = mResourceCryptoSettingCache.value(identifier); } else { if (PimCommon::Util::isImapResource(identifier)) { MailCommon::ResourceReadConfigFile resourceFile(identifier); const KConfigGroup grp = resourceFile.group(QStringLiteral("network")); if (grp.isValid()) { const QString imapSafety = grp.readEntry(QStringLiteral("Safety")); if (imapSafety == QLatin1String("None")) { cryptoStatus = KPIM::ProgressItem::Unencrypted; } else { cryptoStatus = KPIM::ProgressItem::Encrypted; } mResourceCryptoSettingCache.insert(identifier, cryptoStatus); } } else if (identifier.contains(POP3_RESOURCE_IDENTIFIER)) { MailCommon::ResourceReadConfigFile resourceFile(identifier); const KConfigGroup grp = resourceFile.group(QStringLiteral("General")); if (grp.isValid()) { if (grp.readEntry(QStringLiteral("useSSL"), false) || grp.readEntry(QStringLiteral("useTLS"), false)) { cryptoStatus = KPIM::ProgressItem::Encrypted; } mResourceCryptoSettingCache.insert(identifier, cryptoStatus); } } } // Creating a progress item twice is ok, it will simply return the already existing // item KPIM::ProgressItem *progress = KPIM::ProgressManagerAkonadi::createProgressItem(nullptr, instance, instance.identifier(), instance.name(), instance.statusMessage(), true, cryptoStatus); progress->setProperty("AgentIdentifier", instance.identifier()); } else if (instance.status() == Akonadi::AgentInstance::Broken) { agentInstanceBroken(instance); } } } void KMKernel::agentInstanceBroken(const Akonadi::AgentInstance &instance) { const QString summary = i18n("Resource %1 is broken.\n%2", instance.name(), instance.statusMessage()); KNotification::event(QStringLiteral("akonadi-resource-broken"), QString(), summary, QStringLiteral("kmail"), nullptr, KNotification::CloseOnTimeout); } void KMKernel::slotProgressItemCompletedOrCanceled(KPIM::ProgressItem *item) { const QString identifier = item->property("AgentIdentifier").toString(); const Akonadi::AgentInstance agent = Akonadi::AgentManager::self()->instance(identifier); if (agent.isValid()) { mResourcesBeingChecked.removeAll(identifier); if (mResourcesBeingChecked.isEmpty()) { qCDebug(KMAIL_LOG) << "Last resource finished syncing, mail check done"; Q_EMIT endCheckMail(); } } } void KMKernel::updatedTemplates() { Q_EMIT customTemplatesChanged(); } void KMKernel::cleanupTemporaryFiles() { QDir dir(QDir::tempPath()); const QStringList lst = dir.entryList(QStringList{QStringLiteral("messageviewer_*")}); qCDebug(KMAIL_LOG) << " list file to delete " << lst; for (const QString &file : lst) { QDir tempDir(QDir::tempPath() + QLatin1Char('/') + file); if (!tempDir.removeRecursively()) { fprintf(stderr, "%s was not removed .\n", qPrintable(tempDir.absolutePath())); } else { fprintf(stderr, "%s was removed .\n", qPrintable(tempDir.absolutePath())); } } } void KMKernel::stopAgentInstance() { const QString resourceGroupPattern(QStringLiteral("Resource %1")); const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (Akonadi::AgentInstance type : lst) { const QString identifier = type.identifier(); KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(identifier)); // Keep sync in ConfigureDialog, don't forget to change there. if (group.readEntry("OfflineOnShutdown", identifier.startsWith(QLatin1String("akonadi_pop3_resource")) ? true : false)) { type.setIsOnline(false); } } } void KMKernel::slotCollectionRemoved(const Akonadi::Collection &col) { KConfigGroup group(KMKernel::config(), MailCommon::FolderSettings::configGroupName(col)); group.deleteGroup(); group.sync(); const QString colStr = QString::number(col.id()); TemplateParser::Util::deleteTemplate(colStr); MessageList::Util::deleteConfig(colStr); } void KMKernel::slotDeleteIdentity(uint identity) { TemplateParser::Util::deleteTemplate(QStringLiteral("IDENTITY_%1").arg(identity)); } bool KMKernel::showPopupAfterDnD() { return KMailSettings::self()->showPopupAfterDnD(); } bool KMKernel::excludeImportantMailFromExpiry() { return KMailSettings::self()->excludeImportantMailFromExpiry(); } qreal KMKernel::closeToQuotaThreshold() { return KMailSettings::self()->closeToQuotaThreshold(); } Akonadi::Collection::Id KMKernel::lastSelectedFolder() { return KMailSettings::self()->lastSelectedFolder(); } void KMKernel::setLastSelectedFolder(Akonadi::Collection::Id col) { KMailSettings::self()->setLastSelectedFolder(col); } QStringList KMKernel::customTemplates() { return GlobalSettingsBase::self()->customTemplates(); } void KMKernel::openFilterDialog(bool createDummyFilter) { if (!mFilterEditDialog) { mFilterEditDialog = new MailCommon::KMFilterDialog(getKMMainWidget()->actionCollections(), nullptr, createDummyFilter); mFilterEditDialog->setObjectName(QStringLiteral("filterdialog")); } mFilterEditDialog->show(); mFilterEditDialog->raise(); mFilterEditDialog->activateWindow(); } void KMKernel::createFilter(const QByteArray &field, const QString &value) { mFilterEditDialog->createFilter(field, value); } void KMKernel::checkFolderFromResources(const Akonadi::Collection::List &collectionList) { const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(); for (const Akonadi::AgentInstance &type : lst) { if (type.status() == Akonadi::AgentInstance::Broken) { continue; } const QString typeIdentifier(type.identifier()); if (PimCommon::Util::isImapResource(typeIdentifier)) { OrgKdeAkonadiImapSettingsInterface *iface = PimCommon::Util::createImapSettingsInterface(typeIdentifier); if (iface && iface->isValid()) { const Akonadi::Collection::Id imapTrashId = iface->trashCollection(); for (const Akonadi::Collection &collection : collectionList) { const Akonadi::Collection::Id collectionId = collection.id(); if (imapTrashId == collectionId) { //Use default trash iface->setTrashCollection(CommonKernel->trashCollectionFolder().id()); iface->save(); break; } } } delete iface; } else if (typeIdentifier.contains(POP3_RESOURCE_IDENTIFIER)) { OrgKdeAkonadiPOP3SettingsInterface *iface = MailCommon::Util::createPop3SettingsInterface(typeIdentifier); if (iface->isValid()) { for (const Akonadi::Collection &collection : qAsConst(collectionList)) { const Akonadi::Collection::Id collectionId = collection.id(); if (iface->targetCollection() == collectionId) { //Use default inbox iface->setTargetCollection(CommonKernel->inboxCollectionFolder().id()); iface->save(); break; } } } delete iface; } } } const QAbstractItemModel *KMKernel::treeviewModelSelection() { if (getKMMainWidget()) { return getKMMainWidget()->folderTreeView()->selectionModel()->model(); } else { return entityTreeModel(); } } void KMKernel::slotInstanceWarning(const Akonadi::AgentInstance &instance, const QString &message) { const QString summary = i18nc(": ", "%1: %2", instance.name(), message); KNotification::event(QStringLiteral("akonadi-instance-warning"), QString(), summary, QStringLiteral("kmail"), nullptr, KNotification::CloseOnTimeout); } void KMKernel::slotInstanceError(const Akonadi::AgentInstance &instance, const QString &message) { const QString summary = i18nc(": ", "%1: %2", instance.name(), message); KNotification::event(QStringLiteral("akonadi-instance-error"), QString(), summary, QStringLiteral("kmail"), nullptr, KNotification::CloseOnTimeout); } void KMKernel::slotInstanceRemoved(const Akonadi::AgentInstance &instance) { const QString identifier(instance.identifier()); const QString resourceGroup = QStringLiteral("Resource %1").arg(identifier); if (KMKernel::config()->hasGroup(resourceGroup)) { KConfigGroup group(KMKernel::config(), resourceGroup); group.deleteGroup(); group.sync(); } if (mResourceCryptoSettingCache.contains(identifier)) { mResourceCryptoSettingCache.remove(identifier); } mFolderArchiveManager->slotInstanceRemoved(instance); if (MailCommon::Util::isMailAgent(instance)) { Q_EMIT incomingAccountsChanged(); } } void KMKernel::slotInstanceAdded(const Akonadi::AgentInstance &instance) { if (MailCommon::Util::isMailAgent(instance)) { Q_EMIT incomingAccountsChanged(); } } void KMKernel::savePaneSelection() { KMMainWidget *widget = getKMMainWidget(); if (widget) { widget->savePaneSelection(); } } void KMKernel::updatePaneTagComboBox() { KMMainWidget *widget = getKMMainWidget(); if (widget) { widget->updatePaneTagComboBox(); } } void KMKernel::resourceGoOnLine() { KMMainWidget *widget = getKMMainWidget(); if (widget) { if (widget->currentCollection().isValid()) { Akonadi::Collection collection = widget->currentCollection(); Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(collection.resource()); instance.setIsOnline(true); widget->refreshMessageListSelection(); } } } void KMKernel::makeResourceOnline(MessageViewer::Viewer::ResourceOnlineMode mode) { switch (mode) { case MessageViewer::Viewer::AllResources: resumeNetworkJobs(); break; case MessageViewer::Viewer::SelectedResource: resourceGoOnLine(); break; } } PimCommon::AutoCorrection *KMKernel::composerAutoCorrection() { return mAutoCorrection; } void KMKernel::toggleSystemTray() { KMMainWidget *widget = getKMMainWidget(); if (widget) { mUnityServiceManager->toggleSystemTray(widget); } } void KMKernel::showFolder(const QString &collectionId) { if (!collectionId.isEmpty()) { const Akonadi::Collection::Id id = collectionId.toLongLong(); selectCollectionFromId(id); } } void KMKernel::reloadFolderArchiveConfig() { mFolderArchiveManager->reloadConfig(); } void KMKernel::slotCollectionChanged(const Akonadi::Collection &, const QSet &set) { if (set.contains("newmailnotifierattribute")) { mUnityServiceManager->initListOfCollection(); } } FolderArchiveManager *KMKernel::folderArchiveManager() const { return mFolderArchiveManager; } bool KMKernel::allowToDebug() const { return mDebug; } bool KMKernel::firstStart() const { return the_firstStart; } bool KMKernel::shuttingDown() const { return the_shuttingDown; } void KMKernel::setShuttingDown(bool flag) { the_shuttingDown = flag; } void KMKernel::expunge(Akonadi::Collection::Id col, bool sync) { Q_UNUSED(col); Q_UNUSED(sync); } diff --git a/src/kmmainwidget.cpp b/src/kmmainwidget.cpp index 0e0b99524..f3abe2621 100644 --- a/src/kmmainwidget.cpp +++ b/src/kmmainwidget.cpp @@ -1,4907 +1,4907 @@ /* This file is part of KMail, the KDE mail client. Copyright (c) 2002 Don Sanders Copyright (c) 2009-2019 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 "kmmainwidget.h" #include "kmreadermainwin.h" #include "job/composenewmessagejob.h" #include "editor/composer.h" #include "searchdialog/searchwindow.h" #include "widgets/vacationscriptindicatorwidget.h" #include "widgets/zoomlabelwidget.h" #include "undostack.h" #include "kmcommands.h" #include "kmmainwin.h" #include #include "MailCommon/FolderSelectionDialog" #include "MailCommon/FolderTreeWidget" #include "PimCommonAkonadi/MailUtil" #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 "plugininterface/kmailplugininterface.h" #include "PimCommon/NetworkUtil" #include "kpimtextedit/texttospeech.h" #include "job/markallmessagesasreadinfolderandsubfolderjob.h" #include "job/removeduplicatemessageinfolderandsubfolderjob.h" #include "sieveimapinterface/kmsieveimappasswordprovider.h" #include #include #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 #include "PimCommon/PimUtil" #include "MailCommon/CollectionGeneralPage" #include "MailCommon/CollectionExpiryPage" #include "MailCommon/ExpireCollectionAttribute" #include "MailCommon/FilterManager" #include "MailCommon/MailFilter" #include "MailCommon/FavoriteCollectionWidget" #include "MailCommon/FavoriteCollectionOrderProxyModel" #include "mailcommonsettings_base.h" // Other PIM includes #include "kmail-version.h" #include "messageviewer/messageviewersettings.h" #include "messageviewer/viewer.h" #include "messageviewer/headerstyleplugin.h" #include "messageviewer/headerstyle.h" #include "messageviewer/config-messageviewer.h" #include #ifndef QT_NO_CURSOR #include "Libkdepim/KCursorSaver" #endif #include #include "MessageComposer/MessageHelper" #include #include "MessageCore/MailingList" #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 -#include +#include +#include +#include #include "kmail_debug.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include // Qt includes #include #include #include #include #include #include #include #include // System includes #include #include #include #include #include #include "PimCommonAkonadi/ManageServerSideSubscriptionJob" #include #include #ifdef USE_DKIM_CHECKER #include #endif using namespace KMime; using namespace Akonadi; using namespace MailCommon; using KPIM::ProgressManager; using KPIM::BroadcastStatus; using KMail::SearchWindow; using KMime::Types::AddrSpecList; using MessageViewer::AttachmentStrategy; Q_GLOBAL_STATIC(KMMainWidget::PtrList, theMainWidgetList) //----------------------------------------------------------------------------- KMMainWidget::KMMainWidget(QWidget *parent, KXMLGUIClient *aGUIClient, KActionCollection *actionCollection, const KSharedConfig::Ptr &config) : QWidget(parent) , mManageShowCollectionProperties(new ManageShowCollectionProperties(this, this)) { 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->setContentsMargins(0, 0, 0, 0); mConfig = config; mGUIClient = aGUIClient; mFolderTreeWidget = nullptr; Akonadi::ControlGui::widgetNeedsAkonadi(this); mFavoritesModel = nullptr; mSievePasswordProvider = new KMSieveImapPasswordProvider(winId()); mVacationManager = new KSieveUi::VacationManager(mSievePasswordProvider, this); connect(mVacationManager, &KSieveUi::VacationManager::updateVacationScriptStatus, this, qOverload(&KMMainWidget::updateVacationScriptStatus)); mToolbarActionSeparator = new QAction(this); mToolbarActionSeparator->setSeparator(true); KMailPluginInterface::self()->setActionCollection(mActionCollection); KMailPluginInterface::self()->initializePlugins(); KMailPluginInterface::self()->setMainWidget(this); 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 = qobject_cast(window()); mCurrentStatusBar = mainWin ? mainWin->statusBar() : nullptr; mVacationScriptIndicator = new KMail::VacationScriptIndicatorWidget(mCurrentStatusBar); mVacationScriptIndicator->hide(); mZoomLabelIndicator = new ZoomLabelWidget(mCurrentStatusBar); if (mMsgView) { setZoomChanged(mMsgView->viewer()->webViewZoomFactor()); } 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()) { const QStringList listOfMailerFound = MailCommon::Util::foundMailer(); if (!listOfMailerFound.isEmpty()) { if (KMessageBox::questionYesNoList(this, i18n("Another mailer was found on system. Do you want to import data from it?"), listOfMailerFound) == KMessageBox::Yes) { const QString path = QStandardPaths::findExecutable(QStringLiteral("akonadiimportwizard")); 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); setupUnifiedMailboxChecker(); } #ifdef USE_DKIM_CHECKER QWidget *KMMainWidget::dkimWidgetInfo() const { if (mMsgView) { return mMsgView->viewer()->dkimWidgetInfo(); } return nullptr; } #endif 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 (mCurrentCollection.isValid()) { id = mCurrentCollection.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(); clearCurrentFolder(); delete mMoveOrCopyToDialog; delete mSelectFromAllFoldersDialog; delete mSievePasswordProvider; disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)), this, nullptr); disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemRemoved(Akonadi::Item)), this, nullptr); disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)), this, nullptr); disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(collectionChanged(Akonadi::Collection,QSet)), this, nullptr); disconnect(kmkernel->folderCollectionMonitor(), SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), this, nullptr); mDestructed = true; } void KMMainWidget::clearCurrentFolder() { mCurrentFolderSettings.clear(); mCurrentCollection = Akonadi::Collection(); } 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::setCurrentCollection(const Akonadi::Collection &col) { mCurrentCollection = col; if (mCurrentFolderSettings) { mCurrentFolderSettings->setCollection(col); } } 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 (collectionId == mCurrentCollection.id()) { setCurrentCollection(CommonKernel->collectionFromId(mCurrentCollection.id())); updateMessageActions(); updateFolderMenu(); } // We call this for any collection, it could be one of our parents... if (mCurrentCollection.isValid()) { Q_EMIT captionChangeRequest(fullCollectionPath()); } } QString KMMainWidget::fullCollectionPath() const { if (mCurrentCollection.isValid()) { return MailCommon::Util::fullCollectionPath(mCurrentCollection); } return {}; } // Connected to the currentChanged signals from the folderTreeView and favorites view. void KMMainWidget::slotFolderChanged(const Akonadi::Collection &collection) { if (mCurrentCollection == collection) { return; } folderSelected(collection); if (collection.cachePolicy().syncOnDemand()) { AgentManager::self()->synchronizeCollection(collection, false); } mMsgActions->setCurrentMessage(Akonadi::Item()); Q_EMIT captionChangeRequest(MailCommon::Util::fullCollectionPath(collection)); } // Called by slotFolderChanged (no particular reason for this method to be split out) void KMMainWidget::folderSelected(const Akonadi::Collection &col) { 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 = mCurrentCollection != col; // Delete any pending timer, if needed it will be recreated below delete mShowBusySplashTimer; mShowBusySplashTimer = nullptr; if (newFolder) { // We're changing folder: write configuration for the old one writeFolderConfig(); } mCurrentFolderSettings = FolderSettings::forCollection(col); mCurrentCollection = col; readFolderConfig(); if (mMsgView) { assignLoadExternalReference(); } if (!mCurrentFolderSettings->isValid() && (mMessagePane->count() < 2)) { slotIntro(); } else { if (mMessagePane->isHidden()) { mMessagePane->show(); } } // 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 (mCurrentCollection.isValid()) { QModelIndex idx = Akonadi::EntityTreeModel::modelIndexForCollection(KMKernel::self()->entityTreeModel(), mCurrentCollection); mMessagePane->setCurrentFolder(mCurrentCollection, idx, false, mPreSelectionMode); } updateMessageActions(); updateFolderMenu(); } //----------------------------------------------------------------------------- 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 = (KMKernel::self()->mailCommonSettings()->favoriteCollectionViewMode() != MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::HiddenMode); mEnableFolderQuickSearch = KMailSettings::self()->enableFolderQuickSearch(); readFolderConfig(); updateHtmlMenuEntry(); if (mMsgView) { mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference); mMsgView->update(true); } } //----------------------------------------------------------------------------- void KMMainWidget::readFolderConfig() { if (!mCurrentCollection.isValid()) { return; } KSharedConfig::Ptr config = KMKernel::self()->config(); KConfigGroup group(config, MailCommon::FolderSettings::configGroupName(mCurrentCollection)); 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 (mCurrentCollection.isValid()) { KSharedConfig::Ptr config = KMKernel::self()->config(); KConfigGroup group(config, MailCommon::FolderSettings::configGroupName(mCurrentCollection)); if (mFolderHtmlLoadExtPreference) { group.writeEntry("htmlLoadExternalOverride", mFolderHtmlLoadExtPreference); } else { group.deleteEntry("htmlLoadExternalOverride"); } 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 = mFolderTreeWidget; if (mFavoriteCollectionsView) { mFolderViewSplitter = new QSplitter(Qt::Vertical); mFolderViewSplitter->setChildrenCollapsible(false); mFolderViewSplitter->addWidget(mFavoriteCollectionsView); mFolderViewSplitter->addWidget(mFolderTreeWidget); 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 ? KMKernel::self()->mailCommonSettings()->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, static_cast(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 (KMKernel::self()->mailCommonSettings()->favoriteCollectionViewMode() == MailCommon::MailCommonSettings::EnumFavoriteCollectionViewMode::IconMode) { mFavoriteCollectionsView->changeViewMode(QListView::IconMode); } else if (KMKernel::self()->mailCommonSettings()->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(KMKernel::self()->mailCommonSettings()->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) { KMKernel::self()->mailCommonSettings()->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; } Collection KMMainWidget::currentCollection() const { return mCurrentCollection; } //----------------------------------------------------------------------------- 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 = nullptr; delete mSplitter1; mMsgView = nullptr; mFolderViewSplitter = nullptr; mFavoriteCollectionsView = nullptr; mFolderTreeWidget = nullptr; mSplitter1 = nullptr; mSplitter2 = nullptr; mFavoritesModel = 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 the folder tree // FolderTreeWidget::TreeViewOptions opt = FolderTreeWidget::ShowUnreadCount; opt |= FolderTreeWidget::UseLineEditForFiltering; opt |= FolderTreeWidget::ShowCollectionStatisticAnimation; opt |= FolderTreeWidget::DontKeyFilter; mFolderTreeWidget = new FolderTreeWidget(this, mGUIClient, opt); connect(mFolderTreeWidget->folderTreeView(), qOverload(&EntityTreeView::currentChanged), this, &KMMainWidget::slotFolderChanged); connect(mFolderTreeWidget->folderTreeView()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KMMainWidget::updateFolderMenu); connect(mFolderTreeWidget->folderTreeView(), &FolderTreeView::newTabRequested, this, &KMMainWidget::slotCreateNewTab); // // Create the message pane // mMessagePane = new CollectionPane(!KMailSettings::self()->startSpecificFolderAtStartup(), KMKernel::self()->entityTreeModel(), mFolderTreeWidget->folderTreeView()->selectionModel(), this); connect(KMKernel::self()->entityTreeModel(), &Akonadi::EntityTreeModel::collectionFetched, this, &KMMainWidget::slotCollectionFetched); 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, this, &KMMainWidget::showMessageActivities); connect(mMessagePane, &MessageList::Pane::forceLostFocus, this, &KMMainWidget::slotSetFocusToViewer); // // Create the reader window // if (mReaderWindowActive) { mMsgView = new KMReaderWin(this, this, actionCollection()); if (mMsgActions) { mMsgActions->setMessageView(mMsgView); } connect(mMsgView->viewer(), &MessageViewer::Viewer::displayPopupMenu, this, &KMMainWidget::slotMessagePopup); connect(mMsgView->viewer(), &MessageViewer::Viewer::moveMessageToTrash, this, &KMMainWidget::slotMoveMessageToTrash); connect(mMsgView->viewer(), &MessageViewer::Viewer::pageIsScrolledToBottom, this, &KMMainWidget::slotPageIsScrolledToBottom); connect(mMsgView->viewer(), &MessageViewer::Viewer::replyMessageTo, this, &KMMainWidget::slotReplyMessageTo); connect(mMsgView->viewer(), &MessageViewer::Viewer::showStatusBarMessage, this, &KMMainWidget::setShowStatusBarMessage); connect(mMsgView->viewer(), &MessageViewer::Viewer::zoomChanged, this, &KMMainWidget::setZoomChanged); if (mShowIntroductionAction) { mShowIntroductionAction->setEnabled(true); } } else { if (mMsgActions) { mMsgActions->setMessageView(nullptr); } if (mShowIntroductionAction) { mShowIntroductionAction->setEnabled(false); } } 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(KMKernel::self()->mailCommonSettings(), mGUIClient, this); refreshFavoriteFoldersViewProperties(); connect(mFavoriteCollectionsView, qOverload(&EntityListView::currentChanged), this, &KMMainWidget::slotFolderChanged); connect(mFavoriteCollectionsView, &FavoriteCollectionWidget::newTabRequested, this, &KMMainWidget::slotCreateNewTab); mFavoritesModel = new Akonadi::FavoriteCollectionsModel( mFolderTreeWidget->folderTreeWidgetProxyModel(), KMKernel::self()->config()->group("FavoriteCollections"), mFavoriteCollectionsView); auto *orderProxy = new MailCommon::FavoriteCollectionOrderProxyModel(this); orderProxy->setOrderConfig(KMKernel::self()->config()->group("FavoriteCollectionsOrder")); orderProxy->setSourceModel(mFavoritesModel); orderProxy->sort(0, Qt::AscendingOrder); mFavoriteCollectionsView->setModel(orderProxy); mAkonadiStandardActionManager->setFavoriteCollectionsModel(mFavoritesModel); mAkonadiStandardActionManager->setFavoriteSelectionModel(mFavoriteCollectionsView->selectionModel()); } //Don't use mMailActionManager->createAllActions() to save memory by not //creating actions that doesn't make sense. const auto 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, }; for (StandardActionManager::Type standardAction : standardActions) { mAkonadiStandardActionManager->createAction(standardAction); } if (mEnableFavoriteFolderView) { const auto favoriteActions = { StandardActionManager::AddToFavoriteCollections, StandardActionManager::RemoveFromFavoriteCollections, StandardActionManager::RenameFavoriteCollection, StandardActionManager::SynchronizeFavoriteCollections, }; for (StandardActionManager::Type favoriteAction : favoriteActions) { mAkonadiStandardActionManager->createAction(favoriteAction); } } const auto mailActions = { StandardMailActionManager::MarkAllMailAsRead, StandardMailActionManager::MoveToTrash, StandardMailActionManager::MoveAllToTrash, StandardMailActionManager::RemoveDuplicates, StandardMailActionManager::EmptyAllTrash, StandardMailActionManager::MarkMailAsRead, StandardMailActionManager::MarkMailAsUnread, StandardMailActionManager::MarkMailAsImportant, StandardMailActionManager::MarkMailAsActionItem }; for (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(), qOverload &>(&ChangeRecorder::collectionChanged), this, &KMMainWidget::slotCollectionChanged); connect(kmkernel->folderCollectionMonitor(), &Monitor::collectionStatisticsChanged, this, &KMMainWidget::slotCollectionStatisticsChanged); } void KMMainWidget::updateMoveAction(const Akonadi::CollectionStatistics &statistic) { const bool hasUnreadMails = (statistic.unreadCount() > 0); updateMoveAction(hasUnreadMails); } void KMMainWidget::updateMoveAction(bool hasUnreadMails) { const bool enable_goto_unread = hasUnreadMails || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllFolders) || (KMailSettings::self()->loopOnGotoUnread() == KMailSettings::EnumLoopOnGotoUnread::LoopInAllMarkedFolders); actionCollection()->action(QStringLiteral("go_next_unread_message"))->setEnabled(enable_goto_unread); 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(qint64 statistics) { if (mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)) { const bool folderWithContent = mCurrentFolderSettings && !mCurrentFolderSettings->isStructural(); mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)->setEnabled(folderWithContent && (statistics > 0) && mCurrentFolderSettings->canDeleteMessages()); } } 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 (id == mCurrentCollection.id()) { updateMoveAction(statistic); updateAllToTrashAction(statistic.count()); setCurrentCollection(CommonKernel->collectionFromId(mCurrentCollection.id())); } } void KMMainWidget::slotCreateNewTab(bool preferNewTab) { mMessagePane->setPreferEmptyTab(preferNewTab); } void KMMainWidget::slotCollectionChanged(const Akonadi::Collection &collection, const QSet &set) { if ((collection == mCurrentCollection) && (set.contains("MESSAGEFOLDER") || set.contains("expirationcollectionattribute"))) { if (set.contains("MESSAGEFOLDER")) { mMessagePane->resetModelStorage(); } else { setCurrentCollection(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::showSearchDialog() { if (!mSearchWin) { mSearchWin = new SearchWindow(this, mCurrentCollection); mSearchWin->setModal(false); mSearchWin->setObjectName(QStringLiteral("Search")); } else { mSearchWin->activateFolder(mCurrentCollection); } mSearchWin->show(); KWindowSystem::activateWindow(mSearchWin->winId()); return true; } //----------------------------------------------------------------------------- void KMMainWidget::slotFilter() { FilterIf->openFilterDialog(true); } void KMMainWidget::slotManageSieveScripts() { if (!kmkernel->askToGoOnline()) { return; } if (mManageSieveDialog) { return; } mManageSieveDialog = new KSieveUi::ManageSieveScriptsDialog(mSievePasswordProvider); connect(mManageSieveDialog.data(), &KSieveUi::ManageSieveScriptsDialog::finished, this, &KMMainWidget::slotCheckVacation); mManageSieveDialog->show(); } //----------------------------------------------------------------------------- void KMMainWidget::slotCheckMail() { kmkernel->checkMail(); } //----------------------------------------------------------------------------- void KMMainWidget::slotCheckMailOnStartup() { kmkernel->checkMailOnStartup(); } void KMMainWidget::slotCompose() { ComposeNewMessageJob *job = new ComposeNewMessageJob; job->setFolderSettings(mCurrentFolderSettings); job->setCurrentCollection(mCurrentCollection); job->start(); } //----------------------------------------------------------------------------- // TODO: do we want the list sorted alphabetically? void KMMainWidget::slotShowNewFromTemplate() { if (mCurrentFolderSettings) { const KIdentityManagement::Identity &ident = kmkernel->identityManager()->identityForUoidOrDefault(mCurrentFolderSettings->identity()); mTemplateFolder = CommonKernel->collectionFromId(ident.templates().toLongLong()); } if (!mTemplateFolder.isValid()) { mTemplateFolder = CommonKernel->templatesCollectionFolder(); } if (!mTemplateFolder.isValid()) { qCWarning(KMAIL_LOG) << "Template folder not found"; 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 = MessageComposer::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 (mCurrentFolderSettings && mCurrentFolderSettings->isMailingListEnabled()) { if (KMail::Util::mailingListPost(mCurrentFolderSettings, mCurrentCollection)) { return; } } slotCompose(); } void KMMainWidget::slotExpireFolder() { if (!mCurrentFolderSettings) { return; } const MailCommon::ExpireCollectionAttribute *attr = mCurrentCollection.attribute(); if (attr) { 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); return; } if (KMailSettings::self()->warnBeforeExpire()) { const QString message = i18n("Are you sure you want to expire the folder %1?", mCurrentFolderSettings->name().toHtmlEscaped()); if (KMessageBox::warningContinueCancel(this, message, i18n("Expire Folder"), KGuiItem(i18n("&Expire"))) != KMessageBox::Continue) { return; } } MailCommon::Util::expireOldMessages(mCurrentCollection, true /*immediate*/); } } //----------------------------------------------------------------------------- void KMMainWidget::slotEmptyFolder() { if (!mCurrentCollection.isValid()) { return; } const bool isTrash = CommonKernel->folderIsTrash(mCurrentCollection); 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?", mCurrentCollection.name().toHtmlEscaped()); const QString icon = (isTrash) ? QStringLiteral("edit-delete-shred") : QStringLiteral("edit-delete"); if (KMessageBox::warningContinueCancel(this, text, title, KGuiItem(title, icon)) != 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. */ deleteSelectedMessages(false); } else { slotTrashSelectedMessages(); } if (mMsgView) { mMsgView->clearCache(); } if (!isTrash) { const QString str = i18n("Moved all messages to the trash"); showMessageActivities(str); } 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 (mCurrentCollection.isValid()) { QPointer archiveDialog = new KMail::ArchiveFolderDialog(this); archiveDialog->setFolder(mCurrentCollection); archiveDialog->exec(); delete archiveDialog; } } //----------------------------------------------------------------------------- void KMMainWidget::slotRemoveFolder() { if (!mCurrentFolderSettings) { return; } if (!mCurrentFolderSettings->isValid()) { return; } if (mCurrentFolderSettings->isSystemFolder()) { return; } if (mCurrentFolderSettings->isReadOnly()) { return; } RemoveCollectionJob *job = new RemoveCollectionJob(this); connect(job, &RemoveCollectionJob::clearCurrentFolder, this, &KMMainWidget::slotClearCurrentFolder); job->setMainWidget(this); job->setCurrentFolder(mCurrentCollection); job->start(); } void KMMainWidget::slotClearCurrentFolder() { clearCurrentFolder(); } //----------------------------------------------------------------------------- void KMMainWidget::slotExpireAll() { if (KMailSettings::self()->warnBeforeExpire()) { const int ret = KMessageBox::warningContinueCancel(KMainWindow::memberList().constFirst(), 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::assignLoadExternalReference() { if (mFolderHtmlLoadExtPreference) { mMsgView->setHtmlLoadExtDefault(mFolderHtmlLoadExtPreference); } else { mMsgView->setHtmlLoadExtDefault(mHtmlLoadExtGlobalSetting); } mMsgView->setDisplayFormatMessageOverwrite(mFolderDisplayFormatPreference); } //----------------------------------------------------------------------------- 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"), nullptr); if (result == KMessageBox::Cancel) { mPreferHtmlLoadExtAction->setChecked(false); return; } } mFolderHtmlLoadExtPreference = !mFolderHtmlLoadExtPreference; if (mMsgView) { assignLoadExternalReference(); mMsgView->update(true); } } //----------------------------------------------------------------------------- void KMMainWidget::slotForwardInlineMsg() { if (!mCurrentFolderSettings) { return; } const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } const QString text = mMsgView ? mMsgView->copyText() : QString(); KMForwardCommand *command = new KMForwardCommand( this, selectedMessages, mCurrentFolderSettings->identity(), QString(), text ); command->start(); } //----------------------------------------------------------------------------- void KMMainWidget::slotForwardAttachedMessage() { if (!mCurrentFolderSettings) { return; } const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } KMForwardAttachedCommand *command = new KMForwardAttachedCommand( this, selectedMessages, mCurrentFolderSettings->identity() ); command->start(); } //----------------------------------------------------------------------------- void KMMainWidget::slotUseTemplate() { newFromTemplate(mMessagePane->currentItem()); } //----------------------------------------------------------------------------- // 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) { const int selectedMessageCount = selectMsg.count(); 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.
", selectedMessageCount ), selectedMessageCount > 1 ? i18n("Delete Messages") : i18n("Delete Message"), KStandardGuiItem::del(), KStandardGuiItem::cancel() ); 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()) { showMessageActivities(i18n("Moving messages...")); } else { showMessageActivities(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(); QString str; if (command->result() == KMCommand::OK) { if (moveWasReallyADelete) { str = i18n("Messages deleted successfully."); } else { str = i18n("Messages moved successfully."); } } else { if (moveWasReallyADelete) { if (command->result() == KMCommand::Failed) { str = i18n("Deleting messages failed."); } else { str = i18n("Deleting messages canceled."); } } else { if (command->result() == KMCommand::Failed) { str = i18n("Moving messages failed."); } else { str = i18n("Moving messages canceled."); } } } showMessageActivities(str); // The command will autodelete itself and will also kill the set. } void KMMainWidget::slotDeleteMessages() { deleteSelectedMessages(true && !KMailSettings::self()->deleteMessageWithoutConfirmation()); } Akonadi::Item::List KMMainWidget::currentSelection() const { Akonadi::Item::List selectMsg; MessageList::Core::MessageItemSetReference ref = mMessagePane->selectionAsPersistentSet(); if (ref != -1) { selectMsg = mMessagePane->itemListFromPersistentSet(ref); } return selectMsg; } void KMMainWidget::deleteSelectedMessages(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(); showMessageActivities(i18n("Copying messages...")); } void KMMainWidget::slotCopyMessagesCompleted(KMCommand *command) { Q_ASSERT(command); QString str; if (command->result() == KMCommand::OK) { str = i18n("Messages copied successfully."); } else { if (command->result() == KMCommand::Failed) { str = i18n("Copying messages failed."); } else { str = i18n("Copying messages canceled."); } } showMessageActivities(str); // 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 (!mCurrentCollection.isValid()) { 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 :) KMTrashMsgCommand *command = new KMTrashMsgCommand(mCurrentCollection, select, ref); QObject::connect(command, &KMTrashMsgCommand::moveDone, this, &KMMainWidget::slotTrashMessagesCompleted); command->start(); switch (command->operation()) { case KMTrashMsgCommand::MoveToTrash: showMessageActivities(i18n("Moving messages to trash...")); break; case KMTrashMsgCommand::Delete: showMessageActivities(i18n("Deleting messages...")); break; case KMTrashMsgCommand::Both: case KMTrashMsgCommand::Unknown: showMessageActivities(i18n("Deleting and moving messages to trash...")); break; } } void KMMainWidget::slotTrashMessagesCompleted(KMTrashMsgCommand *command) { Q_ASSERT(command); mMessagePane->markMessageItemsAsAboutToBeRemoved(command->refSet(), false); mMessagePane->deletePersistentSet(command->refSet()); if (command->result() == KMCommand::OK) { switch (command->operation()) { case KMTrashMsgCommand::MoveToTrash: showMessageActivities(i18n("Messages moved to trash successfully.")); break; case KMTrashMsgCommand::Delete: showMessageActivities(i18n("Messages deleted successfully.")); break; case KMTrashMsgCommand::Both: case KMTrashMsgCommand::Unknown: showMessageActivities(i18n("Messages moved to trash or deleted successfully")); break; } } else if (command->result() == KMCommand::Failed) { switch (command->operation()) { case KMTrashMsgCommand::MoveToTrash: showMessageActivities(i18n("Moving messages to trash failed.")); break; case KMTrashMsgCommand::Delete: showMessageActivities(i18n("Deleting messages failed.")); break; case KMTrashMsgCommand::Both: case KMTrashMsgCommand::Unknown: showMessageActivities(i18n("Deleting or moving messages to trash failed.")); break; } } else { switch (command->operation()) { case KMTrashMsgCommand::MoveToTrash: showMessageActivities(i18n("Moving messages to trash canceled.")); break; case KMTrashMsgCommand::Delete: showMessageActivities(i18n("Deleting messages canceled.")); break; case KMTrashMsgCommand::Both: case KMTrashMsgCommand::Unknown: showMessageActivities(i18n("Deleting or moving messages to trash canceled.")); break; } } // 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; } QPointer dlg = new TagSelectDialog(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(); } delete dlg; } 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()); Q_EMIT captionChangeRequest(MailCommon::Util::fullCollectionPath(mMessagePane->currentFolder())); } //----------------------------------------------------------------------------- // 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::slotRedirectMessage() { 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 (!mCurrentFolderSettings) { return; } const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } const QString text = mMsgView ? mMsgView->copyText() : QString(); qCDebug(KMAIL_LOG) << "Forward with template:" << tmpl; KMForwardCommand *command = new KMForwardCommand( this, selectedMessages, mCurrentFolderSettings->identity(), tmpl, text ); 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); // call it explicitly in case the collection is filtered out in the foldertreeview } } void KMMainWidget::slotApplyFilters() { const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); if (selectedMessages.isEmpty()) { return; } applyFilters(selectedMessages); } Akonadi::Collection::List KMMainWidget::applyFilterOnCollection(bool recursive) { Akonadi::Collection::List cols; if (recursive) { cols = KMKernel::self()->subfolders(mCurrentCollection); } else { cols << mCurrentCollection; } return cols; } void KMMainWidget::slotApplyFiltersOnFolder(bool recursive) { if (mCurrentCollection.isValid()) { const Akonadi::Collection::List cols = applyFilterOnCollection(recursive); applyFilters(cols); } } void KMMainWidget::slotApplyFilterOnFolder(bool recursive) { if (mCurrentCollection.isValid()) { const Akonadi::Collection::List cols = applyFilterOnCollection(recursive); QAction *action = qobject_cast(sender()); applyFilter(cols, action->property("filter_id").toString()); } } 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::applyFilters(const Akonadi::Collection::List &selectedCols) { #ifndef QT_NO_CURSOR KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy()); #endif MailCommon::FilterManager::instance()->filter(selectedCols); } void KMMainWidget::applyFilter(const Akonadi::Collection::List &selectedCols, const QString &filter) { #ifndef QT_NO_CURSOR KPIM::KCursorSaver busy(KPIM::KBusyPtr::busy()); #endif MailCommon::FilterManager::instance()->filter(selectedCols, { filter }); } //----------------------------------------------------------------------------- void KMMainWidget::slotCheckVacation() { updateVacationScriptStatus(false); if (!kmkernel->askToGoOnline()) { return; } mVacationManager->checkVacation(); } void KMMainWidget::slotEditCurrentVacation() { slotEditVacation(QString()); } void KMMainWidget::slotEditVacation(const QString &serverName) { if (!kmkernel->askToGoOnline()) { return; } mVacationManager->slotEditVacation(serverName); } //----------------------------------------------------------------------------- void KMMainWidget::slotDebugSieve() { if (kmkernel->allowToDebug()) { QPointer mSieveDebugDialog = new KSieveUi::SieveDebugDialog(mSievePasswordProvider, this); mSieveDebugDialog->exec(); delete mSieveDebugDialog; } } 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 = 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, mMsgView->viewer()); } saveCommand->start(); } void KMMainWidget::slotOnlineStatus() { // KMKernel will 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()) { if (!kmkernel->msgSender()->sendQueued()) { KNotification::event(QStringLiteral("sent-mail-error"), i18n("Send Email"), i18n("Impossible to send email"), QStringLiteral("kmail"), this, KNotification::CloseOnTimeout); } } } //----------------------------------------------------------------------------- void KMMainWidget::slotSendQueuedVia(MailTransport::Transport *transport) { if (transport) { if (kmkernel->msgSender()) { if (!kmkernel->msgSender()->sendQueued(transport->id())) { KNotification::event(QStringLiteral("sent-mail-error"), i18n("Send Email"), i18n("Impossible to send email"), QStringLiteral("kmail"), this, KNotification::CloseOnTimeout); } } } } //----------------------------------------------------------------------------- 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::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::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 (!mCurrentCollection.isValid() || !msg.isValid()) { return; } if (CommonKernel->folderIsDraftOrOutbox(mCurrentCollection)) { mMsgActions->setCurrentMessage(msg); mMsgActions->editCurrentMessage(); return; } if (CommonKernel->folderIsTemplates(mCurrentCollection)) { slotUseTemplate(); return; } KMReaderMainWin *win = nullptr; if (!mMsgView) { win = new KMReaderMainWin(mFolderDisplayFormatPreference, mFolderHtmlLoadExtPreference); } // Try to fetch the mail, even in offline mode, it might be cached KMFetchMessageCommand *cmd = new KMFetchMessageCommand(this, msg, win ? win->viewer() : mMsgView->viewer(), win); 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) << "slotItemsFetchedForActivation result:" << result; return; } KMFetchMessageCommand *fetchCmd = qobject_cast(command); const Item msg = fetchCmd->item(); KMReaderMainWin *win = fetchCmd->readerMainWin(); if (!win) { win = new KMReaderMainWin(mFolderDisplayFormatPreference, mFolderHtmlLoadExtPreference); } if (mMsgView && mMsgView->viewer()) { win->viewer()->setWebViewZoomFactor(mMsgView->viewer()->webViewZoomFactor()); win->viewer()->setHtmlLoadExtOverride(mMsgView->viewer()->htmlLoadExtOverride()); win->viewer()->setDisplayFormatMessageOverwrite(mMsgView->viewer()->displayFormatMessageOverwrite()); } const bool useFixedFont = mMsgView ? mMsgView->isFixedFont() : MessageViewer::MessageViewerSettings::self()->useFixedFont(); win->setUseFixedFont(useFixedFont); win->showMessage(overrideEncoding(), msg, CommonKernel->collectionFromId(msg.parentCollection().id())); 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 WebEngineViewer::WebHitTestResult &result, const QPoint &aPoint) { QUrl aUrl = result.linkUrl(); QUrl imageUrl = result.imageUrl(); 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); job->setProperty("webhitresult", QVariant::fromValue(result)); connect(job, &Akonadi::ContactSearchJob::result, this, &KMMainWidget::slotContactSearchJobForMessagePopupDone); } else { showMessagePopup(msg, aUrl, imageUrl, aPoint, false, false, result); } } 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(); const WebEngineViewer::WebHitTestResult result = job->property("webhitresult").value(); showMessagePopup(msg, url, imageUrl, aPoint, contactAlreadyExists, uniqueContactFound, result); } void KMMainWidget::showMessagePopup(const Akonadi::Item &msg, const QUrl &url, const QUrl &imageUrl, const QPoint &aPoint, bool contactAlreadyExists, bool uniqueContactFound, const WebEngineViewer::WebHitTestResult &result) { QMenu menu(this); 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()); menu.addActions(mMsgView->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedUrl)); if (!imageUrl.isEmpty()) { menu.addSeparator(); menu.addAction(mMsgView->copyImageLocation()); menu.addAction(mMsgView->downloadImageToDiskAction()); menu.addAction(mMsgView->shareImage()); } 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 return; } Akonadi::Collection parentCol = msg.parentCollection(); if (parentCol.isValid() && CommonKernel->folderIsTemplates(parentCol)) { menu.addAction(mMsgActions->newMessageFromTemplateAction()); } else { menu.addAction(mMsgActions->replyMenu()); menu.addAction(mMsgActions->forwardMenu()); } if (parentCol.isValid() && CommonKernel->folderIsSentMailFolder(parentCol)) { menu.addAction(mMsgActions->sendAgainAction()); } else { menu.addAction(mMsgActions->editAsNewAction()); } 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(); } menu.addSeparator(); menu.addAction(mMsgActions->printPreviewAction()); menu.addAction(mMsgActions->printAction()); menu.addSeparator(); } 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(); } #if 0 menu.addAction(mMsgActions->annotateAction()); menu.addSeparator(); #endif menu.addAction(mMsgActions->addFollowupReminderAction()); if (kmkernel->allowToDebug()) { menu.addSeparator(); menu.addAction(mMsgActions->debugAkonadiSearchAction()); } } if (mMsgView) { const QList interceptorUrlActions = mMsgView->interceptorUrlActions(result); if (!interceptorUrlActions.isEmpty()) { menu.addSeparator(); menu.addActions(interceptorUrlActions); } } KAcceleratorManager::manage(&menu); menu.exec(aPoint, nullptr); } void KMMainWidget::setZoomChanged(qreal zoomFactor) { if (mZoomLabelIndicator) { mZoomLabelIndicator->setZoom(zoomFactor); } } void KMMainWidget::setupActions() { KMailPluginInterface::self()->setParentWidget(this); KMailPluginInterface::self()->createPluginInterface(); 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, &KMMainWidget::slotOpenMsg, actionCollection()); mOpenRecentAction = KStandardAction::openRecent(this, &KMMainWidget::slotOpenRecentMessage, 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"))); 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::slotRunAddressBook); 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("document-import")), i18n("&Import Messages..."), this); actionCollection()->addAction(QStringLiteral("import"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotImport); if (QStandardPaths::findExecutable(QStringLiteral("akonadiimportwizard")).isEmpty()) { action->setEnabled(false); } } { if (kmkernel->allowToDebug()) { QAction *action = new QAction(i18n("&Debug Sieve..."), this); actionCollection()->addAction(QStringLiteral("tools_debug_sieve"), action); connect(action, &QAction::triggered, this, &KMMainWidget::slotDebugSieve); } } { 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("&Import from another Email Client..."), 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, &QAction::triggered, this, &KMMainWidget::slotEditCurrentVacation); } { 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 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("edit-delete"))); KMail::Util::addQActionHelpText(mTrashThreadAction, i18n("Move thread to trashcan")); connect(mTrashThreadAction, &QAction::triggered, this, &KMMainWidget::slotTrashThread); mDeleteThreadAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete-shred")), i18n("Delete T&hread"), this); actionCollection()->addAction(QStringLiteral("delete_thread"), mDeleteThreadAction); //Don't use new connect api. connect(mDeleteThreadAction, &QAction::triggered, this, &KMMainWidget::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)); mSelectAllMessages = new QAction(i18n("Select &All Messages"), this); actionCollection()->addAction(QStringLiteral("mark_all_messages"), mSelectAllMessages); connect(mSelectAllMessages, &QAction::triggered, this, &KMMainWidget::slotSelectAllMessages); actionCollection()->setDefaultShortcut(mSelectAllMessages, 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)); //----- 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()); //----- "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); mCopyDecryptedActionMenu = new KActionMenu(i18n("Copy Decrypted To..."), this); actionCollection()->addAction(QStringLiteral("copy_decrypted_to_menu"), mCopyDecryptedActionMenu); connect(mCopyDecryptedActionMenu->menu(), &QMenu::triggered, this, &KMMainWidget::slotCopyDecryptedTo); 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); actionCollection()->setDefaultShortcuts(dukeOfMonmoth, QList { QKeySequence(Qt::Key_Enter), QKeySequence(Qt::Key_Return) }); //----- Go Menu { QAction *action = new QAction(i18n("&Next Message"), this); actionCollection()->addAction(QStringLiteral("go_next_message"), action); actionCollection()->setDefaultShortcuts(action, QList { QKeySequence(Qt::Key_N), QKeySequence(Qt::Key_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()->setDefaultShortcuts(action, QList { QKeySequence(Qt::Key_P), QKeySequence(Qt::Key_Left) }); connect(action, &QAction::triggered, this, &KMMainWidget::slotSelectPreviousMessage); } { 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); } { QAction *action = new QAction(i18n("&Add Account..."), this); actionCollection()->addAction(QStringLiteral("accountWizard"), action); connect(action, &QAction::triggered, mLaunchExternalComponent, &KMLaunchExternalComponent::slotAccountWizard); } { 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 != nullptr); } // ----- Standard Actions { 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); } { mApplyAllFiltersFolderAction = new QAction(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("Apply All Filters"), this); actionCollection()->addAction(QStringLiteral("apply_filters_folder"), mApplyAllFiltersFolderAction); connect(mApplyAllFiltersFolderAction, &QAction::triggered, this, [this] { slotApplyFiltersOnFolder(/* recursive */ false); }); } { mApplyAllFiltersFolderRecursiveAction = new QAction(QIcon::fromTheme(QStringLiteral("view-filter")), i18n("Apply All Filters"), this); actionCollection()->addAction(QStringLiteral("apply_filters_folder_recursive"), mApplyAllFiltersFolderRecursiveAction); connect(mApplyAllFiltersFolderRecursiveAction, &QAction::triggered, this, [this] { slotApplyFiltersOnFolder(/* recursive */ true); }); } { mApplyFilterFolderActionsMenu = new KActionMenu(i18n("Apply Filters on Folder"), this); actionCollection()->addAction(QStringLiteral("apply_filters_on_folder_actions"), mApplyFilterFolderActionsMenu); } { mApplyFilterFolderRecursiveActionsMenu = new KActionMenu(i18n("Apply Filters on Folder and all its Subfolders"), this); actionCollection()->addAction(QStringLiteral("apply_filters_on_folder_recursive_actions"), mApplyFilterFolderRecursiveActionsMenu); } { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("kontact")), i18n("Import/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); } QAction *act = actionCollection()->addAction(KStandardAction::Undo, QStringLiteral("kmail_undo")); connect(act, &QAction::triggered, this, &KMMainWidget::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); mUseLessBandwidth = new KToggleAction(i18n("Use Less Bandwidth"), this); actionCollection()->addAction(QStringLiteral("low_bandwidth"), mUseLessBandwidth); connect(mUseLessBandwidth, &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); mRemoveDuplicateRecursiveAction = new QAction(i18n("Remove Duplicates in This Folder and All its Subfolder"), this); actionCollection()->addAction(QStringLiteral("remove_duplicate_recursive"), mRemoveDuplicateRecursiveAction); connect(mRemoveDuplicateRecursiveAction, &KToggleAction::triggered, this, &KMMainWidget::slotRemoveDuplicateRecursive); mAccountSettings = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Account &Settings"), this); actionCollection()->addAction(QStringLiteral("resource_settings"), mAccountSettings); connect(mAccountSettings, &QAction::triggered, this, &KMMainWidget::slotAccountSettings); mRestartAccountSettings = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Restart Account"), this); actionCollection()->addAction(QStringLiteral("resource_restart"), mRestartAccountSettings); connect(mRestartAccountSettings, &QAction::triggered, this, &KMMainWidget::slotRestartAccount); } 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() { QPointer notifyDlg = new KMail::KMKnotify(this); notifyDlg->exec(); delete notifyDlg; } //----------------------------------------------------------------------------- void KMMainWidget::slotReadOn() { if (!mMsgView) { return; } mMsgView->viewer()->atBottom(); } void KMMainWidget::slotPageIsScrolledToBottom(bool isAtBottom) { if (isAtBottom) { slotSelectNextUnreadMessage(); } else { if (mMsgView) { mMsgView->viewer()->slotJumpDown(); } } } 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 (mCurrentFolderSettings && mCurrentFolderSettings->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; bool currentFolderSettingsIsValid = mCurrentFolderSettings && mCurrentFolderSettings->isValid(); if (currentFolderSettingsIsValid && mMessagePane->getSelectionStats(selectedItems, selectedVisibleItems, &allSelectedBelongToSameThread) ) { count = selectedItems.count(); currentMessage = mMessagePane->currentItem(); } else { count = 0; currentMessage = Akonadi::Item(); } mApplyFilterActionsMenu->setEnabled(currentFolderSettingsIsValid); mApplyFilterFolderRecursiveActionsMenu->setEnabled(currentFolderSettingsIsValid); // // 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. // const bool readOnly = currentFolderSettingsIsValid && (mCurrentFolderSettings->rights() & Akonadi::Collection::ReadOnly); // can we apply strictly single message actions ? (this is false if the whole selection contains more than one message) const bool single_actions = count == 1; // can we apply loosely single message actions ? (this is false if the VISIBLE selection contains more than one message) const bool singleVisibleMessageSelected = selectedVisibleItems.count() == 1; // can we apply "mass" actions to the selection ? (this is actually always true if the selection is non-empty) const bool mass_actions = count >= 1; // does the selection identify a single thread ? const bool thread_actions = mass_actions && allSelectedBelongToSameThread && mMessagePane->isThreaded(); // can we apply flags to the selected messages ? const bool flags_available = KMailSettings::self()->allowLocalFlags() || !(currentFolderSettingsIsValid ? 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 = currentFolderSettingsIsValid && (mCurrentFolderSettings->rights() & Akonadi::Collection::CanDeleteItem); mTrashThreadAction->setEnabled(thread_actions && canDeleteMessages); mDeleteThreadAction->setEnabled(thread_actions && canDeleteMessages); if (messageView() && messageView()->viewer() && messageView()->viewer()->headerStylePlugin()) { messageView()->viewer()->headerStylePlugin()->headerStyle()->setReadOnlyMessage(!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 && !MailCommon::Util::isVirtualCollection(mCurrentCollection)); if (mMsgView) { mMsgView->findInMessageAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentCollection)); } mMsgActions->forwardInlineAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentCollection)); mMsgActions->forwardAttachedAction()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentCollection)); mMsgActions->forwardMenu()->setEnabled(mass_actions && !CommonKernel->folderIsTemplates(mCurrentCollection)); mMsgActions->editAsNewAction()->setEnabled(single_actions); mMsgActions->newMessageFromTemplateAction()->setEnabled(single_actions && CommonKernel->folderIsTemplates(mCurrentCollection)); filterMenu()->setEnabled(single_actions); mMsgActions->redirectAction()->setEnabled(/*single_actions &&*/ mass_actions && !CommonKernel->folderIsTemplates(mCurrentCollection)); if (auto *menuCustom = mMsgActions->customTemplatesMenu()) { menuCustom->forwardActionMenu()->setEnabled(mass_actions); menuCustom->replyActionMenu()->setEnabled(single_actions); menuCustom->replyAllActionMenu()->setEnabled(single_actions); } // "Print" will act on the current message: it will ignore any hidden selection mMsgActions->printAction()->setEnabled(singleVisibleMessageSelected && mMsgView); // "Print preview" will act on the current message: it will ignore any hidden selection if (QAction *printPreviewAction = mMsgActions->printPreviewAction()) { printPreviewAction->setEnabled(singleVisibleMessageSelected && mMsgView); } // "View Source" will act on the current message: it will ignore any hidden selection if (mMsgView) { mMsgView->viewSourceAction()->setEnabled(singleVisibleMessageSelected); mMsgView->selectAllAction()->setEnabled(count); } MessageStatus status; status.setStatusFromFlags(currentMessage.flags()); QList< QAction *> actionList; bool statusSendAgain = single_actions && ((currentMessage.isValid() && status.isSent()) || (currentMessage.isValid() && CommonKernel->folderIsSentMailFolder(mCurrentCollection))); if (statusSendAgain) { actionList << mMsgActions->sendAgainAction(); } else if (single_actions) { actionList << mMsgActions->editAsNewAction(); } actionList << mSaveAttachmentsAction; if (mCurrentCollection.isValid() && FolderArchive::FolderArchiveUtil::resourceSupportArchiving(mCurrentCollection.resource())) { actionList << mArchiveAction; } mGUIClient->unplugActionList(QStringLiteral("messagelist_actionlist")); mGUIClient->plugActionList(QStringLiteral("messagelist_actionlist"), actionList); mMsgActions->sendAgainAction()->setEnabled(statusSendAgain); if (currentFolderSettingsIsValid) { updateMoveAction(mCurrentFolderSettings->statistics()); } else { updateMoveAction(false); } const auto col = CommonKernel->collectionFromId(CommonKernel->outboxCollectionFolder().id()); const bool nbMsgOutboxCollectionIsNotNull = (col.statistics().count() > 0); mSendQueued->setEnabled(nbMsgOutboxCollectionIsNotNull); mSendActionMenu->setEnabled(nbMsgOutboxCollectionIsNotNull); const bool newPostToMailingList = mCurrentFolderSettings && mCurrentFolderSettings->isMailingListEnabled(); mMessageNewList->setEnabled(newPostToMailingList); slotUpdateOnlineStatus(static_cast(KMailSettings::self()->networkState())); if (QAction *act = action(QStringLiteral("kmail_undo"))) { act->setEnabled(kmkernel->undoStack() && !kmkernel->undoStack()->isEmpty()); } // Enable / disable all filters. for (QAction *filterAction : qAsConst(mFilterMenuActions)) { filterAction->setEnabled(count > 0); } mApplyAllFiltersAction->setEnabled(count); mApplyFilterActionsMenu->setEnabled(count); mSelectAllMessages->setEnabled(count); if (currentMessage.hasFlag(Akonadi::MessageFlags::Encrypted) || count > 1) { mCopyDecryptedActionMenu->setVisible(true); mCopyDecryptedActionMenu->menu()->clear(); mAkonadiStandardActionManager->standardActionManager()->createActionFolderMenu( mCopyDecryptedActionMenu->menu(), Akonadi::StandardActionManager::CopyItemToMenu); } else { mCopyDecryptedActionMenu->setVisible(false); } } void KMMainWidget::slotAkonadiStandardActionUpdated() { if (mCollectionProperties) { if (mCurrentCollection.isValid()) { const Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(mCurrentCollection.resource()); mCollectionProperties->setEnabled(!mCurrentFolderSettings->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 = mCurrentFolderSettings && !mCurrentFolderSettings->isStructural(); if (QAction *act = mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections)) { act->setEnabled(mCurrentFolderSettings && (mCurrentCollection.rights() & Collection::CanDeleteCollection) && !mCurrentFolderSettings->isSystemFolder() && folderWithContent); } if (QAction *act = mAkonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MoveAllToTrash)) { act->setEnabled(folderWithContent && (mCurrentFolderSettings->count() > 0) && mCurrentFolderSettings->canDeleteMessages()); act->setText((mCurrentFolderSettings && CommonKernel->folderIsTrash(mCurrentCollection)) ? 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) { // the visual ones only make sense if we are showing a message list const bool enabledAction = (mFolderTreeWidget && mFolderTreeWidget->folderTreeView()->currentFolder().isValid()); mDisplayMessageFormatMenu->setEnabled(enabledAction); const bool isEnabled = (mFolderTreeWidget && mFolderTreeWidget->folderTreeView()->currentFolder().isValid()); const bool useHtml = (mFolderDisplayFormatPreference == MessageViewer::Viewer::Html || (mHtmlGlobalSetting && mFolderDisplayFormatPreference == MessageViewer::Viewer::UseGlobalSetting)); mPreferHtmlLoadExtAction->setEnabled(isEnabled && useHtml); mDisplayMessageFormatMenu->setDisplayMessageFormat(mFolderDisplayFormatPreference); if (mFolderHtmlLoadExtPreference) { mPreferHtmlLoadExtAction->setChecked(true); } else { mPreferHtmlLoadExtAction->setChecked(mHtmlLoadExtGlobalSetting); } } } //----------------------------------------------------------------------------- void KMMainWidget::updateFolderMenu() { if (!CommonKernel->outboxCollectionFolder().isValid()) { QTimer::singleShot(1000, this, &KMMainWidget::updateFolderMenu); return; } const bool folderWithContent = mCurrentFolderSettings && !mCurrentFolderSettings->isStructural() && mCurrentFolderSettings->isValid(); mFolderMailingListPropertiesAction->setEnabled(folderWithContent && !mCurrentFolderSettings->isSystemFolder()); QList< QAction * > actionlist; if (mCurrentCollection.id() == CommonKernel->outboxCollectionFolder().id() && (mCurrentCollection).statistics().count() > 0) { qCDebug(KMAIL_LOG) << "Enabling send queued"; mSendQueued->setEnabled(true); actionlist << mSendQueued; } // if ( mCurrentCollection.id() != CommonKernel->trashCollectionFolder().id() ) { // actionlist << mTrashAction; // } mGUIClient->unplugActionList(QStringLiteral("outbox_folder_actionlist")); mGUIClient->plugActionList(QStringLiteral("outbox_folder_actionlist"), actionlist); actionlist.clear(); const bool isASearchFolder = mCurrentCollection.resource() == QLatin1String("akonadi_search_resource"); if (isASearchFolder) { mAkonadiStandardActionManager->action(Akonadi::StandardActionManager::DeleteCollections)->setText(i18n("&Delete Search")); } mArchiveFolderAction->setEnabled(mCurrentFolderSettings && folderWithContent); const bool isInTrashFolder = (mCurrentFolderSettings && CommonKernel->folderIsTrash(mCurrentCollection)); QAction *moveToTrash = akonadiStandardAction(Akonadi::StandardMailActionManager::MoveToTrash); KMail::Util::setActionTrashOrDelete(moveToTrash, isInTrashFolder); mTrashThreadAction->setIcon(isInTrashFolder ? QIcon::fromTheme(QStringLiteral("edit-delete-shred")) : QIcon::fromTheme(QStringLiteral("edit-delete"))); mTrashThreadAction->setText(isInTrashFolder ? i18n("Delete T&hread") : i18n("M&ove Thread to Trash")); mSearchMessages->setText((mCurrentCollection.resource() == QLatin1String("akonadi_search_resource")) ? i18n("Edit Search...") : i18n("&Find Messages...")); mExpireConfigAction->setEnabled(mCurrentFolderSettings && !mCurrentFolderSettings->isStructural() && mCurrentFolderSettings->canDeleteMessages() && folderWithContent && !MailCommon::Util::isVirtualCollection(mCurrentCollection)); updateHtmlMenuEntry(); mShowFolderShortcutDialogAction->setEnabled(folderWithContent); actionlist << akonadiStandardAction(Akonadi::StandardActionManager::ManageLocalSubscriptions); bool imapFolderIsOnline = false; if (mCurrentFolderSettings && PimCommon::MailUtil::isImapFolder(mCurrentCollection, imapFolderIsOnline)) { if (imapFolderIsOnline) { actionlist << mServerSideSubscription; } } if (mCurrentCollection.parentCollection() != Akonadi::Collection::root()) { mGUIClient->unplugActionList(QStringLiteral("resource_settings")); mGUIClient->unplugActionList(QStringLiteral("resource_restart")); } else { mGUIClient->plugActionList(QStringLiteral("resource_settings"), {mAccountSettings}); mGUIClient->plugActionList(QStringLiteral("resource_restart"), {mRestartAccountSettings}); } mGUIClient->unplugActionList(QStringLiteral("collectionview_actionlist")); mGUIClient->plugActionList(QStringLiteral("collectionview_actionlist"), actionlist); const bool folderIsValid = folderWithContent; mApplyAllFiltersFolderAction->setEnabled(folderIsValid); mApplyFilterFolderActionsMenu->setEnabled(folderIsValid); mApplyFilterFolderRecursiveActionsMenu->setEnabled(folderIsValid); for (auto a : qAsConst(mFilterFolderMenuActions)) { a->setEnabled(folderIsValid); } for (auto a : qAsConst(mFilterFolderMenuRecursiveActions)) { a->setEnabled(folderIsValid); } } //----------------------------------------------------------------------------- void KMMainWidget::slotIntro() { if (!mMsgView) { return; } mMsgView->clear(true); // hide widgets that are in the way: if (mMessagePane && mLongFolderList) { mMessagePane->hide(); } mMsgView->displayAboutPage(); clearCurrentFolder(); } void KMMainWidget::slotShowStartupFolder() { connect(MailCommon::FilterManager::instance(), &FilterManager::filtersChanged, this, [this]() { initializeFilterActions(true); }); // 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); initializePluginActions(); const QString newFeaturesMD5 = KMReaderWin::newFeaturesMD5(); if (kmkernel->firstStart() || KMailSettings::self()->previousNewFeaturesMD5() != newFeaturesMD5) { KMailSettings::self()->setPreviousNewFeaturesMD5(newFeaturesMD5); slotIntro(); } } void KMMainWidget::checkAkonadiServerManagerState() { Akonadi::ServerManager::State state = Akonadi::ServerManager::self()->state(); if (state == Akonadi::ServerManager::Running) { initializeFilterActions(true); } else { connect(Akonadi::ServerManager::self(), &ServerManager::stateChanged, this, &KMMainWidget::slotServerStateChanged); } } void KMMainWidget::slotServerStateChanged(Akonadi::ServerManager::State state) { if (state == Akonadi::ServerManager::Running) { initializeFilterActions(true); 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 (mGUIClient->factory()) { if (!mFilterTBarActions.isEmpty()) { mGUIClient->unplugActionList(QStringLiteral("toolbar_filter_actions")); } if (!mFilterMenuActions.isEmpty()) { mGUIClient->unplugActionList(QStringLiteral("menu_filter_actions")); } if (!mFilterFolderMenuActions.isEmpty()) { mGUIClient->unplugActionList(QStringLiteral("menu_filter_folder_actions")); } if (!mFilterFolderMenuRecursiveActions.isEmpty()) { mGUIClient->unplugActionList(QStringLiteral("menu_filter_folder_recursive_actions")); } } for (QAction *a : qAsConst(mFilterMenuActions)) { actionCollection()->removeAction(a); } for (QAction *a : qAsConst(mFilterFolderMenuActions)) { actionCollection()->removeAction(a); } for (QAction *a : qAsConst(mFilterFolderMenuRecursiveActions)) { actionCollection()->removeAction(a); } mApplyFilterActionsMenu->menu()->clear(); mApplyFilterFolderActionsMenu->menu()->clear(); mApplyFilterFolderRecursiveActionsMenu->menu()->clear(); mFilterTBarActions.clear(); mFilterMenuActions.clear(); mFilterFolderMenuActions.clear(); mFilterFolderMenuRecursiveActions.clear(); qDeleteAll(mFilterCommands); mFilterCommands.clear(); } void KMMainWidget::clearPluginActions() { KMailPluginInterface::self()->clearPluginActions(QStringLiteral("kmail"), mGUIClient); } void KMMainWidget::initializePluginActions() { KMailPluginInterface::self()->initializePluginActions(QStringLiteral("kmail"), mGUIClient); } QAction *KMMainWidget::filterToAction(MailCommon::MailFilter *filter) { 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->setProperty("filter_id", filter->identifier()); 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); return filterAction; } //----------------------------------------------------------------------------- void KMMainWidget::initializeFilterActions(bool clearFilter) { if (clearFilter) { clearFilterActions(); } mApplyFilterActionsMenu->menu()->addAction(mApplyAllFiltersAction); mApplyFilterFolderActionsMenu->menu()->addAction(mApplyAllFiltersFolderAction); mApplyFilterFolderRecursiveActionsMenu->menu()->addAction(mApplyAllFiltersFolderRecursiveAction); bool addedSeparator = false; const QVector lstFilters = MailCommon::FilterManager::instance()->filters(); for (MailFilter *filter : lstFilters) { 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; } if (!addedSeparator) { QAction *a = mApplyFilterActionsMenu->menu()->addSeparator(); mFilterMenuActions.append(a); a = mApplyFilterFolderActionsMenu->menu()->addSeparator(); mFilterFolderMenuActions.append(a); a = mApplyFilterFolderRecursiveActionsMenu->menu()->addSeparator(); mFilterFolderMenuRecursiveActions.append(a); addedSeparator = true; } KMMetaFilterActionCommand *filterCommand = new KMMetaFilterActionCommand(filter->identifier(), this); mFilterCommands.append(filterCommand); auto filterAction = filterToAction(filter); actionCollection()->addAction(normalizedName, filterAction); connect(filterAction, &QAction::triggered, filterCommand, &KMMetaFilterActionCommand::start); actionCollection()->setDefaultShortcut(filterAction, filter->shortcut()); mApplyFilterActionsMenu->menu()->addAction(filterAction); mFilterMenuActions.append(filterAction); if (filter->configureToolbar()) { mFilterTBarActions.append(filterAction); } filterAction = filterToAction(filter); actionCollection()->addAction(normalizedName + QStringLiteral("___folder"), filterAction); connect(filterAction, &QAction::triggered, this, [this] { slotApplyFilterOnFolder(/* recursive */ false); }); mApplyFilterFolderActionsMenu->menu()->addAction(filterAction); mFilterFolderMenuActions.append(filterAction); filterAction = filterToAction(filter); actionCollection()->addAction(normalizedName + QStringLiteral("___folder_recursive"), filterAction); connect(filterAction, &QAction::triggered, this, [this] { slotApplyFilterOnFolder(/* recursive */ true); }); mApplyFilterFolderRecursiveActionsMenu->menu()->addAction(filterAction); mFilterFolderMenuRecursiveActions.append(filterAction); } } if (mGUIClient->factory()) { if (!mFilterMenuActions.isEmpty()) { mGUIClient->plugActionList(QStringLiteral("menu_filter_actions"), mFilterMenuActions); } if (!mFilterTBarActions.isEmpty()) { mFilterTBarActions.prepend(mToolbarActionSeparator); mGUIClient->plugActionList(QStringLiteral("toolbar_filter_actions"), mFilterTBarActions); } if (!mFilterFolderMenuActions.isEmpty()) { mGUIClient->plugActionList(QStringLiteral("menu_filter_folder_actions"), mFilterFolderMenuActions); } if (!mFilterFolderMenuRecursiveActions.isEmpty()) { mGUIClient->plugActionList(QStringLiteral("menu_filter_folder_recursive_actions"), mFilterFolderMenuRecursiveActions); } } // 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 nullptr; } return theMainWidgetList; } QSharedPointer KMMainWidget::currentFolder() const { return mCurrentFolderSettings; } QAction *KMMainWidget::action(const QString &name) { return mActionCollection->action(name); } KActionMenu *KMMainWidget::filterMenu() const { return mFilterMenu; } KActionMenu *KMMainWidget::mailingListActionMenu() const { return mMsgActions->mailingListActionMenu(); } QAction *KMMainWidget::sendQueuedAction() const { return mSendQueued; } KActionMenuTransport *KMMainWidget::sendQueueViaMenu() const { return mSendActionMenu; } KMail::MessageActions *KMMainWidget::messageActions() const { return mMsgActions; } //----------------------------------------------------------------------------- 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; } KActionCollection *KMMainWidget::actionCollection() const { return mActionCollection; } 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 (!showSearchDialog()) { 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()) { MessageList::Core::QuickSearchLine::SearchOptions options = mMessagePane->currentOptions(); QByteArray searchStringVal; if (options & MessageList::Core::QuickSearchLine::SearchEveryWhere) { searchStringVal = QByteArrayLiteral(""); } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstSubject) { searchStringVal = QByteArrayLiteral("subject"); } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstBody) { searchStringVal = QByteArrayLiteral(""); } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstFrom) { searchStringVal = QByteArrayLiteral("from"); } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstBcc) { searchStringVal = QByteArrayLiteral("bcc"); } else if (options & MessageList::Core::QuickSearchLine::SearchAgainstTo) { searchStringVal = QByteArrayLiteral("to"); } else { searchStringVal = QByteArrayLiteral(""); } pattern.append(SearchRule::createInstance(searchStringVal, SearchRule::FuncContains, searchString)); const QVector statusList = mMessagePane->currentFilterStatus(); for (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; } QWidget *KMMainWidget::zoomLabelIndicator() const { return mZoomLabelIndicator; } FolderTreeView *KMMainWidget::folderTreeView() const { return mFolderTreeWidget->folderTreeView(); } KXMLGUIClient *KMMainWidget::guiClient() const { return mGUIClient; } KMail::TagActionManager *KMMainWidget::tagActionManager() const { return mTagActionManager; } KMail::FolderShortcutActionManager *KMMainWidget::folderShortcutActionManager() const { return mFolderShortcutActionManager; } void KMMainWidget::slotMessageSelected(const Akonadi::Item &item) { delete mShowBusySplashTimer; mShowBusySplashTimer = 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 = mMsgView->viewer()->createFetchJob(item); if (mCurrentCollection.isValid()) { const QString resource = mCurrentCollection.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 = 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; } } Akonadi::Item copyItem(item); if (mCurrentCollection.isValid()) { copyItem.setParentCollection(mCurrentCollection); } mMsgView->setMessage(copyItem); assignLoadExternalReference(); mMsgView->setDecryptMessageOverwrite(false); mMsgActions->setCurrentMessage(copyItem); } void KMMainWidget::itemsFetchDone(KJob *job) { delete mShowBusySplashTimer; mShowBusySplashTimer = 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 mMessagePane->show(); if (mMsgView) { mMsgView->viewer()->enableMessageDisplay(); mMsgView->clear(true); if (kmkernel->isOffline()) { showOfflinePage(); } else { showResourceOfflinePage(); } } } else { // Some other error showMessageActivities(job->errorString()); } } } QAction *KMMainWidget::akonadiStandardAction(Akonadi::StandardActionManager::Type type) { return mAkonadiStandardActionManager->action(type); } QAction *KMMainWidget::akonadiStandardAction(Akonadi::StandardMailActionManager::Type type) { return mAkonadiStandardActionManager->action(type); } StandardMailActionManager *KMMainWidget::standardMailActionManager() const { return mAkonadiStandardActionManager; } void KMMainWidget::slotRemoveDuplicates() { RemoveDuplicateMailJob *job = new RemoveDuplicateMailJob(mFolderTreeWidget->folderTreeView()->selectionModel(), this, this); job->start(); } void KMMainWidget::slotServerSideSubscription() { if (!mCurrentCollection.isValid()) { return; } PimCommon::ManageServerSideSubscriptionJob *job = new PimCommon::ManageServerSideSubscriptionJob(this); job->setCurrentCollection(mCurrentCollection); job->setParentWidget(this); job->start(); } void KMMainWidget::slotAccountSettings() { if (!mCurrentCollection.isValid() || mCurrentCollection.parentCollection() != Akonadi::Collection::root()) { return; } auto instance = Akonadi::AgentManager::self()->instance(mCurrentCollection.resource()); if (!instance.isValid()) { return; } instance.configure(this); } void KMMainWidget::slotRestartAccount() { if (!mCurrentCollection.isValid() || mCurrentCollection.parentCollection() != Akonadi::Collection::root()) { return; } auto instance = Akonadi::AgentManager::self()->instance(mCurrentCollection.resource()); if (!instance.isValid()) { return; } instance.restart(); } 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::slotOpenRecentMessage(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() && mCurrentCollection.isValid()) { KMTrashMsgCommand *command = new KMTrashMsgCommand(mCurrentCollection, messageView()->viewer()->messageItem(), -1); command->start(); } } void KMMainWidget::slotArchiveMails() { if (mCurrentCollection.isValid()) { const Akonadi::Item::List selectedMessages = mMessagePane->selectionAsMessageItemList(); KMKernel::self()->folderArchiveManager()->setArchiveItems(selectedMessages, mCurrentCollection.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"), 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); } writeFolderConfig(); } void KMMainWidget::populateMessageListStatusFilterCombo() { mMessagePane->populateStatusFilterCombo(); } void KMMainWidget::slotCollectionRemoved(const Akonadi::Collection &col) { if (mFavoritesModel) { mFavoritesModel->removeCollection(col); } } void KMMainWidget::slotMarkAllMessageAsReadInCurrentFolderAndSubfolder() { if (mCurrentCollection.isValid()) { MarkAllMessagesAsReadInFolderAndSubFolderJob *job = new MarkAllMessagesAsReadInFolderAndSubFolderJob(this); job->setTopLevelCollection(mCurrentCollection); job->start(); } } void KMMainWidget::slotRemoveDuplicateRecursive() { if (mCurrentCollection.isValid()) { RemoveDuplicateMessageInFolderAndSubFolderJob *job = new RemoveDuplicateMessageInFolderAndSubFolderJob(this, this); job->setTopLevelCollection(mCurrentCollection); job->start(); } } void KMMainWidget::slotUpdateConfig() { readFolderConfig(); updateHtmlMenuEntry(); } void KMMainWidget::printCurrentMessage(bool preview) { bool result = false; if (messageView() && messageView()->viewer()) { if (MessageViewer::MessageViewerSettings::self()->printSelectedText()) { result = messageView()->printSelectedText(preview); } } if (!result) { const bool useFixedFont = MessageViewer::MessageViewerSettings::self()->useFixedFont(); const QString overrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding(); const Akonadi::Item currentItem = messageView()->viewer()->messageItem(); KMPrintCommandInfo commandInfo; commandInfo.mMsg = currentItem; commandInfo.mHeaderStylePlugin = messageView()->viewer()->headerStylePlugin(); commandInfo.mFormat = messageView()->viewer()->displayFormatMessageOverwrite(); commandInfo.mHtmlLoadExtOverride = messageView()->viewer()->htmlLoadExternal(); commandInfo.mPrintPreview = preview; commandInfo.mUseFixedFont = useFixedFont; commandInfo.mOverrideFont = overrideEncoding; commandInfo.mShowSignatureDetails = messageView()->viewer()->showSignatureDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails(); commandInfo.mShowEncryptionDetails = messageView()->viewer()->showEncryptionDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails(); KMPrintCommand *command = new KMPrintCommand(this, commandInfo); command->start(); } } void KMMainWidget::slotRedirectCurrentMessage() { if (messageView() && messageView()->viewer()) { const Akonadi::Item currentItem = messageView()->viewer()->messageItem(); if (!currentItem.hasPayload()) { return; } KMCommand *command = new KMRedirectCommand(this, currentItem); command->start(); } } void KMMainWidget::replyCurrentMessageCommand(MessageComposer::ReplyStrategy strategy) { if (messageView() && messageView()->viewer()) { Akonadi::Item currentItem = messageView()->viewer()->messageItem(); if (!currentItem.hasPayload()) { return; } const QString text = messageView()->copyText(); KMReplyCommand *command = new KMReplyCommand(this, currentItem, strategy, text); command->start(); } } void KMMainWidget::slotReplyMessageTo(const KMime::Message::Ptr &message, bool replyToAll) { Akonadi::Item item; item.setPayload(message); item.setMimeType(KMime::Message::mimeType()); KMReplyCommand *command = new KMReplyCommand(this, item, replyToAll ? MessageComposer::ReplyAll : MessageComposer::ReplyAuthor); command->start(); } void KMMainWidget::showMessageActivities(const QString &str) { BroadcastStatus::instance()->setStatusMsg(str); PimCommon::LogActivitiesManager::self()->appendLog(str); } void KMMainWidget::slotCopyDecryptedTo(QAction *action) { if (action) { const auto index = action->data().toModelIndex(); const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value(); auto command = new KMCopyDecryptedCommand(collection, mMessagePane->selectionAsMessageItemList()); command->start(); } } void KMMainWidget::slotSetFocusToViewer() { if (messageView() && messageView()->viewer()) { messageView()->viewer()->setFocus(); } } void KMMainWidget::setShowStatusBarMessage(const QString &msg) { if (mCurrentStatusBar) { mCurrentStatusBar->showMessage(msg); } } void KMMainWidget::setupUnifiedMailboxChecker() { if (!KMailSettings::self()->askEnableUnifiedMailboxes()) { return; } const auto ask = [this]() { if (!KMailSettings::self()->askEnableUnifiedMailboxes()) { return; } if (kmkernel->accounts().count() <= 1) { return; } KMailSettings::self()->setAskEnableUnifiedMailboxes(false); const auto service = Akonadi::ServerManager::self()->agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_unifiedmailbox_agent")); QDBusInterface iface(service, QStringLiteral("/"), QStringLiteral("org.freedesktop.Akonadi.UnifiedMailboxAgent"), QDBusConnection::sessionBus(), this); if (!iface.isValid()) { return; } QDBusReply reply = iface.call(QStringLiteral("enabledAgent")); if (!reply.isValid() || bool(reply)) { return; } const auto answer = KMessageBox::questionYesNo( this, i18n("You have more than one email account set up.\nDo you want to enable the Unified Mailbox feature to " "show unified content of your inbox, sent and drafts folders?\n" "You can configure unified mailboxes, create custom ones or\ndisable the feature completely in KMail's Plugin settings."), i18n("Enable Unified Mailboxes?"), KGuiItem(i18n("Enable Unified Mailboxes"), QStringLiteral("dialog-ok")), KGuiItem(i18n("Cancel"), QStringLiteral("dialog-cancel"))); if (answer == KMessageBox::Yes) { iface.call(QStringLiteral("setEnableAgent"), true); } }; connect(kmkernel, &KMKernel::incomingAccountsChanged, this, ask); // Wait for a bit before asking so we at least have the window on screen QTimer::singleShot(500, this, ask); } diff --git a/src/kmmainwidget.h b/src/kmmainwidget.h index 6a73024a1..039d5d946 100644 --- a/src/kmmainwidget.h +++ b/src/kmmainwidget.h @@ -1,654 +1,654 @@ /* 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 KMAIL_KMMAINWIDGET #define KMAIL_KMMAINWIDGET #include "kmail_export.h" #include "kmreaderwin.h" //for inline actions #include "kmkernel.h" // for access to config #include "mailcommon/foldertreewidget.h" -#include +#include #include "messageactions.h" -#include +#include #include #include "messageviewer/config-messageviewer.h" #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 KMTrashMsgCommand; class KRecentFilesAction; class ManageShowCollectionProperties; class KActionMenuTransport; class KActionMenuAccount; class ZoomLabelWidget; namespace KIO { class Job; } namespace KMail { class SearchWindow; class VacationScriptIndicatorWidget; class TagActionManager; class FolderShortcutActionManager; } namespace KSieveUi { class SieveImapPasswordProvider; class ManageSieveScriptsDialog; class VacationManager; } #ifdef USE_DKIM_CHECKER namespace MessageViewer { class DKIMWidgetInfo; } #endif namespace MailCommon { class FolderSelectionDialog; class FavoriteCollectionWidget; class MailFilter; } class QStatusBar; class KMAIL_EXPORT KMMainWidget : public QWidget { Q_OBJECT public: typedef QList PtrList; KMMainWidget(QWidget *parent, KXMLGUIClient *aGUIClient, KActionCollection *actionCollection, const KSharedConfig::Ptr &config = KMKernel::self()->config()); ~KMMainWidget() override; 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; Akonadi::Collection currentCollection() const; QSharedPointer currentFolder() const; static void cleanup(); QAction *action(const QString &name); QAction *sendQueuedAction() const; KActionMenuTransport *sendQueueViaMenu() const; /** 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; #ifdef USE_DKIM_CHECKER QWidget *dkimWidgetInfo() const; #endif MailCommon::FolderTreeView *folderTreeView() const; /** Returns the XML GUI client. */ KXMLGUIClient *guiClient() const; KMail::TagActionManager *tagActionManager() const; KMail::FolderShortcutActionManager *folderShortcutActionManager() const; void savePaneSelection(); void updatePaneTagComboBox(); void addRecentFile(const QUrl &mUrl); void updateQuickSearchLineText(); void populateMessageListStatusFilterCombo(); void initializePluginActions(); Q_REQUIRED_RESULT Akonadi::Item::List currentSelection() const; Q_REQUIRED_RESULT QString fullCollectionPath() const; void initializeFilterActions(bool clearFilter); /** Clear and create actions for marked filters */ void clearFilterActions(); /** * 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(). */ Q_REQUIRED_RESULT QList actionCollections() const; void refreshMessageListSelection(); Akonadi::StandardMailActionManager *standardMailActionManager() const; QAction *akonadiStandardAction(Akonadi::StandardActionManager::Type type); QAction *akonadiStandardAction(Akonadi::StandardMailActionManager::Type type); QWidget *zoomLabelIndicator() const; void clearPluginActions(); public Q_SLOTS: /** 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 &); /** Adds if not existing/removes if existing the tag identified by @p aLabel in all selected messages */ void slotUpdateMessageTagList(const Akonadi::Tag &tag); void slotSelectCollectionFolder(const Akonadi::Collection &col); void slotUpdateConfig(); Q_SIGNALS: void messagesTransfered(bool); void captionChangeRequest(const QString &caption); void recreateGui(); protected: void showEvent(QShowEvent *event) override; private: void assignLoadExternalReference(); KMail::MessageActions *messageActions() const; KActionMenu *filterMenu() const; KActionMenu *mailingListActionMenu() const; // 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(); /** Trigger the dialog for editing out-of-office scripts. */ void slotEditVacation(const QString &serverName); void slotStartCheckMail(); void slotEndCheckMail(); void restoreCollectionFolderViewConfig(); /** Update message actions */ void updateMessageActions(bool fast = false); void updateMessageActionsDelayed(); /** Update message menu */ void updateMessageMenu(); void slotRemoveDuplicates(); /** Select the given folder If the folder is 0 the intro is shown */ void folderSelected(const Akonadi::Collection &col); /** Start a timer to update message actions */ void startUpdateMessageActionsTimer(); void slotSelectMoreMessageTagList(); 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); KActionCollection *actionCollection() const; /** @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(); void checkAkonadiServerManagerState(); void updateHtmlMenuEntry(); void updateMoveAction(const Akonadi::CollectionStatistics &statistic); void updateMoveAction(bool hasUnreadMails); void updateAllToTrashAction(qint64 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); void applyFilters(const Akonadi::Collection::List &selectedCols); void applyFilter(const Akonadi::Collection::List &selectedCols, const QString &filter); /** * 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, const WebEngineViewer::WebHitTestResult &result); void setZoomChanged(qreal zoomFactor); private Q_SLOTS: void updateFileMenu(); 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 slotUseTemplate(); void slotTrashThread(); void slotDeleteThread(bool confirmDelete); // completely delete thread void slotUndo(); void slotReadOn(); void slotSaveMsg(); void slotOpenMsg(); void slotSaveAttachments(); void slotJumpToFolder(); void slotCheckVacation(); void slotDebugSieve(); void slotApplyFilters(); void slotApplyFiltersOnFolder(bool recursive); void slotApplyFilterOnFolder(bool recursive); 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 &aMsg, const WebEngineViewer::WebHitTestResult &result, const QPoint &aPoint); void slotContactSearchJobForMessagePopupDone(KJob *job); void slotSelectAllMessages(); void slotFocusQuickSearch(); void slotIntro(); void slotShowStartupFolder(); void slotCopyDecryptedTo(QAction *action); /** 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 slotForwardAttachedMessage(); void slotRedirectMessage(); 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 slotOpenRecentMessage(const QUrl &url); void slotMoveMessageToTrash(); /** * Called when a "move to trash" operation is completed */ void slotTrashMessagesCompleted(KMTrashMsgCommand *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 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(); void slotRemoveDuplicateRecursive(); void slotRedirectCurrentMessage(); void slotEditCurrentVacation(); void slotReplyMessageTo(const KMime::Message::Ptr &message, bool replyToAll); private: void slotSetFocusToViewer(); void deleteSelectedMessages(bool confirmDelete); // completely delete message bool showSearchDialog(); void clearCurrentFolder(); void setCurrentCollection(const Akonadi::Collection &col); void showMessageActivities(const QString &str); void slotPageIsScrolledToBottom(bool isAtBottom); void printCurrentMessage(bool preview); void replyCurrentMessageCommand(MessageComposer::ReplyStrategy strategy); void setupUnifiedMailboxChecker(); QAction *filterToAction(MailCommon::MailFilter *filter); Akonadi::Collection::List applyFilterOnCollection(bool recursive); void setShowStatusBarMessage(const QString &msg); void slotRestartAccount(); void slotAccountSettings(); // Message actions QAction *mDeleteAction = nullptr; QAction *mTrashThreadAction = nullptr; QAction *mDeleteThreadAction = nullptr; QAction *mSaveAsAction = nullptr; QAction *mApplyAllFiltersAction = nullptr; QAction *mSaveAttachmentsAction = nullptr; QAction *mOpenAction = nullptr; QAction *mMoveMsgToFolderAction = nullptr; QAction *mCollectionProperties = nullptr; QAction *mSendQueued = nullptr; QAction *mArchiveAction = nullptr; QAction *mSelectAllMessages = nullptr; KActionMenuTransport *mSendActionMenu = nullptr; QAction *mRestartAccountSettings = nullptr; // Filter actions KActionMenu *mFilterMenu = nullptr; QAction *mExpireConfigAction = nullptr; KActionMenu *mApplyFilterFolderActionsMenu = nullptr; KActionMenu *mApplyFilterFolderRecursiveActionsMenu = nullptr; QAction *mApplyAllFiltersFolderAction = nullptr; QAction *mApplyAllFiltersFolderRecursiveAction = nullptr; // Custom template actions menu KActionMenu *mTemplateMenu = nullptr; KActionMenu *mThreadStatusMenu = nullptr; KActionMenu *mApplyFilterActionsMenu = nullptr; QAction *mCopyActionMenu = nullptr; QAction *mMoveActionMenu = nullptr; QAction *mCopyDecryptedActionMenu = nullptr; QAction *mMarkThreadAsReadAction = nullptr; QAction *mMarkThreadAsUnreadAction = nullptr; KToggleAction *mToggleThreadImportantAction = nullptr; KToggleAction *mToggleThreadToActAction = nullptr; KToggleAction *mWatchThreadAction = nullptr; KToggleAction *mIgnoreThreadAction = nullptr; MailCommon::FavoriteCollectionWidget *mFavoriteCollectionsView = nullptr; Akonadi::FavoriteCollectionsModel *mFavoritesModel = nullptr; KMReaderWin *mMsgView = nullptr; QSplitter *mSplitter1 = nullptr; QSplitter *mSplitter2 = nullptr; QSplitter *mFolderViewSplitter = nullptr; Akonadi::Collection mTemplateFolder; bool mLongFolderList = false; bool mStartupDone = false; bool mWasEverShown = false; bool mHtmlGlobalSetting = false; bool mHtmlLoadExtGlobalSetting = false; bool mFolderHtmlLoadExtPreference = false; bool mReaderWindowActive = false; bool mReaderWindowBelow = false; bool mEnableFavoriteFolderView = false; bool mEnableFolderQuickSearch = false; QPointer mSearchWin; QAction *mExpireFolderAction = nullptr; QAction *mFolderMailingListPropertiesAction = nullptr; QAction *mShowFolderShortcutDialogAction = nullptr; QAction *mArchiveFolderAction = nullptr; QAction *mMessageNewList = nullptr; KToggleAction *mPreferHtmlLoadExtAction = nullptr; QTimer *menutimer = nullptr; QTimer *mShowBusySplashTimer = nullptr; KSieveUi::VacationManager *mVacationManager = nullptr; KActionCollection *mActionCollection = nullptr; QAction *mToolbarActionSeparator = nullptr; QVBoxLayout *mTopLayout = nullptr; bool mDestructed = false; QList mFilterMenuActions; QList mFilterFolderMenuActions; QList mFilterFolderMenuRecursiveActions; QList mFilterTBarActions; QList mFilterCommands; KMail::TagActionManager *mTagActionManager = nullptr; KMail::FolderShortcutActionManager *mFolderShortcutActionManager = nullptr; KSharedConfig::Ptr mConfig; KXMLGUIClient *mGUIClient = nullptr; KMail::MessageActions *mMsgActions = nullptr; Akonadi::StandardMailActionManager *mAkonadiStandardActionManager = nullptr; CollectionPane *mMessagePane = nullptr; QSharedPointer mCurrentFolderSettings; MailCommon::FolderTreeWidget *mFolderTreeWidget = nullptr; KMail::VacationScriptIndicatorWidget *mVacationScriptIndicator = nullptr; bool mVacationIndicatorActive = false; bool mGoToFirstUnreadMessageInSelectedFolder = false; MessageList::Core::PreSelectionMode mPreSelectionMode; QTimer mCheckMailTimer; KSieveUi::SieveImapPasswordProvider *mSievePasswordProvider = nullptr; QPointer mMoveOrCopyToDialog; QPointer mSelectFromAllFoldersDialog; QAction *mServerSideSubscription = nullptr; QAction *mAccountSettings = nullptr; KRecentFilesAction *mOpenRecentAction = nullptr; QPointer mManageSieveDialog; QAction *mQuickSearchAction = nullptr; DisplayMessageFormatActionMenu *mDisplayMessageFormatMenu = nullptr; MessageViewer::Viewer::DisplayFormatMessage mFolderDisplayFormatPreference = MessageViewer::Viewer::UseGlobalSetting; QAction *mSearchMessages = nullptr; KMLaunchExternalComponent *mLaunchExternalComponent = nullptr; ManageShowCollectionProperties *mManageShowCollectionProperties = nullptr; QAction *mShowIntroductionAction = nullptr; KToggleAction *mUseLessBandwidth = nullptr; QAction *mMarkAllMessageAsReadAndInAllSubFolder = nullptr; KActionMenuAccount *mAccountActionMenu = nullptr; QAction *mRemoveDuplicateRecursiveAction = nullptr; Akonadi::Collection mCurrentCollection; QStatusBar *mCurrentStatusBar = nullptr; ZoomLabelWidget *mZoomLabelIndicator = nullptr; }; #endif diff --git a/src/kmmainwin.cpp b/src/kmmainwin.cpp index 5acdbbd09..8171aadc2 100644 --- a/src/kmmainwin.cpp +++ b/src/kmmainwin.cpp @@ -1,227 +1,227 @@ /* * kmail: KDE mail client * Copyright (c) 1996-1998 Stefan Taferner * * 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 "kmmainwin.h" #include "kmmainwidget.h" #include #include #include "libkdepim/broadcaststatus.h" #include "util.h" #include "tag/tagactionmanager.h" #include #include #include #include #include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "kmail_debug.h" #include #include KMMainWin::KMMainWin(QWidget *) : KXmlGuiWindow(nullptr) { setObjectName(QStringLiteral("kmail-mainwindow#")); // Set this to be the group leader for all subdialogs - this means // modal subdialogs will only affect this dialog, not the other windows setAttribute(Qt::WA_GroupLeader); resize(700, 500); // The default size mKMMainWidget = new KMMainWidget(this, this, actionCollection()); connect(mKMMainWidget, &KMMainWidget::recreateGui, this, &KMMainWin::slotUpdateGui); setCentralWidget(mKMMainWidget); setupStatusBar(); if (!kmkernel->xmlGuiInstanceName().isEmpty()) { setComponentName(kmkernel->xmlGuiInstanceName(), i18n("KMail2")); } setStandardToolBarMenuEnabled(true); KStandardAction::configureToolbars(this, &KMMainWin::slotEditToolbars, actionCollection()); KStandardAction::keyBindings(this, &KMMainWin::slotConfigureShortcuts, actionCollection()); mHideMenuBarAction = KStandardAction::showMenubar(this, &KMMainWin::slotToggleMenubar, actionCollection()); KStandardAction::quit(this, &KMMainWin::slotQuit, actionCollection()); createGUI(QStringLiteral("kmmainwin.rc")); //must be after createGUI, otherwise e.g toolbar settings are not loaded setAutoSaveSettings(KMKernel::self()->config()->group("Main Window")); connect(KPIM::BroadcastStatus::instance(), &KPIM::BroadcastStatus::statusMsg, this, &KMMainWin::displayStatusMessage); connect(mKMMainWidget, &KMMainWidget::captionChangeRequest, this, qOverload(&KMainWindow::setCaption)); mKMMainWidget->updateQuickSearchLineText(); mHideMenuBarAction->setChecked(KMailSettings::self()->showMenuBar()); slotToggleMenubar(true); } KMMainWin::~KMMainWin() { // Avoids a crash if there are any Akonadi jobs running, which may // attempt to display a status message when they are killed. disconnect(KPIM::BroadcastStatus::instance(), &KPIM::BroadcastStatus::statusMsg, this, nullptr); } KMMainWidget *KMMainWin::mainKMWidget() const { return mKMMainWidget; } void KMMainWin::displayStatusMessage(const QString &aText) { if (!statusBar() || !mProgressBar->littleProgress()) { return; } const int statusWidth = statusBar()->width() - mProgressBar->littleProgress()->width() - fontMetrics().maxWidth(); const QString text = fontMetrics().elidedText(QLatin1Char(' ') + aText, Qt::ElideRight, statusWidth); // ### FIXME: We should disable richtext/HTML (to avoid possible denial of service attacks), // but this code would double the size of the status bar if the user hovers // over an -style email address :-( // text.replace("&", "&"); // text.replace("<", "<"); // text.replace(">", ">"); mMessageLabel->setText(text); mMessageLabel->setToolTip(aText); } void KMMainWin::slotToggleMenubar(bool dontShowWarning) { if (menuBar()) { if (mHideMenuBarAction->isChecked()) { menuBar()->show(); } else { if (!dontShowWarning) { const QString accel = mHideMenuBarAction->shortcut().toString(); KMessageBox::information(this, i18n("This will hide the menu bar completely." " You can show it again by typing %1.", accel), i18n("Hide menu bar"), QStringLiteral("HideMenuBarWarning")); } menuBar()->hide(); } KMailSettings::self()->setShowMenuBar(mHideMenuBarAction->isChecked()); } } void KMMainWin::slotEditToolbars() { KConfigGroup grp = KMKernel::self()->config()->group("Main Window"); saveMainWindowSettings(grp); QPointer dlg = new KEditToolBar(guiFactory(), this); connect(dlg.data(), &KEditToolBar::newToolBarConfig, this, &KMMainWin::slotUpdateGui); dlg->exec(); delete dlg; } void KMMainWin::slotUpdateGui() { // remove dynamically created actions before editing mKMMainWidget->clearFilterActions(); mKMMainWidget->tagActionManager()->clearActions(); mKMMainWidget->clearPluginActions(); createGUI(QStringLiteral("kmmainwin.rc")); applyMainWindowSettings(KMKernel::self()->config()->group("Main Window")); // plug dynamically created actions again mKMMainWidget->initializeFilterActions(false); mKMMainWidget->tagActionManager()->createActions(); //FIXME mKMMainWidget->initializePluginActions(); } void KMMainWin::setupStatusBar() { /* Create a progress dialog and hide it. */ mProgressBar = new KPIM::ProgressStatusBarWidget(statusBar(), this); mMessageLabel = new QLabel(i18n("Starting...")); mMessageLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); statusBar()->addWidget(mMessageLabel); QTimer::singleShot(2000, KPIM::BroadcastStatus::instance(), &KPIM::BroadcastStatus::reset); #ifdef USE_DKIM_CHECKER statusBar()->addPermanentWidget(mKMMainWidget->dkimWidgetInfo()); #endif statusBar()->addPermanentWidget(mKMMainWidget->zoomLabelIndicator()); statusBar()->addPermanentWidget(mKMMainWidget->vacationScriptIndicator()); statusBar()->addPermanentWidget(mProgressBar->littleProgress()); } void KMMainWin::slotQuit() { mReallyClose = true; close(); } //----------------------------------------------------------------------------- bool KMMainWin::restoreDockedState(int n) { // Default restore behavior is to show the window once it is restored. // Override this if the main window was hidden in the system tray // when the session was saved. KConfigGroup config(KConfigGui::sessionConfig(), QString::number(n)); bool show = !config.readEntry("docked", false); return KMainWindow::restore(n, show); } void KMMainWin::saveProperties(KConfigGroup &config) { // This is called by the session manager on log-off // Save the shown/hidden status so we can restore to the same state. KMainWindow::saveProperties(config); config.writeEntry("docked", isHidden()); } bool KMMainWin::queryClose() { if (kmkernel->shuttingDown() || qApp->isSavingSession() || mReallyClose) { return true; } return kmkernel->canQueryClose(); } void KMMainWin::slotConfigureShortcuts() { if (guiFactory()->configureShortcuts()) { mKMMainWidget->updateQuickSearchLineText(); } } diff --git a/src/kmreadermainwin.cpp b/src/kmreadermainwin.cpp index f0ad08125..64bebae91 100644 --- a/src/kmreadermainwin.cpp +++ b/src/kmreadermainwin.cpp @@ -1,806 +1,806 @@ /* This file is part of KMail, the KDE mail client. Copyright (c) 2002 Don Sanders Copyright (c) 2011-2019 Montel Laurent 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 */ // // A toplevel KMainWindow derived class for displaying // single messages or single message parts. // // Could be extended to include support for normal main window // widgets like a toolbar. #include "kmreadermainwin.h" #include "kmreaderwin.h" #include "widgets/zoomlabelwidget.h" #include "kmmainwidget.h" -#include -#include +#include +#include #include -#include +#include #include -#include -#include -#include -#include +#include +#include +#include +#include #include "kmail_debug.h" #include #include #include #include "kmcommands.h" #include #include #include #include "messageactions.h" #include "util.h" #include "mailcommon/mailkernel.h" #include #include "messageviewer/headerstyleplugin.h" #include "messageviewer/headerstyle.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kpimtextedit/texttospeech.h" #include #ifdef USE_DKIM_CHECKER #include #endif using namespace MailCommon; KMReaderMainWin::KMReaderMainWin(MessageViewer::Viewer::DisplayFormatMessage format, bool htmlLoadExtDefault, const QString &name) : KMail::SecondaryWindow(!name.isEmpty() ? name : QStringLiteral("readerwindow#")) { mReaderWin = new KMReaderWin(this, this, actionCollection()); mReaderWin->setDisplayFormatMessageOverwrite(format); mReaderWin->setHtmlLoadExtDefault(htmlLoadExtDefault); mReaderWin->setDecryptMessageOverwrite(true); initKMReaderMainWin(); } KMReaderMainWin::KMReaderMainWin(const QString &name) : KMail::SecondaryWindow(!name.isEmpty() ? name : QStringLiteral("readerwindow#")) { mReaderWin = new KMReaderWin(this, this, actionCollection()); initKMReaderMainWin(); } KMReaderMainWin::KMReaderMainWin(KMime::Content *aMsgPart, MessageViewer::Viewer::DisplayFormatMessage format, const QString &encoding, const QString &name) : KMail::SecondaryWindow(!name.isEmpty() ? name : QStringLiteral("readerwindow#")) { mReaderWin = new KMReaderWin(this, this, actionCollection()); mReaderWin->setOverrideEncoding(encoding); mReaderWin->setDisplayFormatMessageOverwrite(format); mReaderWin->setMsgPart(aMsgPart); initKMReaderMainWin(); } void KMReaderMainWin::initKMReaderMainWin() { setCentralWidget(mReaderWin); setupAccel(); setupGUI(Keys | StatusBar | Create, QStringLiteral("kmreadermainwin.rc")); mMsgActions->setupForwardingActionsList(this); applyMainWindowSettings(KMKernel::self()->config()->group("Separate Reader Window")); mZoomLabelIndicator = new ZoomLabelWidget(statusBar()); statusBar()->addPermanentWidget(mZoomLabelIndicator); setZoomChanged(mReaderWin->viewer()->webViewZoomFactor()); statusBar()->addPermanentWidget(mReaderWin->viewer()->dkimWidgetInfo()); if (!mReaderWin->message().isValid()) { menuBar()->hide(); toolBar(QStringLiteral("mainToolBar"))->hide(); } else { slotToggleMenubar(true); } connect(kmkernel, &KMKernel::configChanged, this, &KMReaderMainWin::slotConfigChanged); connect(mReaderWin, &KMReaderWin::showStatusBarMessage, this, &KMReaderMainWin::slotShowMessageStatusBar); connect(mReaderWin, &KMReaderWin::zoomChanged, this, &KMReaderMainWin::setZoomChanged); connect(mReaderWin, &KMReaderWin::showPreviousMessage, this, &KMReaderMainWin::showPreviousMessage); connect(mReaderWin, &KMReaderWin::showNextMessage, this, &KMReaderMainWin::showNextMessage); } KMReaderMainWin::~KMReaderMainWin() { KConfigGroup grp(KMKernel::self()->config()->group("Separate Reader Window")); saveMainWindowSettings(grp); } void KMReaderMainWin::setZoomChanged(qreal zoomFactor) { if (mZoomLabelIndicator) { mZoomLabelIndicator->setZoom(zoomFactor); } } void KMReaderMainWin::slotShowMessageStatusBar(const QString &msg) { statusBar()->showMessage(msg); } void KMReaderMainWin::setUseFixedFont(bool useFixedFont) { mReaderWin->setUseFixedFont(useFixedFont); } MessageViewer::Viewer *KMReaderMainWin::viewer() const { return mReaderWin->viewer(); } void KMReaderMainWin::showMessage(const QString &encoding, const Akonadi::Item &msg, const Akonadi::Collection &parentCollection) { mParentCollection = parentCollection; mReaderWin->setOverrideEncoding(encoding); mReaderWin->setMessage(msg, MimeTreeParser::Force); KMime::Message::Ptr message = MessageComposer::Util::message(msg); QString caption; if (message) { caption = message->subject()->asUnicodeString(); } if (mParentCollection.isValid()) { caption += QLatin1String(" - "); caption += MailCommon::Util::fullCollectionPath(mParentCollection); } if (!caption.isEmpty()) { setCaption(caption); } mMsg = msg; mMsgActions->setCurrentMessage(msg); const bool canChange = mParentCollection.isValid() ? static_cast(mParentCollection.rights() & Akonadi::Collection::CanDeleteItem) : false; mTrashAction->setEnabled(canChange); if (mReaderWin->viewer() && mReaderWin->viewer()->headerStylePlugin() && mReaderWin->viewer()->headerStylePlugin()->headerStyle()) { mReaderWin->viewer()->headerStylePlugin()->headerStyle()->setReadOnlyMessage(!canChange); } const bool isInTrashFolder = mParentCollection.isValid() ? CommonKernel->folderIsTrash(mParentCollection) : false; QAction *moveToTrash = actionCollection()->action(QStringLiteral("move_to_trash")); KMail::Util::setActionTrashOrDelete(moveToTrash, isInTrashFolder); updateActions(); } void KMReaderMainWin::updateButtons() { if (mListMessage.count() <= 1) { return; } mReaderWin->updateShowMultiMessagesButton((mCurrentMessageIndex > 0), (mCurrentMessageIndex < (mListMessage.count() - 1))); } void KMReaderMainWin::showNextMessage() { if (mCurrentMessageIndex >= (mListMessage.count() - 1)) { return; } mCurrentMessageIndex++; initializeMessage(mListMessage.at(mCurrentMessageIndex)); updateButtons(); } void KMReaderMainWin::showPreviousMessage() { if (mCurrentMessageIndex <= 0) { return; } mCurrentMessageIndex--; initializeMessage(mListMessage.at(mCurrentMessageIndex)); updateButtons(); } void KMReaderMainWin::showMessage(const QString &encoding, const QList &message) { if (message.isEmpty()) { return; } mListMessage = message; mReaderWin->setOverrideEncoding(encoding); mCurrentMessageIndex = 0; initializeMessage(mListMessage.at(mCurrentMessageIndex)); mReaderWin->hasMultiMessages(message.count() > 1); updateButtons(); } void KMReaderMainWin::initializeMessage(const KMime::Message::Ptr &message) { Akonadi::Item item; item.setPayload(message); Akonadi::MessageFlags::copyMessageFlags(*message, item); item.setMimeType(KMime::Message::mimeType()); mMsg = item; mMsgActions->setCurrentMessage(item); mReaderWin->setMessage(message); setCaption(message->subject()->asUnicodeString()); mTrashAction->setEnabled(false); updateActions(); } void KMReaderMainWin::showMessage(const QString &encoding, const KMime::Message::Ptr &message) { if (!message) { return; } const QList lst{message}; showMessage(encoding, lst); } void KMReaderMainWin::updateActions() { if (mHideMenuBarAction->isChecked()) { menuBar()->show(); } toolBar(QStringLiteral("mainToolBar"))->show(); if (mMsg.isValid()) { mTagActionManager->updateActionStates(1, mMsg); } } void KMReaderMainWin::slotReplyOrForwardFinished() { if (MessageViewer::MessageViewerSettings::self()->closeAfterReplyOrForward()) { close(); } } void KMReaderMainWin::slotSelectMoreMessageTagList() { const Akonadi::Item::List selectedMessages = {mMsg}; if (selectedMessages.isEmpty()) { return; } QPointer dlg = new TagSelectDialog(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(); } delete dlg; } void KMReaderMainWin::slotUpdateMessageTagList(const Akonadi::Tag &tag) { // Create a persistent set from the current thread. const Akonadi::Item::List selectedMessages = {mMsg}; if (selectedMessages.isEmpty()) { return; } toggleMessageSetTag(selectedMessages, tag); } void KMReaderMainWin::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(); } Akonadi::Collection KMReaderMainWin::parentCollection() const { if (mParentCollection.isValid()) { return mParentCollection; } else { return mMsg.parentCollection(); } } void KMReaderMainWin::slotTrashMessage() { if (!mMsg.isValid()) { return; } KMTrashMsgCommand *command = new KMTrashMsgCommand(parentCollection(), mMsg, -1); command->start(); close(); } void KMReaderMainWin::slotForwardInlineMsg() { if (!mReaderWin->message().hasPayload()) { return; } KMCommand *command = nullptr; const Akonadi::Collection parentCol = mReaderWin->message().parentCollection(); if (parentCol.isValid()) { QSharedPointer fd = FolderSettings::forCollection(parentCol, false); if (fd) { command = new KMForwardCommand(this, mReaderWin->message(), fd->identity(), QString(), mReaderWin->copyText()); } else { command = new KMForwardCommand(this, mReaderWin->message(), 0, QString(), mReaderWin->copyText()); } } else { command = new KMForwardCommand(this, mReaderWin->message(), 0, QString(), mReaderWin->copyText()); } connect(command, &KMTrashMsgCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); command->start(); } void KMReaderMainWin::slotForwardAttachedMessage() { if (!mReaderWin->message().hasPayload()) { return; } KMCommand *command = nullptr; const Akonadi::Collection parentCol = mReaderWin->message().parentCollection(); if (parentCol.isValid()) { QSharedPointer fd = FolderSettings::forCollection(parentCol, false); if (fd) { command = new KMForwardAttachedCommand(this, mReaderWin->message(), fd->identity()); } else { command = new KMForwardAttachedCommand(this, mReaderWin->message()); } } else { command = new KMForwardAttachedCommand(this, mReaderWin->message()); } connect(command, &KMTrashMsgCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); command->start(); } void KMReaderMainWin::slotRedirectMessage() { const Akonadi::Item currentItem = mReaderWin->message(); if (!currentItem.hasPayload()) { return; } KMRedirectCommand *command = new KMRedirectCommand(this, currentItem); connect(command, &KMRedirectCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); command->start(); } void KMReaderMainWin::slotCustomReplyToMsg(const QString &tmpl) { const Akonadi::Item currentItem = mReaderWin->message(); if (!currentItem.hasPayload()) { return; } KMReplyCommand *command = new KMReplyCommand(this, currentItem, MessageComposer::ReplySmart, mReaderWin->copyText(), false, tmpl); connect(command, &KMReplyCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); command->start(); } void KMReaderMainWin::slotCustomReplyAllToMsg(const QString &tmpl) { const Akonadi::Item currentItem = mReaderWin->message(); if (!currentItem.hasPayload()) { return; } KMReplyCommand *command = new KMReplyCommand(this, currentItem, MessageComposer::ReplyAll, mReaderWin->copyText(), false, tmpl); connect(command, &KMReplyCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); command->start(); } void KMReaderMainWin::slotCustomForwardMsg(const QString &tmpl) { const Akonadi::Item currentItem = mReaderWin->message(); if (!currentItem.hasPayload()) { return; } KMForwardCommand *command = new KMForwardCommand(this, currentItem, 0, tmpl, mReaderWin->copyText()); connect(command, &KMForwardCommand::completed, this, &KMReaderMainWin::slotReplyOrForwardFinished); command->start(); } void KMReaderMainWin::slotConfigChanged() { //readConfig(); mMsgActions->setupForwardActions(actionCollection()); mMsgActions->setupForwardingActionsList(this); } void KMReaderMainWin::setupAccel() { if (!kmkernel->xmlGuiInstanceName().isEmpty()) { setComponentName(kmkernel->xmlGuiInstanceName(), i18n("KMail2")); } mMsgActions = new KMail::MessageActions(actionCollection(), this); mMsgActions->setMessageView(mReaderWin); connect(mMsgActions, &KMail::MessageActions::replyActionFinished, this, &KMReaderMainWin::slotReplyOrForwardFinished); //----- File Menu mSaveAtmAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-attachment")), i18n("Save A&ttachments..."), actionCollection()); connect(mSaveAtmAction, &QAction::triggered, mReaderWin->viewer(), &MessageViewer::Viewer::slotAttachmentSaveAll); mTrashAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("&Move to Trash"), this); mTrashAction->setIconText(i18nc("@action:intoolbar Move to Trash", "Trash")); KMail::Util::addQActionHelpText(mTrashAction, i18n("Move message to trashcan")); actionCollection()->addAction(QStringLiteral("move_to_trash"), mTrashAction); actionCollection()->setDefaultShortcut(mTrashAction, QKeySequence(Qt::Key_Delete)); connect(mTrashAction, &QAction::triggered, this, &KMReaderMainWin::slotTrashMessage); QAction *closeAction = KStandardAction::close(this, &KMReaderMainWin::close, actionCollection()); QList closeShortcut = closeAction->shortcuts(); closeAction->setShortcuts(closeShortcut << QKeySequence(Qt::Key_Escape)); mTagActionManager = new KMail::TagActionManager(this, actionCollection(), mMsgActions, this); connect(mTagActionManager, &KMail::TagActionManager::tagActionTriggered, this, &KMReaderMainWin::slotUpdateMessageTagList); connect(mTagActionManager, &KMail::TagActionManager::tagMoreActionClicked, this, &KMReaderMainWin::slotSelectMoreMessageTagList); mTagActionManager->createActions(); if (mReaderWin->message().isValid()) { mTagActionManager->updateActionStates(1, mReaderWin->message()); } mHideMenuBarAction = KStandardAction::showMenubar(this, &KMReaderMainWin::slotToggleMenubar, actionCollection()); mHideMenuBarAction->setChecked(KMailSettings::self()->readerShowMenuBar()); //----- Message Menu connect(mReaderWin->viewer(), &MessageViewer::Viewer::displayPopupMenu, this, &KMReaderMainWin::slotMessagePopup); connect(mReaderWin->viewer(), &MessageViewer::Viewer::itemRemoved, this, &QWidget::close); setStandardToolBarMenuEnabled(true); KStandardAction::configureToolbars(this, &KMReaderMainWin::slotEditToolbars, actionCollection()); connect(mReaderWin->viewer(), &MessageViewer::Viewer::moveMessageToTrash, this, &KMReaderMainWin::slotTrashMessage); } void KMReaderMainWin::slotToggleMenubar(bool dontShowWarning) { if (!mReaderWin->message().isValid()) { return; } if (menuBar()) { if (mHideMenuBarAction->isChecked()) { menuBar()->show(); } else { if (!dontShowWarning) { const QString accel = mHideMenuBarAction->shortcut().toString(); KMessageBox::information(this, i18n("This will hide the menu bar completely." " You can show it again by typing %1.", accel), i18n("Hide menu bar"), QStringLiteral("HideMenuBarWarning")); } menuBar()->hide(); } KMailSettings::self()->setReaderShowMenuBar(mHideMenuBarAction->isChecked()); } } QAction *KMReaderMainWin::copyActionMenu(QMenu *menu) { KMMainWidget *mainwin = kmkernel->getKMMainWidget(); if (mainwin) { KActionMenu *action = new KActionMenu(menu); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); action->setText(i18n("Copy Message To...")); mainwin->standardMailActionManager()->standardActionManager()->createActionFolderMenu(action->menu(), Akonadi::StandardActionManager::CopyItemToMenu); connect(action->menu(), &QMenu::triggered, this, &KMReaderMainWin::slotCopyItem); return action; } return nullptr; } QAction *KMReaderMainWin::moveActionMenu(QMenu *menu) { KMMainWidget *mainwin = kmkernel->getKMMainWidget(); if (mainwin) { KActionMenu *action = new KActionMenu(menu); action->setText(i18n("Move Message To...")); mainwin->standardMailActionManager()->standardActionManager()->createActionFolderMenu(action->menu(), Akonadi::StandardActionManager::MoveItemToMenu); connect(action->menu(), &QMenu::triggered, this, &KMReaderMainWin::slotMoveItem); return action; } return nullptr; } void KMReaderMainWin::slotMoveItem(QAction *action) { if (action) { const QModelIndex index = action->data().toModelIndex(); const Akonadi::Collection collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value(); copyOrMoveItem(collection, true); } } void KMReaderMainWin::copyOrMoveItem(const Akonadi::Collection &collection, bool move) { if (mMsg.isValid()) { if (move) { Akonadi::ItemMoveJob *job = new Akonadi::ItemMoveJob(mMsg, collection, this); connect(job, &KJob::result, this, &KMReaderMainWin::slotCopyMoveResult); } else { Akonadi::ItemCopyJob *job = new Akonadi::ItemCopyJob(mMsg, collection, this); connect(job, &KJob::result, this, &KMReaderMainWin::slotCopyMoveResult); } } else { Akonadi::ItemCreateJob *job = new Akonadi::ItemCreateJob(mMsg, collection, this); connect(job, &KJob::result, this, &KMReaderMainWin::slotCopyMoveResult); } } void KMReaderMainWin::slotCopyItem(QAction *action) { if (action) { const QModelIndex index = action->data().toModelIndex(); const Akonadi::Collection collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value(); copyOrMoveItem(collection, false); } } void KMReaderMainWin::slotCopyMoveResult(KJob *job) { if (job->error()) { KMessageBox::sorry(this, i18n("Cannot copy item. %1", job->errorString())); } } void KMReaderMainWin::slotMessagePopup(const Akonadi::Item &aMsg, const WebEngineViewer::WebHitTestResult &result, const QPoint &aPoint) { QUrl aUrl = result.linkUrl(); QUrl imageUrl = result.imageUrl(); mMsg = aMsg; 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(mMsg)); job->setProperty("point", aPoint); job->setProperty("imageUrl", imageUrl); job->setProperty("url", aUrl); job->setProperty("webhitresult", QVariant::fromValue(result)); connect(job, &Akonadi::ItemCopyJob::result, this, &KMReaderMainWin::slotContactSearchJobForMessagePopupDone); } else { showMessagePopup(mMsg, aUrl, imageUrl, aPoint, false, false, result); } } void KMReaderMainWin::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) { mReaderWin->setContactItem(listContact.first(), searchJob->contacts().at(0)); } else { mReaderWin->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(); const WebEngineViewer::WebHitTestResult result = job->property("webhitresult").value(); showMessagePopup(msg, url, imageUrl, aPoint, contactAlreadyExists, uniqueContactFound, result); } void KMReaderMainWin::showMessagePopup(const Akonadi::Item &msg, const QUrl &url, const QUrl &imageUrl, const QPoint &aPoint, bool contactAlreadyExists, bool uniqueContactFound, const WebEngineViewer::WebHitTestResult &result) { QMenu *menu = nullptr; bool urlMenuAdded = false; bool copyAdded = false; const bool messageHasPayload = msg.hasPayload(); if (!url.isEmpty()) { if (url.scheme() == QLatin1String("mailto")) { // popup on a mailto URL menu = new QMenu(this); menu->addAction(mReaderWin->mailToComposeAction()); if (messageHasPayload) { menu->addAction(mReaderWin->mailToReplyAction()); menu->addAction(mReaderWin->mailToForwardAction()); menu->addSeparator(); } if (contactAlreadyExists) { if (uniqueContactFound) { menu->addAction(mReaderWin->editContactAction()); } else { menu->addAction(mReaderWin->openAddrBookAction()); } } else { menu->addAction(mReaderWin->addAddrBookAction()); menu->addAction(mReaderWin->addToExistingContactAction()); } menu->addSeparator(); menu->addMenu(mReaderWin->viewHtmlOption()); menu->addSeparator(); menu->addAction(mReaderWin->copyURLAction()); copyAdded = true; urlMenuAdded = true; } else if (url.scheme() != QLatin1String("attachment")) { // popup on a not-mailto URL menu = new QMenu(this); menu->addAction(mReaderWin->urlOpenAction()); menu->addAction(mReaderWin->addBookmarksAction()); menu->addAction(mReaderWin->urlSaveAsAction()); menu->addAction(mReaderWin->copyURLAction()); menu->addSeparator(); menu->addAction(mReaderWin->shareServiceUrlMenu()); menu->addSeparator(); menu->addActions(mReaderWin->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedUrl)); if (!imageUrl.isEmpty()) { menu->addSeparator(); menu->addAction(mReaderWin->copyImageLocation()); menu->addAction(mReaderWin->downloadImageToDiskAction()); menu->addAction(mReaderWin->shareImage()); } urlMenuAdded = true; } } const QString selectedText(mReaderWin->copyText()); if (!selectedText.isEmpty()) { if (!menu) { menu = new QMenu(this); } if (urlMenuAdded) { menu->addSeparator(); } if (messageHasPayload) { menu->addAction(mMsgActions->replyMenu()); menu->addSeparator(); menu->addAction(mMsgActions->mailingListActionMenu()); menu->addSeparator(); } if (!copyAdded) { menu->addAction(mReaderWin->copyAction()); } menu->addAction(mReaderWin->selectAllAction()); menu->addSeparator(); mMsgActions->addWebShortcutsMenu(menu, selectedText); menu->addSeparator(); menu->addActions(mReaderWin->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedSelection)); if (KPIMTextEdit::TextToSpeech::self()->isReady()) { menu->addSeparator(); menu->addAction(mReaderWin->speakTextAction()); } } else if (!urlMenuAdded) { if (!menu) { menu = new QMenu(this); } // popup somewhere else (i.e., not a URL) on the message if (messageHasPayload) { bool replyForwardMenu = false; Akonadi::Collection col = parentCollection(); if (col.isValid()) { if (!(CommonKernel->folderIsSentMailFolder(col) || CommonKernel->folderIsDrafts(col) || CommonKernel->folderIsTemplates(col))) { replyForwardMenu = true; } } else if (messageHasPayload) { replyForwardMenu = true; } if (replyForwardMenu) { // add the reply and forward actions only if we are not in a sent-mail, // templates or drafts folder menu->addAction(mMsgActions->replyMenu()); menu->addAction(mMsgActions->forwardMenu()); menu->addSeparator(); } else if (col.isValid() && CommonKernel->folderIsTemplates(col)) { menu->addAction(mMsgActions->newMessageFromTemplateAction()); } if (col.isValid() && CommonKernel->folderIsSentMailFolder(col)) { menu->addAction(mMsgActions->sendAgainAction()); menu->addSeparator(); } menu->addAction(copyActionMenu(menu)); menu->addAction(moveActionMenu(menu)); menu->addSeparator(); menu->addAction(mMsgActions->mailingListActionMenu()); menu->addSeparator(); if (!imageUrl.isEmpty()) { menu->addSeparator(); menu->addAction(mReaderWin->copyImageLocation()); menu->addAction(mReaderWin->downloadImageToDiskAction()); menu->addAction(mReaderWin->shareImage()); menu->addSeparator(); } menu->addAction(mMsgActions->printPreviewAction()); menu->addAction(mMsgActions->printAction()); menu->addSeparator(); menu->addAction(mReaderWin->saveAsAction()); menu->addAction(mSaveAtmAction); if (msg.isValid()) { menu->addSeparator(); menu->addActions(mReaderWin->viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedMessage)); } } else { menu->addAction(mReaderWin->toggleFixFontAction()); if (!mReaderWin->mimePartTreeIsEmpty()) { menu->addAction(mReaderWin->toggleMimePartTreeAction()); } } if (msg.isValid()) { menu->addAction(mMsgActions->addFollowupReminderAction()); } if (msg.isValid()) { menu->addSeparator(); menu->addAction(mMsgActions->addFollowupReminderAction()); } if (kmkernel->allowToDebug()) { menu->addSeparator(); menu->addAction(mMsgActions->debugAkonadiSearchAction()); } } const QList interceptorUrlActions = mReaderWin->interceptorUrlActions(result); if (!interceptorUrlActions.isEmpty()) { menu->addSeparator(); menu->addActions(interceptorUrlActions); } if (menu) { KAcceleratorManager::manage(menu); menu->exec(aPoint, nullptr); delete menu; } } void KMReaderMainWin::slotEditToolbars() { KConfigGroup grp(KMKernel::self()->config(), "ReaderWindow"); saveMainWindowSettings(grp); QPointer dlg = new KEditToolBar(guiFactory(), this); connect(dlg.data(), &KEditToolBar::newToolBarConfig, this, &KMReaderMainWin::slotUpdateToolbars); dlg->exec(); delete dlg; } void KMReaderMainWin::slotUpdateToolbars() { createGUI(QStringLiteral("kmreadermainwin.rc")); applyMainWindowSettings(KConfigGroup(KMKernel::self()->config(), "ReaderWindow")); } diff --git a/src/kmreaderwin.cpp b/src/kmreaderwin.cpp index 653c16431..577b604af 100644 --- a/src/kmreaderwin.cpp +++ b/src/kmreaderwin.cpp @@ -1,910 +1,910 @@ /* This file is part of KMail, the KDE mail client. Copyright (c) 1997 Markus Wuebben Copyright (c) 2009-2019 Laurent Montel 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. */ // define this to copy all html that is written to the readerwindow to // filehtmlwriter.out in the current working directory #include "kmreaderwin.h" #include "settings/kmailsettings.h" #include "kmmainwidget.h" #include "kmreadermainwin.h" #include "mailcommon/mailkernel.h" #include "dialog/addemailtoexistingcontactdialog.h" #include "job/addemailtoexistingcontactjob.h" #include "kmail-version.h" #include #include #include #include #include #include "kmcommands.h" #include "MailCommon/SendMdnHandler" #include #include "messageviewer/headerstrategy.h" #include "messageviewer/markmessagereadhandler.h" #include "messageviewer/messageviewersettings.h" #include using MessageViewer::CSSHelper; #include "util.h" #include #include #include #include #include "messageviewer/viewer.h" using namespace MessageViewer; #include #include #include #include "MessageComposer/Composer" #include "MessageComposer/TextPart" #include "MessageComposer/InfoPart" #include #include #include "kmail_debug.h" #include #include #include -#include +#include #include #include #include #include #include // X headers... #undef Never #undef Always #include using namespace KMail; using namespace MailCommon; KMReaderWin::KMReaderWin(QWidget *aParent, QWidget *mainWindow, KActionCollection *actionCollection) : QWidget(aParent) , mMainWindow(mainWindow) , mActionCollection(actionCollection) { createActions(); QVBoxLayout *vlay = new QVBoxLayout(this); vlay->setContentsMargins(0, 4, 0, 0); mViewer = new Viewer(this, mainWindow, mActionCollection); connect(mViewer, qOverload(&Viewer::urlClicked), this, &KMReaderWin::slotUrlClicked); connect(mViewer, &Viewer::requestConfigSync, kmkernel, &KMKernel::slotRequestConfigSync, Qt::QueuedConnection); // happens anyway on shutdown, so we can skip it there with using a queued connection connect(mViewer, &Viewer::makeResourceOnline, kmkernel, &KMKernel::makeResourceOnline); connect(mViewer, &MessageViewer::Viewer::showReader, this, &KMReaderWin::slotShowReader); connect(mViewer, &MessageViewer::Viewer::showMessage, this, &KMReaderWin::slotShowMessage); connect(mViewer, &MessageViewer::Viewer::showStatusBarMessage, this, &KMReaderWin::showStatusBarMessage); connect(mViewer, &MessageViewer::Viewer::printingFinished, this, &KMReaderWin::slotPrintingFinished); connect(mViewer, &MessageViewer::Viewer::zoomChanged, this, &KMReaderWin::zoomChanged); connect(mViewer, qOverload(&Viewer::deleteMessage), this, &KMReaderWin::slotDeleteMessage); connect(mViewer, &MessageViewer::Viewer::showNextMessage, this, &KMReaderWin::showNextMessage); connect(mViewer, &MessageViewer::Viewer::showPreviousMessage, this, &KMReaderWin::showPreviousMessage); mViewer->addMessageLoadedHandler(new MessageViewer::MarkMessageReadHandler(this)); mViewer->addMessageLoadedHandler(new MailCommon::SendMdnHandler(kmkernel, this)); vlay->addWidget(mViewer); readConfig(); } void KMReaderWin::createActions() { KActionCollection *ac = mActionCollection; if (!ac) { return; } // // Message Menu // // new message to mMailToComposeAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new")), i18n("New Message To..."), this); ac->addAction(QStringLiteral("mail_new"), mMailToComposeAction); ac->setShortcutsConfigurable(mMailToComposeAction, false); connect(mMailToComposeAction, &QAction::triggered, this, &KMReaderWin::slotMailtoCompose); // reply to mMailToReplyAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("Reply To..."), this); ac->addAction(QStringLiteral("mailto_reply"), mMailToReplyAction); ac->setShortcutsConfigurable(mMailToReplyAction, false); connect(mMailToReplyAction, &QAction::triggered, this, &KMReaderWin::slotMailtoReply); // forward to mMailToForwardAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")), i18n("Forward To..."), this); ac->setShortcutsConfigurable(mMailToForwardAction, false); ac->addAction(QStringLiteral("mailto_forward"), mMailToForwardAction); connect(mMailToForwardAction, &QAction::triggered, this, &KMReaderWin::slotMailtoForward); // add to addressbook mAddAddrBookAction = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("Add to Address Book"), this); ac->setShortcutsConfigurable(mAddAddrBookAction, false); ac->addAction(QStringLiteral("add_addr_book"), mAddAddrBookAction); connect(mAddAddrBookAction, &QAction::triggered, this, &KMReaderWin::slotMailtoAddAddrBook); mAddEmailToExistingContactAction = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("Add to Existing Contact"), this); ac->setShortcutsConfigurable(mAddEmailToExistingContactAction, false); ac->addAction(QStringLiteral("add_to_existing_contact"), mAddAddrBookAction); connect(mAddEmailToExistingContactAction, &QAction::triggered, this, &KMReaderWin::slotMailToAddToExistingContact); // open in addressbook mOpenAddrBookAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("Open in Address Book"), this); ac->setShortcutsConfigurable(mOpenAddrBookAction, false); ac->addAction(QStringLiteral("openin_addr_book"), mOpenAddrBookAction); connect(mOpenAddrBookAction, &QAction::triggered, this, &KMReaderWin::slotMailtoOpenAddrBook); // bookmark message mAddBookmarksAction = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("Bookmark This Link"), this); ac->setShortcutsConfigurable(mAddBookmarksAction, false); ac->addAction(QStringLiteral("add_bookmarks"), mAddBookmarksAction); connect(mAddBookmarksAction, &QAction::triggered, this, &KMReaderWin::slotAddBookmarks); mEditContactAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("Edit contact..."), this); ac->setShortcutsConfigurable(mEditContactAction, false); ac->addAction(QStringLiteral("edit_contact"), mOpenAddrBookAction); connect(mEditContactAction, &QAction::triggered, this, &KMReaderWin::slotEditContact); // save URL as mUrlSaveAsAction = new QAction(i18n("Save Link As..."), this); ac->addAction(QStringLiteral("saveas_url"), mUrlSaveAsAction); ac->setShortcutsConfigurable(mUrlSaveAsAction, false); connect(mUrlSaveAsAction, &QAction::triggered, this, &KMReaderWin::slotUrlSave); // find text QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("&Find in Message..."), this); ac->addAction(QStringLiteral("find_in_messages"), action); connect(action, &QAction::triggered, this, &KMReaderWin::slotFind); ac->setDefaultShortcut(action, KStandardShortcut::find().first()); // save Image On Disk mImageUrlSaveAsAction = new QAction(i18n("Save Image On Disk..."), this); ac->addAction(QStringLiteral("saveas_imageurl"), mImageUrlSaveAsAction); ac->setShortcutsConfigurable(mImageUrlSaveAsAction, false); connect(mImageUrlSaveAsAction, &QAction::triggered, this, &KMReaderWin::slotSaveImageOnDisk); // View html options mViewHtmlOptions = new QMenu(i18n("Show HTML Format"), this); mViewAsHtml = new QAction(i18n("Show HTML format when mail comes from this contact"), mViewHtmlOptions); ac->setShortcutsConfigurable(mViewAsHtml, false); connect(mViewAsHtml, &QAction::triggered, this, &KMReaderWin::slotContactHtmlOptions); mViewAsHtml->setCheckable(true); mViewHtmlOptions->addAction(mViewAsHtml); mLoadExternalReference = new QAction(i18n("Load external reference when mail comes for this contact"), mViewHtmlOptions); ac->setShortcutsConfigurable(mLoadExternalReference, false); connect(mLoadExternalReference, &QAction::triggered, this, &KMReaderWin::slotContactHtmlOptions); mLoadExternalReference->setCheckable(true); mViewHtmlOptions->addAction(mLoadExternalReference); mShareImage = new QAction(i18n("Share image..."), this); ac->addAction(QStringLiteral("share_imageurl"), mShareImage); ac->setShortcutsConfigurable(mShareImage, false); connect(mShareImage, &QAction::triggered, this, &KMReaderWin::slotShareImage); } void KMReaderWin::setUseFixedFont(bool useFixedFont) { mViewer->setUseFixedFont(useFixedFont); } Viewer *KMReaderWin::viewer() const { return mViewer; } bool KMReaderWin::isFixedFont() const { return mViewer->isFixedFont(); } KMReaderWin::~KMReaderWin() { } void KMReaderWin::readConfig() { mViewer->readConfig(); } void KMReaderWin::setAttachmentStrategy(const MessageViewer::AttachmentStrategy *strategy) { mViewer->setAttachmentStrategy(strategy); } void KMReaderWin::setOverrideEncoding(const QString &encoding) { mViewer->setOverrideEncoding(encoding); } void KMReaderWin::clearCache() { clear(); } void KMReaderWin::updateShowMultiMessagesButton(bool enablePreviousButton, bool enableNextButton) { mViewer->updateShowMultiMessagesButton(enablePreviousButton, enableNextButton); } void KMReaderWin::hasMultiMessages(bool multi) { mViewer->hasMultiMessages(multi); } // enter items for the "Important changes" list here: static const char *const kmailChanges[] = { I18N_NOOP("KMail is now based on the Akonadi Personal Information Management framework, which brings many " "changes all around.") }; static const int numKMailChanges = sizeof kmailChanges / sizeof *kmailChanges; // enter items for the "new features" list here, so the main body of // the welcome page can be left untouched (probably much easier for // the translators). Note that the
  • ...
  • tags are added // automatically below: static const char *const kmailNewFeatures[] = { I18N_NOOP("Push email (IMAP IDLE)"), I18N_NOOP("Improved searches"), I18N_NOOP("Support for adding notes (annotations) to mails"), I18N_NOOP("Less GUI freezes, mail checks happen in the background"), I18N_NOOP("Plugins support"), I18N_NOOP("New HTML renderer (QtWebEngine)"), I18N_NOOP("Added Check for Phishing URL"), }; static const int numKMailNewFeatures = sizeof kmailNewFeatures / sizeof *kmailNewFeatures; //static QString KMReaderWin::newFeaturesMD5() { QByteArray str; for (int i = 0; i < numKMailChanges; ++i) { str += kmailChanges[i]; } for (int i = 0; i < numKMailNewFeatures; ++i) { str += kmailNewFeatures[i]; } QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(str); return QLatin1String(md5.result().toBase64()); } void KMReaderWin::displaySplashPage(const QString &templateName, const QVariantHash &_data) { QVariantHash data = _data; if (!data.contains(QLatin1String("icon"))) { data[QStringLiteral("icon")] = QStringLiteral("kmail"); } if (!data.contains(QLatin1String("name"))) { data[QStringLiteral("name")] = i18n("KMail"); } if (!data.contains(QLatin1String("subtitle"))) { data[QStringLiteral("subtitle")] = i18n("The KDE Mail Client"); } mViewer->displaySplashPage(templateName, data, QByteArrayLiteral("kmail")); } void KMReaderWin::displayBusyPage() { displaySplashPage(QStringLiteral("status.html"), { { QStringLiteral("title"), i18n("Retrieving Folder Contents") }, { QStringLiteral("subtext"), i18n("Please wait . . .") } }); } void KMReaderWin::displayOfflinePage() { displaySplashPage(QStringLiteral("status.html"), { { QStringLiteral("title"), i18n("Offline") }, { QStringLiteral("subtext"), i18n("KMail is currently in offline mode. " "Click here to go online . . .

    ") } }); } void KMReaderWin::displayResourceOfflinePage() { displaySplashPage(QStringLiteral("status.html"), { { QStringLiteral("title"), i18n("Offline") }, { QStringLiteral("subtext"), i18n("Account is currently in offline mode. " "Click here to go online . . .

    ") } }); } void KMReaderWin::displayAboutPage() { QVariantHash data; data[QStringLiteral("version")] = QStringLiteral(KDEPIM_VERSION); data[QStringLiteral("firstStart")] = kmkernel->firstStart(); QVariantList features; features.reserve(numKMailNewFeatures); for (int i = 0; i < numKMailNewFeatures; ++i) { features.push_back(i18n(kmailNewFeatures[i])); } data[QStringLiteral("newFeatures")] = features; QVariantList changes; changes.reserve(numKMailChanges); for (int i = 0; i < numKMailChanges; ++i) { features.push_back(i18n(kmailChanges[i])); } data[QStringLiteral("importantChanges")] = changes; displaySplashPage(QStringLiteral(":/about/introduction_kmail.html"), data); } void KMReaderWin::slotFind() { mViewer->slotFind(); } void KMReaderWin::slotCopySelectedText() { QString selection = mViewer->selectedText(); selection.replace(QChar::Nbsp, QLatin1Char(' ')); QApplication::clipboard()->setText(selection); } void KMReaderWin::setMsgPart(KMime::Content *aMsgPart) { mViewer->setMessagePart(aMsgPart); } QString KMReaderWin::copyText() const { return mViewer->selectedText(); } MessageViewer::Viewer::DisplayFormatMessage KMReaderWin::displayFormatMessageOverwrite() const { return mViewer->displayFormatMessageOverwrite(); } void KMReaderWin::setPrintElementBackground(bool printElementBackground) { mViewer->setPrintElementBackground(printElementBackground); } void KMReaderWin::setDisplayFormatMessageOverwrite(MessageViewer::Viewer::DisplayFormatMessage format) { mViewer->setDisplayFormatMessageOverwrite(format); } void KMReaderWin::setHtmlLoadExtDefault(bool loadExtDefault) { mViewer->setHtmlLoadExtDefault(loadExtDefault); } void KMReaderWin::setHtmlLoadExtOverride(bool loadExtOverride) { mViewer->setHtmlLoadExtOverride(loadExtOverride); } bool KMReaderWin::htmlMail() const { return mViewer->htmlMail(); } bool KMReaderWin::htmlLoadExternal() { return mViewer->htmlLoadExternal(); } Akonadi::Item KMReaderWin::message() const { return mViewer->messageItem(); } QWidget *KMReaderWin::mainWindow() const { return mMainWindow; } void KMReaderWin::slotMailtoCompose() { KMCommand *command = new KMMailtoComposeCommand(urlClicked(), message()); command->start(); } void KMReaderWin::slotMailtoForward() { KMCommand *command = new KMMailtoForwardCommand(mMainWindow, urlClicked(), message()); command->start(); } void KMReaderWin::slotMailtoAddAddrBook() { const QUrl url = urlClicked(); if (url.isEmpty()) { return; } const QString emailString = KEmailAddress::decodeMailtoUrl(url); KPIM::AddEmailAddressJob *job = new KPIM::AddEmailAddressJob(emailString, mMainWindow, this); job->start(); } void KMReaderWin::slotMailToAddToExistingContact() { const QUrl url = urlClicked(); if (url.isEmpty()) { return; } const QString emailString = KEmailAddress::decodeMailtoUrl(url); QPointer dlg = new AddEmailToExistingContactDialog(this); if (dlg->exec()) { Akonadi::Item item = dlg->selectedContact(); if (item.isValid()) { AddEmailToExistingContactJob *job = new AddEmailToExistingContactJob(item, emailString, this); job->start(); } } delete dlg; } void KMReaderWin::slotMailtoOpenAddrBook() { const QUrl url = urlClicked(); if (url.isEmpty()) { return; } const QString emailString = KEmailAddress::decodeMailtoUrl(url).toLower(); KPIM::OpenEmailAddressJob *job = new KPIM::OpenEmailAddressJob(emailString, mMainWindow, this); job->start(); } void KMReaderWin::slotAddBookmarks() { const QUrl url = urlClicked(); if (url.isEmpty()) { return; } KMCommand *command = new KMAddBookmarksCommand(url, this); command->start(); } void KMReaderWin::slotUrlSave() { const QUrl url = urlClicked(); if (url.isEmpty()) { return; } KMCommand *command = new KMUrlSaveCommand(url, mMainWindow); command->start(); } void KMReaderWin::slotSaveImageOnDisk() { const QUrl url = imageUrlClicked(); if (url.isEmpty()) { return; } KMCommand *command = new KMUrlSaveCommand(url, mMainWindow); command->start(); } void KMReaderWin::slotMailtoReply() { KMCommand *command = new KMMailtoReplyCommand(mMainWindow, urlClicked(), message(), copyText()); command->start(); } CSSHelper *KMReaderWin::cssHelper() const { return mViewer->cssHelper(); } bool KMReaderWin::htmlLoadExtOverride() const { return mViewer->htmlLoadExtOverride(); } void KMReaderWin::setDecryptMessageOverwrite(bool overwrite) { mViewer->setDecryptMessageOverwrite(overwrite); } const MessageViewer::AttachmentStrategy *KMReaderWin::attachmentStrategy() const { return mViewer->attachmentStrategy(); } QString KMReaderWin::overrideEncoding() const { return mViewer->overrideEncoding(); } KToggleAction *KMReaderWin::toggleFixFontAction() const { return mViewer->toggleFixFontAction(); } QAction *KMReaderWin::mailToComposeAction() const { return mMailToComposeAction; } QAction *KMReaderWin::mailToReplyAction() const { return mMailToReplyAction; } QAction *KMReaderWin::mailToForwardAction() const { return mMailToForwardAction; } QAction *KMReaderWin::addAddrBookAction() const { return mAddAddrBookAction; } QAction *KMReaderWin::openAddrBookAction() const { return mOpenAddrBookAction; } bool KMReaderWin::mimePartTreeIsEmpty() const { return mViewer->mimePartTreeIsEmpty(); } QAction *KMReaderWin::toggleMimePartTreeAction() const { return mViewer->toggleMimePartTreeAction(); } KActionMenu *KMReaderWin::shareServiceUrlMenu() const { return mViewer->shareServiceUrlMenu(); } QList KMReaderWin::viewerPluginActionList(ViewerPluginInterface::SpecificFeatureTypes features) { return mViewer->viewerPluginActionList(features); } QAction *KMReaderWin::selectAllAction() const { return mViewer->selectAllAction(); } QAction *KMReaderWin::copyURLAction() const { return mViewer->copyURLAction(); } QAction *KMReaderWin::copyImageLocation() const { return mViewer->copyImageLocation(); } QAction *KMReaderWin::copyAction() const { return mViewer->copyAction(); } QAction *KMReaderWin::viewSourceAction() const { return mViewer->viewSourceAction(); } QAction *KMReaderWin::saveAsAction() const { return mViewer->saveAsAction(); } QAction *KMReaderWin::findInMessageAction() const { return mViewer->findInMessageAction(); } QAction *KMReaderWin::urlOpenAction() const { return mViewer->urlOpenAction(); } QAction *KMReaderWin::urlSaveAsAction() const { return mUrlSaveAsAction; } QAction *KMReaderWin::addBookmarksAction() const { return mAddBookmarksAction; } void KMReaderWin::setPrinting(bool enable) { mViewer->setPrinting(enable); } QAction *KMReaderWin::speakTextAction() const { return mViewer->speakTextAction(); } QAction *KMReaderWin::downloadImageToDiskAction() const { return mImageUrlSaveAsAction; } void KMReaderWin::clear(bool force) { mViewer->clear(force ? MimeTreeParser::Force : MimeTreeParser::Delayed); } void KMReaderWin::setMessage(const Akonadi::Item &item, MimeTreeParser::UpdateMode updateMode) { qCDebug(KMAIL_LOG) << Q_FUNC_INFO << parentWidget(); mViewer->setMessageItem(item, updateMode); } void KMReaderWin::setMessage(const KMime::Message::Ptr &message) { mViewer->setMessage(message); } QUrl KMReaderWin::urlClicked() const { return mViewer->urlClicked(); } QUrl KMReaderWin::imageUrlClicked() const { return mViewer->imageUrlClicked(); } void KMReaderWin::update(bool force) { mViewer->update(force ? MimeTreeParser::Force : MimeTreeParser::Delayed); } void KMReaderWin::slotUrlClicked(const Akonadi::Item &item, const QUrl &url) { if (item.isValid() && item.parentCollection().isValid()) { const auto col = CommonKernel->collectionFromId(item.parentCollection().id()); QSharedPointer fd = FolderSettings::forCollection(col, false); KMail::Util::handleClickedURL(url, fd, item.parentCollection()); return; } //No folder so we can't have identity and template. KMail::Util::handleClickedURL(url); } void KMReaderWin::slotShowReader(KMime::Content *msgPart, bool html, const QString &encoding) { const MessageViewer::Viewer::DisplayFormatMessage format = html ? MessageViewer::Viewer::Html : MessageViewer::Viewer::Text; KMReaderMainWin *win = new KMReaderMainWin(msgPart, format, encoding); win->show(); } void KMReaderWin::slotShowMessage(const KMime::Message::Ptr &message, const QString &encoding) { KMReaderMainWin *win = new KMReaderMainWin(); win->showMessage(encoding, message); win->show(); } void KMReaderWin::slotDeleteMessage(const Akonadi::Item &item) { if (!item.isValid()) { return; } KMTrashMsgCommand *command = new KMTrashMsgCommand(item.parentCollection(), item, -1); command->start(); } bool KMReaderWin::printSelectedText(bool preview) { const QString str = mViewer->selectedText(); if (str.isEmpty()) { return false; } ::MessageComposer::Composer *composer = new ::MessageComposer::Composer; composer->textPart()->setCleanPlainText(str); composer->textPart()->setWrappedPlainText(str); KMime::Message::Ptr messagePtr = message().payload(); composer->infoPart()->setFrom(messagePtr->from()->asUnicodeString()); composer->infoPart()->setTo(QStringList() << messagePtr->to()->asUnicodeString()); composer->infoPart()->setCc(QStringList() << messagePtr->cc()->asUnicodeString()); composer->infoPart()->setSubject(messagePtr->subject()->asUnicodeString()); composer->setProperty("preview", preview); connect(composer, &::MessageComposer::Composer::result, this, &KMReaderWin::slotPrintComposeResult); composer->start(); return true; } void KMReaderWin::slotPrintComposeResult(KJob *job) { const bool preview = job->property("preview").toBool(); ::MessageComposer::Composer *composer = dynamic_cast< ::MessageComposer::Composer * >(job); Q_ASSERT(composer); if (composer->error() == ::MessageComposer::Composer::NoError) { Q_ASSERT(composer->resultMessages().size() == 1); Akonadi::Item printItem; printItem.setPayload(composer->resultMessages().constFirst()); Akonadi::MessageFlags::copyMessageFlags(*(composer->resultMessages().constFirst()), printItem); const bool useFixedFont = MessageViewer::MessageViewerSettings::self()->useFixedFont(); const QString overrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding(); KMPrintCommandInfo commandInfo; commandInfo.mMsg = printItem; commandInfo.mHeaderStylePlugin = mViewer->headerStylePlugin(); commandInfo.mFormat = mViewer->displayFormatMessageOverwrite(); commandInfo.mHtmlLoadExtOverride = mViewer->htmlLoadExternal(); commandInfo.mPrintPreview = preview; commandInfo.mUseFixedFont = useFixedFont; commandInfo.mOverrideFont = overrideEncoding; commandInfo.mShowSignatureDetails = mViewer->showSignatureDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails(); commandInfo.mShowEncryptionDetails = mViewer->showEncryptionDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails(); KMPrintCommand *command = new KMPrintCommand(this, commandInfo); command->start(); } else { if (static_cast(job)->uiDelegate()) { static_cast(job)->uiDelegate()->showErrorMessage(); } else { qCWarning(KMAIL_LOG) << "Composer for printing failed:" << composer->errorString(); } } } void KMReaderWin::clearContactItem() { mSearchedContact = Akonadi::Item(); mSearchedAddress = KContacts::Addressee(); mLoadExternalReference->setChecked(false); mViewAsHtml->setChecked(false); } void KMReaderWin::setContactItem(const Akonadi::Item &contact, const KContacts::Addressee &address) { mSearchedContact = contact; mSearchedAddress = address; updateHtmlActions(); } void KMReaderWin::updateHtmlActions() { if (!mSearchedContact.isValid()) { mLoadExternalReference->setChecked(false); mViewAsHtml->setChecked(false); } else { const QStringList customs = mSearchedAddress.customs(); for (const QString &custom : customs) { if (custom.contains(QLatin1String("MailPreferedFormatting"))) { const QString value = mSearchedAddress.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("MailPreferedFormatting")); mViewAsHtml->setChecked(value == QLatin1String("HTML")); } else if (custom.contains(QLatin1String("MailAllowToRemoteContent"))) { const QString value = mSearchedAddress.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("MailAllowToRemoteContent")); mLoadExternalReference->setChecked((value == QLatin1String("TRUE"))); } } } } void KMReaderWin::slotContactHtmlOptions() { const QUrl url = urlClicked(); if (url.isEmpty()) { return; } const QString emailString = KEmailAddress::decodeMailtoUrl(url).toLower(); KPIM::AddEmailDiplayJob *job = new KPIM::AddEmailDiplayJob(emailString, mMainWindow, this); job->setRemoteContent(mLoadExternalReference->isChecked()); job->setShowAsHTML(mViewAsHtml->isChecked()); job->setContact(mSearchedContact); job->start(); } void KMReaderWin::slotEditContact() { if (mSearchedContact.isValid()) { QPointer dlg = new Akonadi::ContactEditorDialog(Akonadi::ContactEditorDialog::EditMode, this); connect(dlg.data(), &Akonadi::ContactEditorDialog::contactStored, this, &KMReaderWin::contactStored); connect(dlg.data(), &Akonadi::ContactEditorDialog::error, this, &KMReaderWin::slotContactEditorError); dlg->setContact(mSearchedContact); dlg->exec(); delete dlg; } } void KMReaderWin::slotContactEditorError(const QString &error) { KMessageBox::error(this, i18n("Contact cannot be stored: %1", error), i18n("Failed to store contact")); } void KMReaderWin::contactStored(const Akonadi::Item &item) { Q_UNUSED(item); KPIM::BroadcastStatus::instance()->setStatusMsg(i18n("Contact modified successfully")); } QAction *KMReaderWin::saveMessageDisplayFormatAction() const { return mViewer->saveMessageDisplayFormatAction(); } QAction *KMReaderWin::resetMessageDisplayFormatAction() const { return mViewer->resetMessageDisplayFormatAction(); } QAction *KMReaderWin::editContactAction() const { return mEditContactAction; } QMenu *KMReaderWin::viewHtmlOption() const { return mViewHtmlOptions; } QAction *KMReaderWin::shareImage() const { return mShareImage; } QAction *KMReaderWin::addToExistingContactAction() const { return mAddEmailToExistingContactAction; } void KMReaderWin::slotShareImage() { KMCommand *command = new KMShareImageCommand(imageUrlClicked(), this); command->start(); } QList KMReaderWin::interceptorUrlActions(const WebEngineViewer::WebHitTestResult &result) const { return mViewer->interceptorUrlActions(result); } void KMReaderWin::slotPrintingFinished() { if (mViewer->printingMode()) { deleteLater(); } } diff --git a/src/kmsystemtray.h b/src/kmsystemtray.h index 6a37534d3..50399cab4 100644 --- a/src/kmsystemtray.h +++ b/src/kmsystemtray.h @@ -1,70 +1,70 @@ /*************************************************************************** kmsystemtray.h - description ------------------- begin : Fri Aug 31 22:38:44 EDT 2001 copyright : (C) 2001 by Ryan Breen email : ryan@porivo.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KMSYSTEMTRAY_H #define KMSYSTEMTRAY_H #include -#include +#include #include #include class QMenu; /** * KMSystemTray extends KStatusNotifierItem and handles system * tray notification for KMail */ namespace KMail { class UnityServiceManager; class KMSystemTray : public KStatusNotifierItem { Q_OBJECT public: /** constructor */ explicit KMSystemTray(QObject *parent = nullptr); /** destructor */ ~KMSystemTray(); void hideKMail(); void updateStatus(int count); void updateCount(int count); void setUnityServiceManager(KMail::UnityServiceManager *unityServiceManager); void initialize(int count); void updateToolTip(int count); private: void slotActivated(); void slotContextMenuAboutToShow(); void slotSelectCollection(QAction *act); Q_REQUIRED_RESULT bool mainWindowIsOnCurrentDesktop(); Q_REQUIRED_RESULT bool buildPopupMenu(); void fillFoldersMenu(QMenu *menu, const QAbstractItemModel *model, const QString &parentName = QString(), const QModelIndex &parentIndex = QModelIndex()); int mDesktopOfMainWin = 0; bool mHasUnreadMessage = false; bool mIconNotificationsEnabled = true; QMenu *mNewMessagesPopup = nullptr; QAction *mSendQueued = nullptr; KMail::UnityServiceManager *mUnityServiceManager = nullptr; }; } #endif diff --git a/src/main.cpp b/src/main.cpp index 18fa917ae..eba7d1dd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,167 +1,167 @@ /* * kmail: KDE mail client * Copyright (c) 1996-1998 Stefan Taferner * * 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 #include "kmkernel.h" //control center #include "kmail_options.h" #include "kmmigrateapplication.h" #include "kmail_debug.h" -#include +#include #undef Status // stupid X headers #include "aboutdata.h" #include #include #include #include //----------------------------------------------------------------------------- class KMailApplication : public KontactInterface::PimUniqueApplication { public: KMailApplication(int &argc, char **argv[]) : KontactInterface::PimUniqueApplication(argc, argv) , mDelayedInstanceCreation(false) , mEventLoopReached(false) { } int activate(const QStringList &args, const QString &workingDir) override; void commitData(QSessionManager &sm); void setEventLoopReached(); void delayedInstanceCreation(const QStringList &args, const QString &workingDir); public Q_SLOTS: int newInstance(const QByteArray &startupId, const QStringList &arguments, const QString &workingDirectory) override; protected: bool mDelayedInstanceCreation; bool mEventLoopReached; }; void KMailApplication::commitData(QSessionManager &) { kmkernel->dumpDeadLetters(); kmkernel->setShuttingDown(true); // Prevent further dumpDeadLetters calls } void KMailApplication::setEventLoopReached() { mEventLoopReached = true; } int KMailApplication::newInstance(const QByteArray &startupId, const QStringList &arguments, const QString &workingDirectory) { if (!kmkernel->firstInstance() && !arguments.isEmpty()) { // if we're going to create a new window (viewer or composer), // don't bring the mainwindow onto the current desktop return activate(arguments, workingDirectory); } else { return PimUniqueApplication::newInstance(startupId, arguments, workingDirectory); } } int KMailApplication::activate(const QStringList &args, const QString &workingDir) { // If the event loop hasn't been reached yet, the kernel is probably not // fully initialized. Creating an instance would therefore fail, this is why // that is delayed until delayedInstanceCreation() is called. if (!mEventLoopReached) { qCDebug(KMAIL_LOG) << "Delaying instance creation."; mDelayedInstanceCreation = true; return 0; } if (!kmkernel) { return 0; } if (kmkernel->shuttingDown()) { qCDebug(KMAIL_LOG) << "KMail is in a shutdown mode."; return 0; } if (!kmkernel->firstInstance() || !qApp->isSessionRestored()) { kmkernel->handleCommandLine(true, args, workingDir); } kmkernel->setFirstInstance(false); return 0; } void KMailApplication::delayedInstanceCreation(const QStringList &args, const QString &workingDir) { if (mDelayedInstanceCreation) { activate(args, workingDir); } } int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); KMailApplication app(argc, &argv); KLocalizedString::setApplicationDomain("kmail"); app.setWindowIcon(QIcon::fromTheme(QStringLiteral("kmail"))); app.setDesktopFileName(QStringLiteral("org.kde.kmail2")); KCrash::initialize(); KMail::AboutData about; app.setAboutData(about); QCommandLineParser *cmdArgs = app.cmdArgs(); kmail_options(cmdArgs); const QStringList args = QApplication::arguments(); cmdArgs->process(args); about.processCommandLine(cmdArgs); if (!KMailApplication::start(args)) { qCDebug(KMAIL_LOG) << "Another instance of KMail already running"; return 0; } KMMigrateApplication migrate; migrate.migrate(); //local, do the init KMKernel kmailKernel; kmailKernel.init(); // and session management kmailKernel.doSessionManagement(); // any dead letters? kmailKernel.recoverDeadLetters(); kmkernel->setupDBus(); // Ok. We are ready for D-Bus requests. //If the instance hasn't been created yet, do that now app.setEventLoopReached(); app.delayedInstanceCreation(args, QDir::currentPath()); // Go! int ret = qApp->exec(); // clean up kmailKernel.cleanup(); return ret; } diff --git a/src/searchdialog/searchwindow.h b/src/searchdialog/searchwindow.h index dc7be79a8..3f3f90058 100644 --- a/src/searchdialog/searchwindow.h +++ b/src/searchdialog/searchwindow.h @@ -1,198 +1,198 @@ /* * kmail: KDE mail client * Copyright (c) 1996-1998 Stefan Taferner * Copyright (c) 2001 Aaron J. Seigo * * 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 KMAIL_SEARCHWINDOW_H #define KMAIL_SEARCHWINDOW_H #include "MailCommon/SearchPattern" #include "ui_searchwindow.h" #include #include #include -#include +#include #include #include class QCloseEvent; class QKeyEvent; class KActionMenu; class KJob; class KMMainWidget; class KMSearchMessageModel; namespace PimCommon { class SelectMultiCollectionDialog; } namespace Akonadi { class StandardMailActionManager; } namespace KMime { class Message; } namespace KMail { /** * The SearchWindow class provides a dialog for triggering a search on * folders and storing that search as a search folder. It shows the search * results in a listview and allows triggering of operations such as printing * or moving on them. */ class SearchPatternWarning; class SearchWindow : public QDialog, public KXMLGUIClient { Q_OBJECT public: /** * Creates a new search window. * * @param parent The parent widget. * @param collection The folder which will be pre-selected as the base folder * of search operations. */ explicit SearchWindow(KMMainWidget *parent, const Akonadi::Collection &collection = Akonadi::Collection()); /** * Destroys the search window. */ ~SearchWindow() override; /** * Changes the base folder for search operations to a different folder. * * @param folder The folder to use as the new base for searches. */ void activateFolder(const Akonadi::Collection &folder); /** * Provides access to the list of currently selected message in the listview. * * @return The list of currently selected search result messages. */ Q_REQUIRED_RESULT Akonadi::Item::List selectedMessages() const; /** * Provides access to the currently selected message. * * @return the currently selected message. */ Q_REQUIRED_RESULT Akonadi::Item selectedMessage() const; /** * Loads a search pattern into the search window, appending its rules to the current one. */ void addRulesToSearchPattern(const MailCommon::SearchPattern &pattern); protected: /** Reimplemented to react to Escape. */ void keyPressEvent(QKeyEvent *) override; /** Reimplemented to stop searching when the window is closed */ void closeEvent(QCloseEvent *) override; void createSearchModel(); private: void updateCollectionStatistic(Akonadi::Collection::Id, const Akonadi::CollectionStatistics &); void slotClose(); void slotSearch(); void slotStop(); void scheduleRename(const QString &); void renameSearchFolder(); void openSearchFolder(); void slotViewSelectedMsg(); void slotViewMsg(const Akonadi::Item &); void slotCurrentChanged(const Akonadi::Item &); void updateContextMenuActions(); void slotFolderActivated(); void slotClearSelection(); void slotReplyToMsg(); void slotReplyAllToMsg(); void slotReplyListToMsg(); void slotForwardMsg(); void slotForwardAttachedMsg(); void slotSaveMsg(); void slotSaveAttachments(); void slotPrintMsg(); /** GUI cleanup after search */ void slotCollectionStatisticsRetrieved(KJob *job); void searchDone(KJob *); void enableGUI(); void setEnabledSearchButton(bool); void slotSearchFolderRenameDone(KJob *); void slotContextMenuRequested(const QPoint &); void slotSelectMultipleFolders(); void slotSearchCollectionsFetched(KJob *job); void slotJumpToFolder(); private: void doSearch(); QVector checkIncompleteIndex(const Akonadi::Collection::List &searchCols, bool recursive); Akonadi::Collection::List searchCollectionsRecursive(const Akonadi::Collection::List &cols) const; QPointer mSelectMultiCollectionDialog; QVector mCollectionId; Akonadi::SearchQuery mQuery; Qt::SortOrder mSortOrder = Qt::AscendingOrder; Akonadi::Collection mFolder; Ui_SearchWindow mUi; KGuiItem mStartSearchGuiItem; KGuiItem mStopSearchGuiItem; MailCommon::SearchPattern mSearchPattern; bool mCloseRequested = false; int mSortColumn = 0; KJob *mSearchJob = nullptr; KMSearchMessageModel *mResultModel = nullptr; QPushButton *mSearchButton = nullptr; QAction *mReplyAction = nullptr; QAction *mReplyAllAction = nullptr; QAction *mReplyListAction = nullptr; QAction *mSaveAsAction = nullptr; QAction *mForwardInlineAction = nullptr; QAction *mForwardAttachedAction = nullptr; QAction *mPrintAction = nullptr; QAction *mClearAction = nullptr; QAction *mSaveAtchAction = nullptr; QAction *mJumpToFolderAction = nullptr; KActionMenu *mForwardActionMenu = nullptr; QTimer mRenameTimer; QByteArray mHeaderState; // not owned by us KMMainWidget *mKMMainWidget = nullptr; SearchPatternWarning *mSearchPatternWidget = nullptr; Akonadi::StandardMailActionManager *mAkonadiStandardAction = nullptr; }; } #endif diff --git a/src/undostack.cpp b/src/undostack.cpp index 42057c908..b00710f98 100644 --- a/src/undostack.cpp +++ b/src/undostack.cpp @@ -1,149 +1,149 @@ /* This file is part of KMail Copyright (C) 1999 Waldo Bastian (bastian@kde.org) Copyright (c) 2003 Zack Rusin This library 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. This software 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 library; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "undostack.h" #include "kmmainwin.h" #include "kmkernel.h" #include #include -#include +#include #include #include "kmail_debug.h" #include using namespace KMail; UndoStack::UndoStack(int size) : QObject(nullptr) , mSize(size) { } UndoStack::~UndoStack() { clear(); } void UndoStack::clear() { qDeleteAll(mStack); mStack.clear(); } int UndoStack::size() const { return mStack.count(); } QString UndoStack::undoInfo() const { if (!mStack.isEmpty()) { UndoInfo *info = mStack.first(); return info->moveToTrash ? i18n("Move To Trash") : i18np("Move Message", "Move Messages", info->items.count()); } else { return QString(); } } int UndoStack::newUndoAction(const Akonadi::Collection &srcFolder, const Akonadi::Collection &destFolder) { UndoInfo *info = new UndoInfo; info->id = ++mLastId; info->srcFolder = srcFolder; info->destFolder = destFolder; info->moveToTrash = (destFolder == CommonKernel->trashCollectionFolder()); if (static_cast(mStack.count()) == mSize) { delete mStack.last(); mStack.removeLast(); } mStack.prepend(info); Q_EMIT undoStackChanged(); return info->id; } void UndoStack::addMsgToAction(int undoId, const Akonadi::Item &item) { if (!mCachedInfo || mCachedInfo->id != undoId) { QList::const_iterator itr = mStack.constBegin(); while (itr != mStack.constEnd()) { if ((*itr)->id == undoId) { mCachedInfo = (*itr); break; } ++itr; } } Q_ASSERT(mCachedInfo); mCachedInfo->items.append(item); } bool UndoStack::isEmpty() const { return mStack.isEmpty(); } void UndoStack::undo() { if (!mStack.isEmpty()) { UndoInfo *info = mStack.takeFirst(); Q_EMIT undoStackChanged(); Akonadi::ItemMoveJob *job = new Akonadi::ItemMoveJob(info->items, info->srcFolder, this); connect(job, &Akonadi::ItemMoveJob::result, this, &UndoStack::slotMoveResult); delete info; } else { // Sorry.. stack is empty.. KMessageBox::sorry(kmkernel->mainWin(), i18n("There is nothing to undo.")); } } void UndoStack::slotMoveResult(KJob *job) { if (job->error()) { KMessageBox::sorry(kmkernel->mainWin(), i18n("Cannot move message. %1", job->errorString())); } } void UndoStack::pushSingleAction(const Akonadi::Item &item, const Akonadi::Collection &folder, const Akonadi::Collection &destFolder) { const int id = newUndoAction(folder, destFolder); addMsgToAction(id, item); } void UndoStack::folderDestroyed(const Akonadi::Collection &folder) { QList::iterator it = mStack.begin(); while (it != mStack.end()) { UndoInfo *info = *it; if (info && ((info->srcFolder == folder) || (info->destFolder == folder))) { delete info; it = mStack.erase(it); } else { ++it; } } Q_EMIT undoStackChanged(); } diff --git a/src/util.cpp b/src/util.cpp index b4481eb05..ffd4c714a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,189 +1,189 @@ /******************************************************************************* ** ** Filename : util ** Created on : 03 April, 2005 ** Copyright : (c) 2005 Till Adam ** Email : ** *******************************************************************************/ /******************************************************************************* ** ** 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. ** ** It 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 ** ** In addition, as a special exception, the copyright holders give ** permission to link the code of this program with any edition of ** the Qt library by Trolltech AS, Norway (or with modified versions ** of Qt that use the same license as Qt), and distribute linked ** combinations including the two. You must obey the GNU General ** Public License in all respects for all of the code used other than ** Qt. If you modify this file, you may extend this exception to ** your version of the file, but you are not obligated to do so. If ** you do not wish to do so, delete this exception statement from ** your version. ** *******************************************************************************/ #include "util.h" #include "kmkernel.h" #include #include "MessageComposer/MessageHelper" #include "job/handleclickedurljob.h" #include -#include -#include +#include +#include #include "kmail_debug.h" #include #include #include using namespace MailCommon; using namespace KMail; KMime::Types::Mailbox::List Util::mailingListsFromMessage(const Akonadi::Item &item) { KMime::Types::Mailbox::List addresses; // determine the mailing list posting address Akonadi::Collection parentCollection = item.parentCollection(); if (parentCollection.isValid()) { const QSharedPointer fd = FolderSettings::forCollection(parentCollection, false); if (fd->isMailingListEnabled() && !fd->mailingListPostAddress().isEmpty()) { KMime::Types::Mailbox mailbox; mailbox.fromUnicodeString(fd->mailingListPostAddress()); addresses << mailbox; } } return addresses; } Akonadi::Item::Id Util::putRepliesInSameFolder(const Akonadi::Item &item) { Akonadi::Collection parentCollection = item.parentCollection(); if (parentCollection.isValid()) { const QSharedPointer fd = FolderSettings::forCollection(parentCollection, false); if (fd->putRepliesInSameFolder()) { return parentCollection.id(); } } return -1; } bool Util::handleClickedURL(const QUrl &url, const QSharedPointer &folder, const Akonadi::Collection &collection) { if (url.scheme() == QLatin1String("mailto")) { HandleClickedUrlJob *job = new HandleClickedUrlJob; job->setUrl(url); job->setFolder(folder); job->setCurrentCollection(collection); job->start(); return true; } else { qCWarning(KMAIL_LOG) << "Can't handle URL:" << url; return false; } } bool Util::mailingListsHandleURL(const QList &lst, const QSharedPointer &folder, const Akonadi::Collection &collection) { const QString handler = (folder->mailingList().handler() == MailingList::KMail) ? QStringLiteral("mailto") : QStringLiteral("https"); QUrl urlToHandle; QList::ConstIterator end(lst.constEnd()); for (QList::ConstIterator itr = lst.constBegin(); itr != end; ++itr) { if (handler == (*itr).scheme()) { urlToHandle = *itr; break; } } if (urlToHandle.isEmpty() && !lst.empty()) { urlToHandle = lst.constFirst(); } if (!urlToHandle.isEmpty()) { return Util::handleClickedURL(urlToHandle, folder, collection); } else { qCWarning(KMAIL_LOG) << "Can't handle url"; return false; } } bool Util::mailingListPost(const QSharedPointer &fd, const Akonadi::Collection &col) { if (fd) { return Util::mailingListsHandleURL(fd->mailingList().postUrls(), fd, col); } return false; } bool Util::mailingListSubscribe(const QSharedPointer &fd, const Akonadi::Collection &col) { if (fd) { return Util::mailingListsHandleURL(fd->mailingList().subscribeUrls(), fd, col); } return false; } bool Util::mailingListUnsubscribe(const QSharedPointer &fd, const Akonadi::Collection &col) { if (fd) { return Util::mailingListsHandleURL(fd->mailingList().unsubscribeUrls(), fd, col); } return false; } bool Util::mailingListArchives(const QSharedPointer &fd, const Akonadi::Collection &col) { if (fd) { return Util::mailingListsHandleURL(fd->mailingList().archiveUrls(), fd, col); } return false; } bool Util::mailingListHelp(const QSharedPointer &fd, const Akonadi::Collection &col) { if (fd) { return Util::mailingListsHandleURL(fd->mailingList().helpUrls(), fd, col); } return false; } void Util::lastEncryptAndSignState(bool &lastEncrypt, bool &lastSign, const KMime::Message::Ptr &msg) { lastSign = KMime::isSigned(msg.data()); lastEncrypt = KMime::isEncrypted(msg.data()); } void Util::addQActionHelpText(QAction *action, const QString &text) { action->setStatusTip(text); action->setToolTip(text); if (action->whatsThis().isEmpty()) { action->setWhatsThis(text); } } void Util::setActionTrashOrDelete(QAction *action, bool isInTrashFolder) { if (action) { action->setText(isInTrashFolder ? i18nc("@action Hard delete, bypassing trash", "&Delete") : i18n("&Move to Trash")); action->setIcon(isInTrashFolder ? QIcon::fromTheme(QStringLiteral("edit-delete-shred")) : QIcon::fromTheme(QStringLiteral("edit-delete"))); //Use same text as in Text property. Change it in kf5 action->setToolTip(isInTrashFolder ? i18nc("@action Hard delete, bypassing trash", "Delete") : i18n("Move to Trash")); } }