diff --git a/src/filter/filteractions/filteractionaddheader.cpp b/src/filter/filteractions/filteractionaddheader.cpp index c6d9307..f04a01d 100644 --- a/src/filter/filteractions/filteractionaddheader.cpp +++ b/src/filter/filteractions/filteractionaddheader.cpp @@ -1,232 +1,232 @@ /* * 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 "filteractionaddheader.h" #include #include #include #include #include #include using namespace MailCommon; FilterActionAddHeader::FilterActionAddHeader(QObject *parent) : FilterActionWithStringList(QStringLiteral("add header"), i18n("Add Header"), parent) { - mParameterList << QStringLiteral("") + mParameterList << QString() << QStringLiteral("Reply-To") << QStringLiteral("Delivered-To") << QStringLiteral("X-KDE-PR-Message") << QStringLiteral("X-KDE-PR-Package") << QStringLiteral("X-KDE-PR-Keywords"); mParameter = mParameterList.at(0); } bool FilterActionAddHeader::isEmpty() const { return mParameter.isEmpty() || mValue.isEmpty(); } FilterAction::ReturnCode FilterActionAddHeader::process(ItemContext &context, bool) const { if (isEmpty()) { return ErrorButGoOn; } KMime::Message::Ptr msg = context.item().payload(); KMime::Headers::Base *header = KMime::Headers::createHeader(mParameter.toLatin1()); if (!header) { header = new KMime::Headers::Generic(mParameter.toLatin1().constData()); } header->fromUnicodeString(mValue, "utf-8"); msg->setHeader(header); msg->assemble(); context.setNeedsPayloadStore(); return GoOn; } QWidget *FilterActionAddHeader::createParamWidget(QWidget *parent) const { QWidget *widget = new QWidget(parent); QHBoxLayout *layout = new QHBoxLayout(widget); layout->setSpacing(4); layout->setMargin(0); PimCommon::MinimumComboBox *comboBox = new PimCommon::MinimumComboBox(widget); comboBox->setObjectName(QStringLiteral("combo")); comboBox->setEditable(true); comboBox->setInsertPolicy(QComboBox::InsertAtBottom); KCompletion *comp = comboBox->completionObject(); comp->setIgnoreCase(true); comp->insertItems(mParameterList); comp->setCompletionMode(KCompletion::CompletionPopupAuto); layout->addWidget(comboBox, 0 /* stretch */); QLabel *label = new QLabel(i18n("With value:"), widget); label->setObjectName(QStringLiteral("label_value")); label->setFixedWidth(label->sizeHint().width()); layout->addWidget(label, 0); KLineEdit *lineEdit = new KLineEdit(widget); lineEdit->setObjectName(QStringLiteral("ledit")); lineEdit->setTrapReturnKey(true); lineEdit->setClearButtonEnabled(true); layout->addWidget(lineEdit, 1); setParamWidgetValue(widget); connect(comboBox, QOverload::of(&PimCommon::MinimumComboBox::currentIndexChanged), this, &FilterActionAddHeader::filterActionModified); connect(comboBox->lineEdit(), &QLineEdit::textChanged, this, &FilterAction::filterActionModified); connect(lineEdit, &QLineEdit::textChanged, this, &FilterActionAddHeader::filterActionModified); return widget; } void FilterActionAddHeader::setParamWidgetValue(QWidget *paramWidget) const { const int index = mParameterList.indexOf(mParameter); PimCommon::MinimumComboBox *comboBox = paramWidget->findChild(QStringLiteral("combo")); Q_ASSERT(comboBox); comboBox->clear(); comboBox->addItems(mParameterList); if (index < 0) { comboBox->addItem(mParameter); comboBox->setCurrentIndex(comboBox->count() - 1); } else { comboBox->setCurrentIndex(index); } QLineEdit *lineEdit = paramWidget->findChild(QStringLiteral("ledit")); Q_ASSERT(lineEdit); lineEdit->setText(mValue); } void FilterActionAddHeader::applyParamWidgetValue(QWidget *paramWidget) { const PimCommon::MinimumComboBox *comboBox = paramWidget->findChild(QStringLiteral("combo")); Q_ASSERT(comboBox); mParameter = comboBox->currentText(); const QLineEdit *lineEdit = paramWidget->findChild(QStringLiteral("ledit")); Q_ASSERT(lineEdit); mValue = lineEdit->text(); } void FilterActionAddHeader::clearParamWidget(QWidget *paramWidget) const { PimCommon::MinimumComboBox *comboBox = paramWidget->findChild(QStringLiteral("combo")); Q_ASSERT(comboBox); comboBox->setCurrentIndex(0); QLineEdit *lineEdit = paramWidget->findChild(QStringLiteral("ledit")); Q_ASSERT(lineEdit); lineEdit->clear(); } SearchRule::RequiredPart FilterActionAddHeader::requiredPart() const { return SearchRule::CompleteMessage; } QString FilterActionAddHeader::argsAsString() const { QString result = mParameter; result += QLatin1Char('\t'); result += mValue; return result; } QString FilterActionAddHeader::displayString() const { return label() + QStringLiteral(" \"") + argsAsString().toHtmlEscaped() + QStringLiteral("\""); } void FilterActionAddHeader::argsFromString(const QString &argsStr) { const QStringList list = argsStr.split(QLatin1Char('\t')); QString result; if (list.count() < 2) { result = list[ 0 ]; mValue.clear(); } else { result = list[ 0 ]; mValue = list[ 1 ]; } int index = mParameterList.indexOf(result); if (index < 0) { mParameterList.append(result); index = mParameterList.count() - 1; } mParameter = mParameterList.at(index); } FilterAction *FilterActionAddHeader::newAction() { return new FilterActionAddHeader; } QStringList FilterActionAddHeader::sieveRequires() const { return QStringList() << QStringLiteral("editheader"); } QString FilterActionAddHeader::sieveCode() const { if (isEmpty()) { return QStringLiteral("# invalid filter. Need to fix it by hand"); } else { return QStringLiteral("addheader \"%1\" \"%2\";").arg(mParameter, mValue); } } QString FilterActionAddHeader::informationAboutNotValidAction() const { QString result; if (mParameter.isEmpty()) { result = i18n("The header name was missing."); } if (mValue.isEmpty()) { if (result.isEmpty()) { result += QLatin1Char('\n'); } result += i18n("The header value was missing."); } if (!result.isEmpty()) { result = name() + QLatin1Char('\n') + result; } return result; } diff --git a/src/filter/filteractions/filteractionremoveheader.cpp b/src/filter/filteractions/filteractionremoveheader.cpp index bd3d8c7..e247131 100644 --- a/src/filter/filteractions/filteractionremoveheader.cpp +++ b/src/filter/filteractions/filteractionremoveheader.cpp @@ -1,115 +1,115 @@ /* * 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 "filteractionremoveheader.h" #include "PimCommon/MinimumComboBox" #include #include using namespace MailCommon; FilterAction *FilterActionRemoveHeader::newAction() { return new FilterActionRemoveHeader; } FilterActionRemoveHeader::FilterActionRemoveHeader(QObject *parent) : FilterActionWithStringList(QStringLiteral("remove header"), i18n("Remove Header"), parent) { - mParameterList << QStringLiteral("") + mParameterList << QString() << QStringLiteral("Reply-To") << QStringLiteral("Delivered-To") << QStringLiteral("X-KDE-PR-Message") << QStringLiteral("X-KDE-PR-Package") << QStringLiteral("X-KDE-PR-Keywords"); mParameter = mParameterList.at(0); } QWidget *FilterActionRemoveHeader::createParamWidget(QWidget *parent) const { PimCommon::MinimumComboBox *comboBox = new PimCommon::MinimumComboBox(parent); comboBox->setEditable(true); comboBox->setInsertPolicy(QComboBox::InsertAtBottom); setParamWidgetValue(comboBox); connect(comboBox, QOverload::of(&PimCommon::MinimumComboBox::currentIndexChanged), this, &FilterActionRemoveHeader::filterActionModified); connect(comboBox->lineEdit(), &QLineEdit::textChanged, this, &FilterAction::filterActionModified); return comboBox; } FilterAction::ReturnCode FilterActionRemoveHeader::process(ItemContext &context, bool) const { if (isEmpty()) { return ErrorButGoOn; } KMime::Message::Ptr msg = context.item().payload(); const QByteArray param(mParameter.toLatin1()); bool headerRemove = false; while (msg->removeHeader(param.constData())) { headerRemove = true; } if (headerRemove) { msg->assemble(); context.setNeedsPayloadStore(); } return GoOn; } SearchRule::RequiredPart FilterActionRemoveHeader::requiredPart() const { return SearchRule::CompleteMessage; } void FilterActionRemoveHeader::setParamWidgetValue(QWidget *paramWidget) const { PimCommon::MinimumComboBox *comboBox = qobject_cast(paramWidget); Q_ASSERT(comboBox); const int index = mParameterList.indexOf(mParameter); comboBox->clear(); comboBox->addItems(mParameterList); if (index < 0) { comboBox->addItem(mParameter); comboBox->setCurrentIndex(comboBox->count() - 1); } else { comboBox->setCurrentIndex(index); } } QStringList FilterActionRemoveHeader::sieveRequires() const { return QStringList() << QStringLiteral("editheader"); } QString FilterActionRemoveHeader::sieveCode() const { return QStringLiteral("deleteheader \"%1\";").arg(mParameter); } QString FilterActionRemoveHeader::informationAboutNotValidAction() const { return i18n("Header name undefined."); } diff --git a/src/filter/filteractions/filteractionrewriteheader.cpp b/src/filter/filteractions/filteractionrewriteheader.cpp index 2ffb45c..26ff4af 100644 --- a/src/filter/filteractions/filteractionrewriteheader.cpp +++ b/src/filter/filteractions/filteractionrewriteheader.cpp @@ -1,249 +1,249 @@ /* * 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 "filteractionrewriteheader.h" #include #include #include #include #include using namespace MailCommon; FilterAction *FilterActionRewriteHeader::newAction() { return new FilterActionRewriteHeader; } FilterActionRewriteHeader::FilterActionRewriteHeader(QObject *parent) : FilterActionWithStringList(QStringLiteral("rewrite header"), i18n("Rewrite Header"), parent) { - mParameterList << QStringLiteral("") + mParameterList << QString() << QStringLiteral("Subject") << QStringLiteral("Reply-To") << QStringLiteral("Delivered-To") << QStringLiteral("X-KDE-PR-Message") << QStringLiteral("X-KDE-PR-Package") << QStringLiteral("X-KDE-PR-Keywords"); mParameter = mParameterList.at(0); } bool FilterActionRewriteHeader::isEmpty() const { return mParameter.isEmpty() || mRegExp.isEmpty(); } QString FilterActionRewriteHeader::informationAboutNotValidAction() const { QString info; if (mParameter.isEmpty()) { info = i18n("Header not defined"); } if (mRegExp.isEmpty()) { if (!info.isEmpty()) { info += QLatin1Char('\n'); } info += i18n("Search string is empty."); } return info; } FilterAction::ReturnCode FilterActionRewriteHeader::process(ItemContext &context, bool) const { if (isEmpty()) { return ErrorButGoOn; } const KMime::Message::Ptr msg = context.item().payload(); const QByteArray param(mParameter.toLatin1()); KMime::Headers::Base *header = msg->headerByType(param.constData()); if (!header) { return GoOn; //TODO: Maybe create a new header by type? } QString value = header->asUnicodeString(); const QString oldValue = value; const QString newValue = value.replace(mRegExp, mReplacementString); if (newValue != oldValue) { msg->removeHeader(param.constData()); KMime::Headers::Base *newheader = KMime::Headers::createHeader(param); if (!newheader) { newheader = new KMime::Headers::Generic(param.constData()); } newheader->fromUnicodeString(newValue, "utf-8"); msg->setHeader(newheader); msg->assemble(); context.setNeedsPayloadStore(); } return GoOn; } SearchRule::RequiredPart FilterActionRewriteHeader::requiredPart() const { return SearchRule::CompleteMessage; } QWidget *FilterActionRewriteHeader::createParamWidget(QWidget *parent) const { QWidget *widget = new QWidget(parent); QHBoxLayout *layout = new QHBoxLayout(widget); layout->setSpacing(4); layout->setMargin(0); PimCommon::MinimumComboBox *comboBox = new PimCommon::MinimumComboBox(widget); comboBox->setEditable(true); comboBox->setObjectName(QStringLiteral("combo")); comboBox->setInsertPolicy(QComboBox::InsertAtBottom); layout->addWidget(comboBox, 0 /* stretch */); KCompletion *comp = comboBox->completionObject(); comp->setIgnoreCase(true); comp->insertItems(mParameterList); comp->setCompletionMode(KCompletion::CompletionPopupAuto); QLabel *label = new QLabel(i18n("Replace:"), widget); label->setObjectName(QStringLiteral("label_replace")); label->setFixedWidth(label->sizeHint().width()); layout->addWidget(label, 0); KLineEdit *regExpLineEdit = new KLineEdit(widget); regExpLineEdit->setClearButtonEnabled(true); regExpLineEdit->setTrapReturnKey(true); regExpLineEdit->setObjectName(QStringLiteral("search")); layout->addWidget(regExpLineEdit, 1); label = new QLabel(i18n("With:"), widget); label->setFixedWidth(label->sizeHint().width()); label->setObjectName(QStringLiteral("label_with")); layout->addWidget(label, 0); KLineEdit *lineEdit = new KLineEdit(widget); lineEdit->setObjectName(QStringLiteral("replace")); lineEdit->setClearButtonEnabled(true); lineEdit->setTrapReturnKey(true); layout->addWidget(lineEdit, 1); setParamWidgetValue(widget); connect(comboBox, QOverload::of(&PimCommon::MinimumComboBox::currentIndexChanged), this, &FilterActionRewriteHeader::filterActionModified); connect(comboBox->lineEdit(), &QLineEdit::textChanged, this, &FilterAction::filterActionModified); connect(regExpLineEdit, &KLineEdit::textChanged, this, &FilterActionRewriteHeader::filterActionModified); connect(lineEdit, &KLineEdit::textChanged, this, &FilterActionRewriteHeader::filterActionModified); return widget; } void FilterActionRewriteHeader::setParamWidgetValue(QWidget *paramWidget) const { const int index = mParameterList.indexOf(mParameter); PimCommon::MinimumComboBox *comboBox = paramWidget->findChild(QStringLiteral("combo")); Q_ASSERT(comboBox); comboBox->clear(); comboBox->addItems(mParameterList); if (index < 0) { comboBox->addItem(mParameter); comboBox->setCurrentIndex(comboBox->count() - 1); } else { comboBox->setCurrentIndex(index); } KLineEdit *regExpLineEdit = paramWidget->findChild(QStringLiteral("search")); Q_ASSERT(regExpLineEdit); regExpLineEdit->setText(mRegExp.pattern()); KLineEdit *lineEdit = paramWidget->findChild(QStringLiteral("replace")); Q_ASSERT(lineEdit); lineEdit->setText(mReplacementString); } void FilterActionRewriteHeader::applyParamWidgetValue(QWidget *paramWidget) { const PimCommon::MinimumComboBox *comboBox = paramWidget->findChild(QStringLiteral("combo")); Q_ASSERT(comboBox); mParameter = comboBox->currentText(); const KLineEdit *regExpLineEdit = paramWidget->findChild(QStringLiteral("search")); Q_ASSERT(regExpLineEdit); mRegExp.setPattern(regExpLineEdit->text()); const KLineEdit *lineEdit = paramWidget->findChild(QStringLiteral("replace")); Q_ASSERT(lineEdit); mReplacementString = lineEdit->text(); } void FilterActionRewriteHeader::clearParamWidget(QWidget *paramWidget) const { PimCommon::MinimumComboBox *comboBox = paramWidget->findChild(QStringLiteral("combo")); Q_ASSERT(comboBox); comboBox->setCurrentIndex(0); KLineEdit *regExpLineEdit = paramWidget->findChild(QStringLiteral("search")); Q_ASSERT(regExpLineEdit); regExpLineEdit->clear(); KLineEdit *lineEdit = paramWidget->findChild(QStringLiteral("replace")); Q_ASSERT(lineEdit); lineEdit->clear(); } QString FilterActionRewriteHeader::argsAsString() const { QString result = mParameter; result += QLatin1Char('\t'); result += mRegExp.pattern(); result += QLatin1Char('\t'); result += mReplacementString; return result; } QString FilterActionRewriteHeader::displayString() const { return label() + QStringLiteral(" \"") + argsAsString().toHtmlEscaped() + QStringLiteral("\""); } void FilterActionRewriteHeader::argsFromString(const QString &argsStr) { const QStringList list = argsStr.split(QLatin1Char('\t')); if (list.count() < 3) { return; } QString result; result = list[ 0 ]; mRegExp.setPattern(list[ 1 ]); mReplacementString = list[ 2 ]; int index = mParameterList.indexOf(result); if (index < 0) { mParameterList.append(result); index = mParameterList.count() - 1; } mParameter = mParameterList.at(index); } diff --git a/src/filter/filterimporter/autotests/filtertestkernel.h b/src/filter/filterimporter/autotests/filtertestkernel.h index 7c86ddf..f31f284 100644 --- a/src/filter/filterimporter/autotests/filtertestkernel.h +++ b/src/filter/filterimporter/autotests/filtertestkernel.h @@ -1,62 +1,63 @@ /* 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, version 2, as published by the Free Software Foundation. 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 FILTERTESTKERNEL_H #define FILTERTESTKERNEL_H #include namespace Akonadi { class EntityTreeModel; class EntityMimeTypeFilterModel; } namespace MailCommon { class FolderCollectionMonitor; } class FilterTestKernel : public QObject, public MailCommon::IKernel, public MailCommon::ISettings { + Q_OBJECT public: explicit FilterTestKernel(QObject *parent = nullptr); KIdentityManagement::IdentityManager *identityManager() override; MessageComposer::MessageSender *msgSender() override; Akonadi::EntityMimeTypeFilterModel *collectionModel() const override; KSharedConfig::Ptr config() override; void syncConfig() override; MailCommon::JobScheduler *jobScheduler() const override; Akonadi::ChangeRecorder *folderCollectionMonitor() const override; void updateSystemTray() override; qreal closeToQuotaThreshold() override; bool excludeImportantMailFromExpiry() override; QStringList customTemplates() override; Akonadi::Collection::Id lastSelectedFolder() override; void setLastSelectedFolder(Akonadi::Collection::Id col) override; bool showPopupAfterDnD() override; void expunge(Akonadi::Collection::Id col, bool sync) override; private: KIdentityManagement::IdentityManager *mIdentityManager = nullptr; MessageComposer::MessageSender *mMessageSender = nullptr; MailCommon::FolderCollectionMonitor *mFolderCollectionMonitor = nullptr; Akonadi::EntityTreeModel *mEntityTreeModel = nullptr; Akonadi::EntityMimeTypeFilterModel *mCollectionModel = nullptr; }; #endif diff --git a/src/filter/filterimporter/filterimporterbalsa.cpp b/src/filter/filterimporter/filterimporterbalsa.cpp index 7f7bb19..aab186a 100644 --- a/src/filter/filterimporter/filterimporterbalsa.cpp +++ b/src/filter/filterimporter/filterimporterbalsa.cpp @@ -1,170 +1,170 @@ /* 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; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "filterimporterbalsa.h" #include "filter/filtermanager.h" #include "filter/mailfilter.h" #include "mailcommon_debug.h" #include #include #include #include #include using namespace MailCommon; FilterImporterBalsa::FilterImporterBalsa(QFile *file) : FilterImporterAbstract() { KConfig config(file->fileName()); readConfig(&config); } FilterImporterBalsa::FilterImporterBalsa() : FilterImporterAbstract() { } FilterImporterBalsa::~FilterImporterBalsa() { } QString FilterImporterBalsa::defaultFiltersSettingsPath() { return QStringLiteral("%1/.balsa/config").arg(QDir::homePath()); } void FilterImporterBalsa::readConfig(KConfig *config) { const QStringList filterList = config->groupList().filter(QRegularExpression(QStringLiteral("filter-\\d+"))); for (const QString &filter : filterList) { KConfigGroup grp = config->group(filter); parseFilter(grp); } } void FilterImporterBalsa::parseFilter(const KConfigGroup &grp) { MailCommon::MailFilter *filter = new MailCommon::MailFilter(); const QString name = grp.readEntry(QStringLiteral("Name")); filter->pattern()->setName(name); filter->setToolbarName(name); //TODO not implemented in kmail. - const QString popupText = grp.readEntry(QStringLiteral("Popup-text")); + //const QString popupText = grp.readEntry(QStringLiteral("Popup-text")); const QString sound = grp.readEntry(QStringLiteral("Sound")); if (!sound.isEmpty()) { const QString actionName = QStringLiteral("play sound"); createFilterAction(filter, actionName, sound); } const int actionType = grp.readEntry(QStringLiteral("Action-type"), -1); const QString actionStr = grp.readEntry(QStringLiteral("Action-string")); parseAction(actionType, actionStr, filter); const QString condition = grp.readEntry(QStringLiteral("Condition")); parseCondition(condition, filter); appendFilter(filter); } void FilterImporterBalsa::parseCondition(const QString &condition, MailCommon::MailFilter *filter) { QStringList conditionList; if (condition.startsWith(QLatin1String("OR "))) { conditionList = condition.split(QStringLiteral("OR")); filter->pattern()->setOp(SearchPattern::OpOr); } else if (condition.startsWith(QLatin1String("AND "))) { conditionList = condition.split(QStringLiteral("AND")); filter->pattern()->setOp(SearchPattern::OpAnd); } else { //no multi condition conditionList << condition; } for (QString cond : qAsConst(conditionList)) { cond = cond.trimmed(); if (cond.startsWith(QLatin1String("NOT"))) { cond = cond.right(cond.length() - 3); cond = cond.trimmed(); } qCDebug(MAILCOMMON_LOG) << " cond" << cond; //Date between QByteArray fieldName; if (cond.startsWith(QLatin1String("DATE"))) { fieldName = ""; cond = cond.right(cond.length() - 4); cond = cond.trimmed(); QStringList splitDate = cond.split(QLatin1Char(' ')); qCDebug(MAILCOMMON_LOG) << " splitDate " << splitDate; } else if (cond.startsWith(QLatin1String("FLAG"))) { qCDebug(MAILCOMMON_LOG) << " FLAG :"; } else if (cond.startsWith(QLatin1String("STRING"))) { qCDebug(MAILCOMMON_LOG) << " STRING"; } else { qCDebug(MAILCOMMON_LOG) << " condition not implemented :" << cond; } //SearchRule::Ptr rule = SearchRule::createInstance( fieldName, functionName, line ); //filter->pattern()->append( rule ); } } void FilterImporterBalsa::parseAction(int actionType, const QString &action, MailCommon::MailFilter *filter) { QString actionName; QString actionStr(action); switch (actionType) { case 0: break; case 1: //Copy actionName = QStringLiteral("copy"); break; case 2: //Move actionName = QStringLiteral("transfer"); break; case 3: //Print //Not implemented in kmail break; case 4: //Execute actionName = QStringLiteral("execute"); break; case 5: //Move to trash actionName = QStringLiteral("transfer"); //Special ! break; case 6: //Put color break; default: qCDebug(MAILCOMMON_LOG) << " unknown parse action type " << actionType; break; } if (!actionName.isEmpty()) { //TODO adapt actionStr createFilterAction(filter, actionName, actionStr); } } diff --git a/src/filter/kmfilterlistbox.cpp b/src/filter/kmfilterlistbox.cpp index abef8fa..9e971a6 100644 --- a/src/filter/kmfilterlistbox.cpp +++ b/src/filter/kmfilterlistbox.cpp @@ -1,887 +1,887 @@ /* 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; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "kmfilterlistbox.h" #include "mailfilter.h" #include "filtermanager.h" #include "filteractions/filteractiondict.h" #include #include "invalidfilters/invalidfilterinfo.h" #include "invalidfilters/invalidfilterdialog.h" #include #include #include #include #include #include #include #include #include "mailcommon_debug.h" #include #include // What's this help texts const char _wt_filterlist[] = I18N_NOOP("

This is the list of defined filters. " "They are processed top-to-bottom.

" "

Click on any filter to edit it " "using the controls in the right-hand half " "of the dialog.

"); const char _wt_filterlist_new[] = I18N_NOOP("

Click this button to create a new filter.

" "

The filter will be inserted just before the currently-" "selected one, but you can always change that " "later on.

" "

If you have clicked this button accidentally, you can undo this " "by clicking on the Delete button.

"); const char _wt_filterlist_copy[] = I18N_NOOP("

Click this button to copy a filter.

" "

If you have clicked this button accidentally, you can undo this " "by clicking on the Delete button.

"); const char _wt_filterlist_delete[] = I18N_NOOP("

Click this button to delete the currently-" "selected filter from the list above.

" "

There is no way to get the filter back once " "it is deleted, but you can always leave the " "dialog by clicking Cancel to discard the " "changes made.

"); const char _wt_filterlist_up[] = I18N_NOOP("

Click this button to move the currently-" "selected filter up one in the list above.

" "

This is useful since the order of the filters in the list " "determines the order in which they are tried on messages: " "The topmost filter gets tried first.

" "

If you have clicked this button accidentally, you can undo this " "by clicking on the Down button.

"); const char _wt_filterlist_down[] = I18N_NOOP("

Click this button to move the currently-" "selected filter down one in the list above.

" "

This is useful since the order of the filters in the list " "determines the order in which they are tried on messages: " "The topmost filter gets tried first.

" "

If you have clicked this button accidentally, you can undo this " "by clicking on the Up button.

"); const char _wt_filterlist_top[] = I18N_NOOP("

Click this button to move the currently-" "selected filter to top of list.

" "

This is useful since the order of the filters in the list " "determines the order in which they are tried on messages: " "The topmost filter gets tried first.

"); const char _wt_filterlist_bottom[] = I18N_NOOP("

Click this button to move the currently-" "selected filter to bottom of list.

" "

This is useful since the order of the filters in the list " "determines the order in which they are tried on messages: " "The topmost filter gets tried first.

"); const char _wt_filterlist_rename[] = I18N_NOOP("

Click this button to rename the currently-selected filter.

" "

Filters are named automatically, as long as they start with " "\"<\".

" "

If you have renamed a filter accidentally and want automatic " "naming back, click this button and select Clear followed " "by OK in the appearing dialog.

"); //============================================================================= // // class KMFilterListBox (the filter list manipulator) // //============================================================================= using namespace MailCommon; KMFilterListBox::KMFilterListBox(const QString &title, QWidget *parent) : QGroupBox(title, parent) { QVBoxLayout *layout = new QVBoxLayout(); //----------- the list box mListWidget = new QListWidget(this); mListWidget->setMinimumWidth(150); mListWidget->setWhatsThis(i18n(_wt_filterlist)); mListWidget->setDragDropMode(QAbstractItemView::InternalMove); mListWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(mListWidget->model(), &QAbstractItemModel::rowsMoved, this, &KMFilterListBox::slotRowsMoved); mSearchListWidget = new KListWidgetSearchLine(this, mListWidget); mSearchListWidget->setPlaceholderText( i18nc("@info Displayed grayed-out inside the textbox, verb to search", "Search")); mSearchListWidget->installEventFilter(this); layout->addWidget(mSearchListWidget); layout->addWidget(mListWidget); //----------- the first row of buttons QWidget *hb = new QWidget(this); QHBoxLayout *hbHBoxLayout = new QHBoxLayout(hb); hbHBoxLayout->setMargin(0); hbHBoxLayout->setSpacing(4); mBtnTop = new QPushButton(QString(), hb); hbHBoxLayout->addWidget(mBtnTop); mBtnTop->setIcon(QIcon::fromTheme(QStringLiteral("go-top"))); mBtnTop->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); mBtnTop->setMinimumSize(mBtnTop->sizeHint() * 1.2); mBtnUp = new QPushButton(QString(), hb); hbHBoxLayout->addWidget(mBtnUp); mBtnUp->setAutoRepeat(true); mBtnUp->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); mBtnUp->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); mBtnUp->setMinimumSize(mBtnUp->sizeHint() * 1.2); mBtnDown = new QPushButton(QString(), hb); hbHBoxLayout->addWidget(mBtnDown); mBtnDown->setAutoRepeat(true); mBtnDown->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); mBtnDown->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); mBtnDown->setMinimumSize(mBtnDown->sizeHint() * 1.2); mBtnBottom = new QPushButton(QString(), hb); hbHBoxLayout->addWidget(mBtnBottom); mBtnBottom->setIcon(QIcon::fromTheme(QStringLiteral("go-bottom"))); mBtnBottom->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); mBtnBottom->setMinimumSize(mBtnBottom->sizeHint() * 1.2); mBtnUp->setToolTip(i18nc("Move selected filter up.", "Up")); mBtnDown->setToolTip(i18nc("Move selected filter down.", "Down")); mBtnTop->setToolTip(i18nc("Move selected filter to the top.", "Top")); mBtnBottom->setToolTip(i18nc("Move selected filter to the bottom.", "Bottom")); mBtnUp->setWhatsThis(i18n(_wt_filterlist_up)); mBtnDown->setWhatsThis(i18n(_wt_filterlist_down)); mBtnBottom->setWhatsThis(i18n(_wt_filterlist_bottom)); mBtnTop->setWhatsThis(i18n(_wt_filterlist_top)); layout->addWidget(hb); //----------- the second row of buttons hb = new QWidget(this); hbHBoxLayout = new QHBoxLayout(hb); hbHBoxLayout->setMargin(0); hbHBoxLayout->setSpacing(4); mBtnNew = new QPushButton(hb); hbHBoxLayout->addWidget(mBtnNew); mBtnNew->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); mBtnNew->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); mBtnNew->setMinimumSize(mBtnNew->sizeHint() * 1.2); mBtnCopy = new QPushButton(hb); hbHBoxLayout->addWidget(mBtnCopy); mBtnCopy->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); mBtnCopy->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); mBtnCopy->setMinimumSize(mBtnCopy->sizeHint() * 1.2); mBtnDelete = new QPushButton(hb); hbHBoxLayout->addWidget(mBtnDelete); mBtnDelete->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); mBtnDelete->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); mBtnDelete->setMinimumSize(mBtnDelete->sizeHint() * 1.2); mBtnRename = new QPushButton(hb); mBtnRename->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); mBtnRename->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); mBtnRename->setMinimumSize(mBtnDelete->sizeHint() * 1.2); hbHBoxLayout->addWidget(mBtnRename); mBtnNew->setToolTip(i18nc("@action:button in filter list manipulator", "New")); mBtnCopy->setToolTip(i18n("Copy")); mBtnDelete->setToolTip(i18n("Delete")); mBtnRename->setToolTip(i18n("Rename")); mBtnNew->setWhatsThis(i18n(_wt_filterlist_new)); mBtnCopy->setWhatsThis(i18n(_wt_filterlist_copy)); mBtnDelete->setWhatsThis(i18n(_wt_filterlist_delete)); mBtnRename->setWhatsThis(i18n(_wt_filterlist_rename)); layout->addWidget(hb); setLayout(layout); QShortcut *shortcut = new QShortcut(this); shortcut->setKey(Qt::Key_Delete); connect(shortcut, &QShortcut::activated, this, &KMFilterListBox::slotDelete); //----------- now connect everything connect(mListWidget, &QListWidget::currentRowChanged, this, &KMFilterListBox::slotSelected); connect(mListWidget, &QListWidget::itemDoubleClicked, this, &KMFilterListBox::slotRename); connect(mListWidget, &QListWidget::itemChanged, this, &KMFilterListBox::slotFilterEnabledChanged); connect(mListWidget, &QListWidget::itemSelectionChanged, this, &KMFilterListBox::slotSelectionChanged); connect(mBtnUp, &QPushButton::clicked, this, &KMFilterListBox::slotUp); connect(mBtnDown, &QPushButton::clicked, this, &KMFilterListBox::slotDown); connect(mBtnTop, &QPushButton::clicked, this, &KMFilterListBox::slotTop); connect(mBtnBottom, &QPushButton::clicked, this, &KMFilterListBox::slotBottom); connect(mBtnNew, &QPushButton::clicked, this, &KMFilterListBox::slotNew); connect(mBtnCopy, &QPushButton::clicked, this, &KMFilterListBox::slotCopy); connect(mBtnDelete, &QPushButton::clicked, this, &KMFilterListBox::slotDelete); connect(mBtnRename, &QPushButton::clicked, this, &KMFilterListBox::slotRename); // the dialog should call loadFilterList() // when all signals are connected. enableControls(); } KMFilterListBox::~KMFilterListBox() { } bool KMFilterListBox::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyPress && obj == mSearchListWidget) { QKeyEvent *key = static_cast(event); if ((key->key() == Qt::Key_Enter) || (key->key() == Qt::Key_Return)) { event->accept(); return true; } } return QGroupBox::eventFilter(obj, event); } bool KMFilterListBox::itemIsValid(QListWidgetItem *item) const { if (!item) { qCDebug(MAILCOMMON_LOG) << "Called while no filter is selected, ignoring."; return false; } if (item->isHidden()) { return false; } return true; } void KMFilterListBox::slotFilterEnabledChanged(QListWidgetItem *item) { if (!item) { qCDebug(MAILCOMMON_LOG) << "Called while no filter is selected, ignoring."; return; } QListWidgetFilterItem *itemFilter = static_cast(item); MailCommon::MailFilter *filter = itemFilter->filter(); filter->setEnabled((item->checkState() == Qt::Checked)); Q_EMIT filterUpdated(filter); } void KMFilterListBox::slotRowsMoved(const QModelIndex &, int sourcestart, int sourceEnd, const QModelIndex &, int destinationRow) { Q_UNUSED(sourceEnd); Q_UNUSED(sourcestart); Q_UNUSED(destinationRow); enableControls(); Q_EMIT filterOrderAltered(); } void KMFilterListBox::createFilter(const QByteArray &field, const QString &value) { SearchRule::Ptr newRule = SearchRule::createInstance(field, SearchRule::FuncContains, value); MailFilter *newFilter = new MailFilter(); newFilter->pattern()->append(newRule); newFilter->pattern()->setName(QStringLiteral("<%1>: %2"). arg(QString::fromLatin1(field)). arg(value)); FilterActionDesc *desc = MailCommon::FilterManager::filterActionDict()->value(QStringLiteral("transfer")); if (desc) { newFilter->actions()->append(desc->create()); } insertFilter(newFilter); enableControls(); } void KMFilterListBox::slotUpdateFilterName() { QListWidgetItem *item = mListWidget->currentItem(); if (!item) { qCDebug(MAILCOMMON_LOG) << "Called while no filter is selected, ignoring."; return; } QListWidgetFilterItem *itemFilter = static_cast(item); MailCommon::MailFilter *filter = itemFilter->filter(); SearchPattern *p = filter->pattern(); if (!p) { return; } QString shouldBeName = p->name(); QString displayedName = itemFilter->text(); if (shouldBeName.trimmed().isEmpty()) { filter->setAutoNaming(true); } if (filter->isAutoNaming()) { // auto-naming of patterns if (!p->isEmpty() && p->first() && !p->first()->field().trimmed().isEmpty()) { shouldBeName = QStringLiteral("<%1>: %2"). arg(QString::fromLatin1(p->first()->field())). arg(p->first()->contents()); } else { shouldBeName = QLatin1Char('<') + i18n("unnamed") + QLatin1Char('>'); } p->setName(shouldBeName); } if (displayedName == shouldBeName) { return; } filter->setToolbarName(shouldBeName); mListWidget->blockSignals(true); itemFilter->setText(shouldBeName); mListWidget->blockSignals(false); } void KMFilterListBox::slotAccepted() { applyFilterChanged(true); } void KMFilterListBox::slotApplied() { applyFilterChanged(false); } void KMFilterListBox::applyFilterChanged(bool closeAfterSaving) { if (mListWidget->currentItem()) { Q_EMIT applyWidgets(); slotSelected(mListWidget->currentRow()); } // by now all edit widgets should have written back // their widget's data into our filter list. bool wasCanceled = false; const QList newFilters = filtersForSaving(closeAfterSaving, wasCanceled); if (!wasCanceled) { MailCommon::FilterManager::instance()->setFilters(newFilters); } } QList KMFilterListBox::filtersForSaving(bool closeAfterSaving, bool &wasCanceled) const { - const_cast(this)->applyWidgets(); // signals aren't const + Q_EMIT const_cast(this)->applyWidgets(); // signals aren't const QList filters; QStringList emptyFilters; QVector listInvalidFilters; const int numberOfFilter(mListWidget->count()); for (int i = 0; i < numberOfFilter; ++i) { QListWidgetFilterItem *itemFilter = static_cast(mListWidget->item(i)); MailFilter *f = new MailFilter(*itemFilter->filter()); // deep copy const QString information = f->purify(); if (!f->isEmpty() && information.isEmpty()) { // the filter is valid: filters.append(f); } else { // the filter is invalid: emptyFilters << f->name(); listInvalidFilters.append(MailCommon::InvalidFilterInfo(f->name(), information)); delete f; } } // report on invalid filters: if (!emptyFilters.empty()) { QPointer dlg = new MailCommon::InvalidFilterDialog(nullptr); dlg->setInvalidFilters(listInvalidFilters); if (!dlg->exec()) { if (closeAfterSaving) { Q_EMIT abortClosing(); } wasCanceled = true; } delete dlg; } return filters; } void KMFilterListBox::slotSelectionChanged() { if (mListWidget->selectedItems().count() > 1) { Q_EMIT resetWidgets(); } enableControls(); } void KMFilterListBox::slotSelected(int aIdx) { if (aIdx >= 0 && aIdx < mListWidget->count()) { QListWidgetFilterItem *itemFilter = static_cast(mListWidget->item(aIdx)); MailFilter *f = itemFilter->filter(); if (f) { Q_EMIT filterSelected(f); } else { Q_EMIT resetWidgets(); } } else { Q_EMIT resetWidgets(); } enableControls(); } void KMFilterListBox::slotNew() { QListWidgetItem *item = mListWidget->currentItem(); if (item && item->isHidden()) { return; } // just insert a new filter. insertFilter(new MailFilter()); enableControls(); } void KMFilterListBox::slotCopy() { QListWidgetItem *item = mListWidget->currentItem(); if (!itemIsValid(item)) { return; } // make sure that all changes are written to the filter before we copy it Q_EMIT applyWidgets(); QListWidgetFilterItem *itemFilter = static_cast(item); MailFilter *filter = itemFilter->filter(); // enableControls should make sure this method is // never called when no filter is selected. Q_ASSERT(filter); // inserts a copy of the current filter. MailFilter *copyFilter = new MailFilter(*filter); copyFilter->generateRandomIdentifier(); copyFilter->setShortcut(QKeySequence()); insertFilter(copyFilter); enableControls(); } void KMFilterListBox::slotDelete() { QListWidgetItem *itemFirst = mListWidget->currentItem(); if (!itemIsValid(itemFirst)) { return; } const bool uniqFilterSelected = (mListWidget->selectedItems().count() == 1); QListWidgetFilterItem *itemFilter = static_cast(itemFirst); MailCommon::MailFilter *filter = itemFilter->filter(); const QString filterName = filter->pattern()->name(); if (uniqFilterSelected) { if (KMessageBox::questionYesNo( this, i18n("Do you want to remove the filter \"%1\"?", filterName), i18n("Remove Filter")) == KMessageBox::No) { return; } } else { if (KMessageBox::questionYesNo( this, i18n("Do you want to remove selected filters?"), i18n("Remove Filters")) == KMessageBox::No) { return; } } const int oIdxSelItem = mListWidget->currentRow(); QList lst; Q_EMIT resetWidgets(); const QList lstItems = mListWidget->selectedItems(); for (QListWidgetItem *item : lstItems) { QListWidgetFilterItem *itemFilter = static_cast(item); MailCommon::MailFilter *filter = itemFilter->filter(); lst << filter; // remove the filter from both the listbox QListWidgetItem *item2 = mListWidget->takeItem(mListWidget->row(item)); delete item2; } const int count = mListWidget->count(); // and set the new current item. if (count > oIdxSelItem) { // oIdxItem is still a valid index mListWidget->setCurrentRow(oIdxSelItem); } else if (count) { // oIdxSelIdx is no longer valid, but the // list box isn't empty mListWidget->setCurrentRow(count - 1); } // work around a problem when deleting the first item in a QListWidget: // after takeItem, slotSelectionChanged is emitted with 1, but the row 0 // remains selected and another selectCurrentRow(0) does not trigger the // selectionChanged signal // (qt-copy as of 2006-12-22 / gungl) if (oIdxSelItem == 0) { slotSelected(0); } enableControls(); Q_EMIT filterRemoved(lst); } void KMFilterListBox::slotTop() { QList listWidgetItem = selectedFilter(); if (listWidgetItem.isEmpty()) { return; } const int numberOfItem(listWidgetItem.count()); if ((numberOfItem == 1) && (mListWidget->currentRow() == 0)) { qCDebug(MAILCOMMON_LOG) << "Called while the _topmost_ filter is selected, ignoring."; return; } QListWidgetItem *item = nullptr; bool wasMoved = false; for (int i = 0; i < numberOfItem; ++i) { const int posItem = mListWidget->row(listWidgetItem.at(i)); if (posItem == i) { continue; } item = mListWidget->takeItem(mListWidget->row(listWidgetItem.at(i))); mListWidget->insertItem(i, item); wasMoved = true; } if (wasMoved) { enableControls(); Q_EMIT filterOrderAltered(); } } QList KMFilterListBox::selectedFilter() { QList listWidgetItem; const int numberOfFilters = mListWidget->count(); for (int i = 0; i < numberOfFilters; ++i) { if (mListWidget->item(i)->isSelected() && !mListWidget->item(i)->isHidden()) { listWidgetItem << mListWidget->item(i); } } return listWidgetItem; } QStringList KMFilterListBox::selectedFilterId(SearchRule::RequiredPart &requiredPart, const QString &resource) const { QStringList listFilterId; requiredPart = SearchRule::Envelope; const int numberOfFilters = mListWidget->count(); for (int i = 0; i < numberOfFilters; ++i) { if (mListWidget->item(i)->isSelected() && !mListWidget->item(i)->isHidden()) { MailFilter *filter = static_cast(mListWidget->item(i))->filter(); if (!filter->isEmpty()) { const QString id = filter->identifier(); listFilterId << id; requiredPart = qMax(requiredPart, static_cast(mListWidget->item(i))->filter()->requiredPart(resource)); } } } return listFilterId; } void KMFilterListBox::slotBottom() { const QList listWidgetItem = selectedFilter(); if (listWidgetItem.isEmpty()) { return; } const int numberOfElement(mListWidget->count()); const int numberOfItem(listWidgetItem.count()); if ((numberOfItem == 1) && (mListWidget->currentRow() == numberOfElement - 1)) { qCDebug(MAILCOMMON_LOG) << "Called while the _last_ filter is selected, ignoring."; return; } QListWidgetItem *item = nullptr; int j = 0; bool wasMoved = false; for (int i = numberOfItem - 1; i >= 0; --i, j++) { const int posItem = mListWidget->row(listWidgetItem.at(i)); if (posItem == (numberOfElement - 1 - j)) { continue; } item = mListWidget->takeItem(mListWidget->row(listWidgetItem.at(i))); mListWidget->insertItem(numberOfElement - j, item); wasMoved = true; } if (wasMoved) { enableControls(); Q_EMIT filterOrderAltered(); } } void KMFilterListBox::slotUp() { const QList listWidgetItem = selectedFilter(); if (listWidgetItem.isEmpty()) { return; } const int numberOfItem(listWidgetItem.count()); if ((numberOfItem == 1) && (mListWidget->currentRow() == 0)) { qCDebug(MAILCOMMON_LOG) << "Called while the _topmost_ filter is selected, ignoring."; return; } bool wasMoved = false; for (int i = 0; i < numberOfItem; ++i) { const int posItem = mListWidget->row(listWidgetItem.at(i)); if (posItem == i) { continue; } swapNeighbouringFilters(posItem, posItem - 1); wasMoved = true; } if (wasMoved) { enableControls(); Q_EMIT filterOrderAltered(); } } void KMFilterListBox::slotDown() { const QList listWidgetItem = selectedFilter(); if (listWidgetItem.isEmpty()) { return; } const int numberOfElement(mListWidget->count()); const int numberOfItem(listWidgetItem.count()); if ((numberOfItem == 1) && (mListWidget->currentRow() == numberOfElement - 1)) { qCDebug(MAILCOMMON_LOG) << "Called while the _last_ filter is selected, ignoring."; return; } int j = 0; bool wasMoved = false; for (int i = numberOfItem - 1; i >= 0; --i, j++) { const int posItem = mListWidget->row(listWidgetItem.at(i)); if (posItem == (numberOfElement - 1 - j)) { continue; } swapNeighbouringFilters(posItem, posItem + 1); wasMoved = true; } if (wasMoved) { enableControls(); Q_EMIT filterOrderAltered(); } } void KMFilterListBox::slotRename() { QListWidgetItem *item = mListWidget->currentItem(); if (!itemIsValid(item)) { return; } QListWidgetFilterItem *itemFilter = static_cast(item); bool okPressed = false; MailFilter *filter = itemFilter->filter(); // enableControls should make sure this method is // never called when no filter is selected. Q_ASSERT(filter); // allow empty names - those will turn auto-naming on again QString newName = QInputDialog::getText(window(), i18n("Rename Filter"), i18n("Rename filter \"%1\" to:\n(leave the field empty for automatic naming)", filter->pattern()->name()), /*label*/ QLineEdit::Normal, filter->pattern()->name(), /* initial value */ &okPressed); if (!okPressed) { return; } if (newName.isEmpty()) { // bait for slotUpdateFilterName to // use automatic naming again. filter->pattern()->setName(QStringLiteral("<>")); filter->setAutoNaming(true); } else { filter->pattern()->setName(newName); filter->setAutoNaming(false); } slotUpdateFilterName(); Q_EMIT filterUpdated(filter); } void KMFilterListBox::enableControls() { const int currentIndex = mListWidget->currentRow(); const bool theFirst = (currentIndex == 0); const int numberOfElement(mListWidget->count()); const bool theLast = (currentIndex >= numberOfElement - 1); const bool aFilterIsSelected = (currentIndex >= 0); const int numberOfSelectedItem(mListWidget->selectedItems().count()); const bool uniqFilterSelected = (numberOfSelectedItem == 1); const bool allItemSelected = (numberOfSelectedItem == numberOfElement); mBtnUp->setEnabled(aFilterIsSelected && ((uniqFilterSelected && !theFirst) || (!uniqFilterSelected)) && !allItemSelected); mBtnDown->setEnabled(aFilterIsSelected && ((uniqFilterSelected && !theLast) || (!uniqFilterSelected)) && !allItemSelected); mBtnCopy->setEnabled(aFilterIsSelected && uniqFilterSelected); mBtnDelete->setEnabled(aFilterIsSelected); mBtnRename->setEnabled(aFilterIsSelected && uniqFilterSelected); mBtnTop->setEnabled(aFilterIsSelected && ((uniqFilterSelected && !theFirst) || (!uniqFilterSelected)) && !allItemSelected); mBtnBottom->setEnabled(aFilterIsSelected && ((uniqFilterSelected && !theLast) || (!uniqFilterSelected)) && !allItemSelected); if (aFilterIsSelected) { mListWidget->scrollToItem(mListWidget->currentItem()); } } void KMFilterListBox::loadFilterList(bool createDummyFilter) { Q_ASSERT(mListWidget); setEnabled(false); Q_EMIT resetWidgets(); // we don't want the insertion to // cause flicker in the edit widgets. blockSignals(true); // clear both lists mListWidget->clear(); const QList filters = MailCommon::FilterManager::instance()->filters(); for (MailFilter *filter : filters) { QListWidgetFilterItem *item = new QListWidgetFilterItem(filter->pattern()->name(), mListWidget); item->setFilter(new MailFilter(*filter)); mListWidget->addItem(item); } blockSignals(false); setEnabled(true); // create an empty filter when there's none, to avoid a completely // disabled dialog (usability tests indicated that the new-filter // button is too hard to find that way): const int numberOfItem(mListWidget->count()); if (numberOfItem == 0) { if (createDummyFilter) { slotNew(); } } else { mListWidget->setCurrentRow(0); } enableControls(); } void KMFilterListBox::insertFilter(MailFilter *aFilter) { // must be really a filter... Q_ASSERT(aFilter); const int currentIndex = mListWidget->currentRow(); // if mIdxSelItem < 0, QListBox::insertItem will append. QListWidgetFilterItem *item = new QListWidgetFilterItem(aFilter->pattern()->name()); item->setFilter(aFilter); mListWidget->insertItem(currentIndex, item); mListWidget->clearSelection(); if (currentIndex < 0) { mListWidget->setCurrentRow(mListWidget->count() - 1); } else { // insert just before selected mListWidget->setCurrentRow(currentIndex); } Q_EMIT filterCreated(); Q_EMIT filterOrderAltered(); } void KMFilterListBox::appendFilter(MailFilter *aFilter) { QListWidgetFilterItem *item = new QListWidgetFilterItem(aFilter->pattern()->name(), mListWidget); item->setFilter(aFilter); mListWidget->addItem(item); Q_EMIT filterCreated(); } void KMFilterListBox::swapNeighbouringFilters(int untouchedOne, int movedOne) { // must be neighbours... Q_ASSERT(untouchedOne - movedOne == 1 || movedOne - untouchedOne == 1); // untouchedOne is at idx. to move it down(up), // remove item at idx+(-)1 w/o deleting it. QListWidgetItem *item = mListWidget->takeItem(movedOne); // now selected item is at idx(idx-1), so // insert the other item at idx, ie. above(below). mListWidget->insertItem(untouchedOne, item); } QListWidgetFilterItem::QListWidgetFilterItem(const QString &text, QListWidget *parent) : QListWidgetItem(text, parent) , mFilter(nullptr) { } QListWidgetFilterItem::~QListWidgetFilterItem() { delete mFilter; } void QListWidgetFilterItem::setFilter(MailCommon::MailFilter *filter) { mFilter = filter; setCheckState(filter->isEnabled() ? Qt::Checked : Qt::Unchecked); } MailCommon::MailFilter *QListWidgetFilterItem::filter() { return mFilter; } diff --git a/src/filter/mailfilter.cpp b/src/filter/mailfilter.cpp index a9c6480..4027201 100644 --- a/src/filter/mailfilter.cpp +++ b/src/filter/mailfilter.cpp @@ -1,819 +1,819 @@ /* * kmail: KDE mail client * Copyright (c) 1996-1998 Stefan Taferner * 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. * */ // my header #include "mailfilter.h" // other kmail headers #include "filteractions/filteraction.h" #include "filteractions/filteractiondict.h" #include "filtermanager.h" #include "filterlog.h" #include "dialog/filteractionmissingaccountdialog.h" using MailCommon::FilterLog; #include // KDEPIMLIBS headers #include // other KDE headers #include #include #include #include #include #include #include #include using namespace MailCommon; MailFilter::MailFilter() { generateRandomIdentifier(); bApplyOnInbound = true; bApplyBeforeOutbound = false; bApplyOnOutbound = false; bApplyOnExplicit = true; bApplyOnAllFolders = false; bStopProcessingHere = true; bConfigureShortcut = false; bConfigureToolbar = false; bAutoNaming = true; mApplicability = All; bEnabled = true; } MailFilter::MailFilter(const KConfigGroup &aConfig, bool interactive, bool &needUpdate) { needUpdate = readConfig(aConfig, interactive); } MailFilter::MailFilter(const MailFilter &aFilter) { mIdentifier = aFilter.mIdentifier; mPattern = aFilter.mPattern; bApplyOnInbound = aFilter.applyOnInbound(); bApplyBeforeOutbound = aFilter.applyBeforeOutbound(); bApplyOnOutbound = aFilter.applyOnOutbound(); bApplyOnExplicit = aFilter.applyOnExplicit(); bApplyOnAllFolders = aFilter.applyOnAllFoldersInbound(); bStopProcessingHere = aFilter.stopProcessingHere(); bConfigureShortcut = aFilter.configureShortcut(); bConfigureToolbar = aFilter.configureToolbar(); mToolbarName = aFilter.toolbarName(); mApplicability = aFilter.applicability(); bAutoNaming = aFilter.isAutoNaming(); bEnabled = aFilter.isEnabled(); mIcon = aFilter.icon(); mShortcut = aFilter.shortcut(); QListIterator it(aFilter.mActions); while (it.hasNext()) { FilterAction *action = it.next(); FilterActionDesc *desc = FilterManager::filterActionDict()->value(action->name()); if (desc) { FilterAction *f = desc->create(); if (f) { f->argsFromString(action->argsAsString()); mActions.append(f); } } } mAccounts.clear(); QStringList::ConstIterator it2; QStringList::ConstIterator end2 = aFilter.mAccounts.constEnd(); for (it2 = aFilter.mAccounts.constBegin(); it2 != end2; ++it2) { mAccounts.append(*it2); } } MailFilter::~MailFilter() { qDeleteAll(mActions); } int MailFilter::filterActionsMaximumSize() { return 8; } void MailFilter::generateRandomIdentifier() { mIdentifier = KRandom::randomString(16); } QString MailFilter::identifier() const { return mIdentifier; } QString MailFilter::name() const { return mPattern.name(); } MailFilter::ReturnCode MailFilter::execActions(ItemContext &context, bool &stopIt, bool applyOnOutbound) const { ReturnCode status = NoResult; QList::const_iterator it(mActions.constBegin()); QList::const_iterator end(mActions.constEnd()); for (; it != end; ++it) { if (FilterLog::instance()->isLogging()) { const QString logText(i18n("Applying filter action: %1", (*it)->displayString())); FilterLog::instance()->add(logText, FilterLog::AppliedAction); } FilterAction::ReturnCode result = (*it)->process(context, applyOnOutbound); switch (result) { case FilterAction::CriticalError: if (FilterLog::instance()->isLogging()) { const QString logText = QStringLiteral("%1") .arg(i18n("A critical error occurred. Processing stops here.")); FilterLog::instance()->add(logText, FilterLog::AppliedAction); } // in case it's a critical error: return immediately! return CriticalError; case FilterAction::ErrorButGoOn: if (FilterLog::instance()->isLogging()) { const QString logText = QStringLiteral("%1") .arg(i18n("A problem was found while applying this action.")); FilterLog::instance()->add(logText, FilterLog::AppliedAction); } case FilterAction::GoOn: case FilterAction::ErrorNeedComplete: break; } } if (status == NoResult) { // No filters matched, keep copy of message status = GoOn; } stopIt = stopProcessingHere(); return status; } QList *MailFilter::actions() { return &mActions; } const QList *MailFilter::actions() const { return &mActions; } SearchPattern *MailFilter::pattern() { return &mPattern; } const SearchPattern *MailFilter::pattern() const { return &mPattern; } void MailFilter::setApplyOnOutbound(bool aApply) { bApplyOnOutbound = aApply; } void MailFilter::setApplyBeforeOutbound(bool aApply) { bApplyBeforeOutbound = aApply; } bool MailFilter::applyOnOutbound() const { return bApplyOnOutbound; } bool MailFilter::applyBeforeOutbound() const { return bApplyBeforeOutbound; } void MailFilter::setApplyOnInbound(bool aApply) { bApplyOnInbound = aApply; } bool MailFilter::applyOnInbound() const { return bApplyOnInbound; } void MailFilter::setApplyOnExplicit(bool aApply) { bApplyOnExplicit = aApply; } bool MailFilter::applyOnExplicit() const { return bApplyOnExplicit; } void MailFilter::setApplyOnAllFoldersInbound(bool aApply) { bApplyOnAllFolders = aApply; } bool MailFilter::applyOnAllFoldersInbound() const { return bApplyOnAllFolders; } void MailFilter::setApplicability(AccountType aApply) { mApplicability = aApply; } MailFilter::AccountType MailFilter::applicability() const { return mApplicability; } SearchRule::RequiredPart MailFilter::requiredPart(const QString &id) const { //find the required message part needed for the filter //this can be either only the Envelope, all Header or the CompleteMessage //Makes the assumption that Envelope < Header < CompleteMessage int requiredPart = SearchRule::Envelope; if (!bEnabled || !applyOnAccount(id)) { return static_cast(requiredPart); } if (pattern()) { requiredPart = qMax(requiredPart, static_cast(pattern()->requiredPart())); // no pattern means always matches? } int requiredPartByActions = SearchRule::Envelope; QList actionList = *actions(); if (!actionList.isEmpty()) { requiredPartByActions = (*std::max_element(actionList.constBegin(), actionList.constEnd(), boost::bind(&MailCommon::FilterAction::requiredPart, _1) < boost::bind(&MailCommon::FilterAction::requiredPart, _2)))->requiredPart(); } requiredPart = qMax(requiredPart, requiredPartByActions); return static_cast(requiredPart); } void MailFilter::agentRemoved(const QString &identifier) { mAccounts.removeAll(identifier); } void MailFilter::folderRemoved(const Akonadi::Collection &aFolder, const Akonadi::Collection &aNewFolder) { QListIterator it(mActions); while (it.hasNext()) { it.next()->folderRemoved(aFolder, aNewFolder); } } void MailFilter::clearApplyOnAccount() { mAccounts.clear(); } void MailFilter::setApplyOnAccount(const QString &id, bool aApply) { if (aApply && !mAccounts.contains(id)) { mAccounts.append(id); } else if (!aApply && mAccounts.contains(id)) { mAccounts.removeAll(id); } } bool MailFilter::applyOnAccount(const QString &id) const { if (applicability() == All) { return true; } if (applicability() == ButImap) { Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(id); if (instance.isValid()) { return !PimCommon::Util::isImapResource(instance.type().identifier()); } else { return false; } } if (applicability() == Checked) { return mAccounts.contains(id); } return false; } void MailFilter::setStopProcessingHere(bool aStop) { bStopProcessingHere = aStop; } bool MailFilter::stopProcessingHere() const { return bStopProcessingHere; } void MailFilter::setConfigureShortcut(bool aShort) { bConfigureShortcut = aShort; bConfigureToolbar = (bConfigureToolbar && bConfigureShortcut); } bool MailFilter::configureShortcut() const { return bConfigureShortcut; } void MailFilter::setConfigureToolbar(bool aTool) { bConfigureToolbar = (aTool && bConfigureShortcut); } bool MailFilter::configureToolbar() const { return bConfigureToolbar; } void MailFilter::setToolbarName(const QString &toolbarName) { mToolbarName = toolbarName; } void MailFilter::setShortcut(const QKeySequence &shortcut) { mShortcut = shortcut; } const QKeySequence &MailFilter::shortcut() const { return mShortcut; } void MailFilter::setIcon(const QString &icon) { mIcon = icon; } QString MailFilter::icon() const { return mIcon; } void MailFilter::setAutoNaming(bool useAutomaticNames) { bAutoNaming = useAutomaticNames; } bool MailFilter::isAutoNaming() const { return bAutoNaming; } //----------------------------------------------------------------------------- bool MailFilter::readConfig(const KConfigGroup &config, bool interactive) { bool needUpdate = false; // MKSearchPattern::readConfig ensures // that the pattern is purified. mPattern.readConfig(config); mIdentifier = config.readEntry("identifier", KRandom::randomString(16)); const QStringList sets = config.readEntry("apply-on", QStringList()); if (sets.isEmpty() && !config.hasKey("apply-on")) { bApplyBeforeOutbound = false; bApplyOnOutbound = false; bApplyOnInbound = true; bApplyOnExplicit = true; bApplyOnAllFolders = false; mApplicability = ButImap; } else { bApplyBeforeOutbound = bool(sets.contains(QLatin1String("before-send-mail"))); bApplyOnInbound = bool(sets.contains(QLatin1String("check-mail"))); bApplyOnOutbound = bool(sets.contains(QLatin1String("send-mail"))); bApplyOnExplicit = bool(sets.contains(QLatin1String("manual-filtering"))); bApplyOnAllFolders = bool(sets.contains(QLatin1String("all-folders"))); mApplicability = static_cast(config.readEntry( "Applicability", static_cast(ButImap))); } bStopProcessingHere = config.readEntry("StopProcessingHere", true); bConfigureShortcut = config.readEntry("ConfigureShortcut", false); QString shortcut(config.readEntry("Shortcut", QString())); if (!shortcut.isEmpty()) { QKeySequence sc(shortcut); setShortcut(sc); } bConfigureToolbar = config.readEntry("ConfigureToolbar", false); bConfigureToolbar = bConfigureToolbar && bConfigureShortcut; mToolbarName = config.readEntry("ToolbarName", name()); mIcon = config.readEntry("Icon", "system-run"); bAutoNaming = config.readEntry("AutomaticName", false); bEnabled = config.readEntry("Enabled", true); QString actName, argsName; mActions.clear(); int numActions = config.readEntry("actions", 0); if (numActions > filterActionsMaximumSize()) { numActions = filterActionsMaximumSize(); KMessageBox::information(nullptr, i18n("Too many filter actions in filter rule %1.", mPattern.name())); } for (int i = 0; i < numActions; ++i) { actName.sprintf("action-name-%d", i); argsName.sprintf("action-args-%d", i); // get the action description... FilterActionDesc *desc = FilterManager::filterActionDict()->value( config.readEntry(actName, QString())); if (desc) { //...create an instance... FilterAction *fa = desc->create(); if (fa) { //...load it with it's parameter... if (interactive) { const bool ret = fa->argsFromStringInteractive(config.readEntry(argsName, QString()), name()); if (ret) { needUpdate = true; } } else { fa->argsFromString(config.readEntry(argsName, QString())); } //...check if it's empty and... if (!fa->isEmpty()) { //...append it if it's not and... mActions.append(fa); } else { //...delete is else. delete fa; } } } else { KMessageBox::information(nullptr /* app-global modal dialog box */, i18n("Unknown filter action %1
in filter rule %2.
Ignoring it.
", config.readEntry(actName, QString()), mPattern.name())); } } mAccounts = config.readEntry("accounts-set", QStringList()); if (!mAccounts.isEmpty() && interactive) { if (!MailCommon::FilterActionMissingAccountDialog::allAccountExist(mAccounts)) { QPointer dlg = new MailCommon::FilterActionMissingAccountDialog(mAccounts, name()); if (dlg->exec()) { mAccounts = dlg->selectedAccount(); needUpdate = true; } delete dlg; } } return needUpdate; } void MailFilter::generateSieveScript(QStringList &requiresModules, QString &code) { mPattern.generateSieveScript(requiresModules, code); QList::const_iterator it; QList::const_iterator end(mActions.constEnd()); const QString indentationStr{ QStringLiteral(" ") }; code += QLatin1String(")\n{\n"); bool firstAction = true; for (it = mActions.constBegin(); it != end; ++it) { //Add endline here. if (firstAction) { firstAction = false; } else { code += QLatin1Char('\n'); } code += indentationStr + (*it)->sieveCode(); const QStringList lstRequires = (*it)->sieveRequires(); for (const QString &str : lstRequires) { if (!requiresModules.contains(str)) { requiresModules.append(str); } } } if (bStopProcessingHere) { code += QLatin1Char('\n') + indentationStr + QStringLiteral("stop;"); } code += QLatin1String("\n}\n"); } void MailFilter::writeConfig(KConfigGroup &config, bool exportFilter) const { mPattern.writeConfig(config); config.writeEntry("identifier", mIdentifier); QStringList sets; if (bApplyOnInbound) { - sets.append(QLatin1String("check-mail")); + sets.append(QStringLiteral("check-mail")); } if (bApplyBeforeOutbound) { - sets.append(QLatin1String("before-send-mail")); + sets.append(QStringLiteral("before-send-mail")); } if (bApplyOnOutbound) { - sets.append(QLatin1String("send-mail")); + sets.append(QStringLiteral("send-mail")); } if (bApplyOnExplicit) { - sets.append(QLatin1String("manual-filtering")); + sets.append(QStringLiteral("manual-filtering")); } if (bApplyOnAllFolders) { - sets.append(QLatin1String("all-folders")); + sets.append(QStringLiteral("all-folders")); } config.writeEntry("apply-on", sets); config.writeEntry("StopProcessingHere", bStopProcessingHere); config.writeEntry("ConfigureShortcut", bConfigureShortcut); if (!mShortcut.isEmpty()) { config.writeEntry("Shortcut", mShortcut.toString()); } config.writeEntry("ConfigureToolbar", bConfigureToolbar); config.writeEntry("ToolbarName", mToolbarName); if (!mIcon.isEmpty()) { config.writeEntry("Icon", mIcon); } config.writeEntry("AutomaticName", bAutoNaming); config.writeEntry("Applicability", static_cast(mApplicability)); config.writeEntry("Enabled", bEnabled); QString key; int i; QList::const_iterator it; QList::const_iterator end(mActions.constEnd()); for (i = 0, it = mActions.constBegin(); it != end; ++it, ++i) { config.writeEntry(key.sprintf("action-name-%d", i), (*it)->name()); config.writeEntry(key.sprintf("action-args-%d", i), exportFilter ? (*it)->argsAsStringReal() : (*it)->argsAsString()); } config.writeEntry("actions", i); if (!mAccounts.isEmpty()) { config.writeEntry("accounts-set", mAccounts); } } QString MailFilter::purify(bool removeAction) { QString informationAboutNotValidAction = mPattern.purify(removeAction); if (mActions.isEmpty()) { if (!informationAboutNotValidAction.isEmpty()) { informationAboutNotValidAction += QLatin1Char('\n'); } informationAboutNotValidAction += i18n("Any action defined."); } else { QListIterator it(mActions); it.toBack(); while (it.hasPrevious()) { FilterAction *action = it.previous(); if (action->isEmpty()) { if (!informationAboutNotValidAction.isEmpty()) { informationAboutNotValidAction += QLatin1Char('\n'); } informationAboutNotValidAction += action->informationAboutNotValidAction(); if (removeAction) { mActions.removeAll(action); } } } } if (!Akonadi::AgentManager::self()->instances().isEmpty()) { // safety test to ensure that Akonadi system is ready // Remove invalid accounts from mAccounts - just to be tidy QStringList::Iterator it2 = mAccounts.begin(); while (it2 != mAccounts.end()) { if (!Akonadi::AgentManager::self()->instance(*it2).isValid()) { it2 = mAccounts.erase(it2); } else { ++it2; } } } return informationAboutNotValidAction; } bool MailFilter::isEmpty() const { return (mPattern.isEmpty() && mActions.isEmpty()) || ((applicability() == Checked) && (bApplyOnInbound && mAccounts.isEmpty())); } QString MailFilter::toolbarName() const { if (mToolbarName.isEmpty()) { return name(); } else { return mToolbarName; } } const QString MailFilter::asString() const { QString result; result += QStringLiteral("Filter name: ") + name() + QStringLiteral(" (") + mIdentifier + QStringLiteral(")\n"); result += mPattern.asString() + QLatin1Char('\n'); result += QString("Filter is %1\n").arg(bEnabled ? QStringLiteral("enabled") : QStringLiteral("disabled")); QList::const_iterator it(mActions.constBegin()); QList::const_iterator end(mActions.constEnd()); for (; it != end; ++it) { result += QStringLiteral(" action: "); result += (*it)->label(); result += QLatin1Char(' '); result += (*it)->argsAsString(); result += QLatin1Char('\n'); } result += QStringLiteral("This filter belongs to the following sets:"); if (bApplyOnInbound) { result += QStringLiteral(" Inbound"); } if (bApplyBeforeOutbound) { result += QStringLiteral(" before-Outbound"); } if (bApplyOnOutbound) { result += QStringLiteral(" Outbound"); } if (bApplyOnExplicit) { result += QStringLiteral(" Explicit"); } if (bApplyOnAllFolders) { result += QStringLiteral(" All Folders"); } result += QLatin1Char('\n'); if (bApplyOnInbound && mApplicability == All) { result += QStringLiteral("This filter applies to all accounts.\n"); } else if (bApplyOnInbound && mApplicability == ButImap) { result += QStringLiteral("This filter applies to all but IMAP accounts.\n"); } else if (bApplyOnInbound) { result += QStringLiteral("This filter applies to the following accounts:"); if (mAccounts.isEmpty()) { result += QStringLiteral(" None"); } else { for (QStringList::ConstIterator it2 = mAccounts.begin(), it2End = mAccounts.end(); it2 != it2End; ++it2) { if (Akonadi::AgentManager::self()->instance(*it2).isValid()) { result += QLatin1Char(' ') + Akonadi::AgentManager::self()->instance(*it2).name(); } } } result += QLatin1Char('\n'); } if (bStopProcessingHere) { result += QStringLiteral("If it matches, processing stops at this filter.\n"); } return result; } QDataStream &MailCommon::operator<<(QDataStream &stream, const MailCommon::MailFilter &filter) { stream << filter.mIdentifier; stream << filter.mPattern.serialize(); stream << filter.mActions.count(); QListIterator it(filter.mActions); while (it.hasNext()) { const FilterAction *action = it.next(); stream << action->name(); stream << action->argsAsString(); } stream << filter.mAccounts; stream << filter.mIcon; stream << filter.mToolbarName; stream << filter.mShortcut; stream << filter.bApplyOnInbound; stream << filter.bApplyBeforeOutbound; stream << filter.bApplyOnOutbound; stream << filter.bApplyOnExplicit; stream << filter.bApplyOnAllFolders; stream << filter.bStopProcessingHere; stream << filter.bConfigureShortcut; stream << filter.bConfigureToolbar; stream << filter.bAutoNaming; stream << filter.mApplicability; stream << filter.bEnabled; return stream; } QDataStream &MailCommon::operator>>(QDataStream &stream, MailCommon::MailFilter &filter) { QByteArray pattern; int numberOfActions; QKeySequence shortcut; bool bApplyOnInbound; bool bApplyBeforeOutbound; bool bApplyOnOutbound; bool bApplyOnExplicit; bool bApplyOnAllFolders; bool bStopProcessingHere; bool bConfigureShortcut; bool bConfigureToolbar; bool bAutoNaming; int applicability; bool bEnabled; stream >> filter.mIdentifier; stream >> pattern; stream >> numberOfActions; qDeleteAll(filter.mActions); filter.mActions.clear(); for (int i = 0; i < numberOfActions; ++i) { QString actionName; QString actionArguments; stream >> actionName; stream >> actionArguments; FilterActionDesc *description = FilterManager::filterActionDict()->value(actionName); if (description) { FilterAction *filterAction = description->create(); if (filterAction) { filterAction->argsFromString(actionArguments); filter.mActions.append(filterAction); } } } stream >> filter.mAccounts; stream >> filter.mIcon; stream >> filter.mToolbarName; stream >> shortcut; stream >> bApplyOnInbound; stream >> bApplyBeforeOutbound; stream >> bApplyOnOutbound; stream >> bApplyOnExplicit; stream >> bApplyOnAllFolders; stream >> bStopProcessingHere; stream >> bConfigureShortcut; stream >> bConfigureToolbar; stream >> bAutoNaming; stream >> applicability; stream >> bEnabled; filter.mPattern.deserialize(pattern); filter.mShortcut = shortcut; filter.bApplyOnInbound = bApplyOnInbound; filter.bApplyBeforeOutbound = bApplyBeforeOutbound; filter.bApplyOnOutbound = bApplyOnOutbound; filter.bApplyOnExplicit = bApplyOnExplicit; filter.bApplyOnAllFolders = bApplyOnAllFolders; filter.bStopProcessingHere = bStopProcessingHere; filter.bConfigureShortcut = bConfigureShortcut; filter.bConfigureToolbar = bConfigureToolbar; filter.bAutoNaming = bAutoNaming; filter.bEnabled = bEnabled; filter.mApplicability = static_cast(applicability); return stream; } bool MailFilter::isEnabled() const { return bEnabled; } void MailFilter::setEnabled(bool enabled) { bEnabled = enabled; } diff --git a/src/job/folderjob.cpp b/src/job/folderjob.cpp index 741489f..c5c7c47 100644 --- a/src/job/folderjob.cpp +++ b/src/job/folderjob.cpp @@ -1,81 +1,82 @@ /* * * Copyright (c) 2003 Zack Rusin * * 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 * * 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 "folderjob.h" #include //krazy:exclude=camelcase as there is no such using namespace MailCommon; //---------------------------------------------------------------------------- -FolderJob::FolderJob() - : mErrorCode(0) +FolderJob::FolderJob(QObject *parent) + : QObject(parent) + , mErrorCode(0) , mStarted(false) , mCancellable(false) { } //---------------------------------------------------------------------------- FolderJob::~FolderJob() { Q_EMIT result(this); Q_EMIT finished(); } //---------------------------------------------------------------------------- void FolderJob::start() { if (!mStarted) { mStarted = true; execute(); } } //---------------------------------------------------------------------------- void FolderJob::kill() { mErrorCode = KIO::ERR_USER_CANCELED; delete this; } int FolderJob::error() const { return mErrorCode; } bool FolderJob::isCancellable() const { return mCancellable; } void FolderJob::setCancellable(bool b) { mCancellable = b; } diff --git a/src/job/folderjob.h b/src/job/folderjob.h index ed49377..ad9f28a 100644 --- a/src/job/folderjob.h +++ b/src/job/folderjob.h @@ -1,107 +1,107 @@ /* * * Copyright (c) 2003 Zack Rusin * * 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 * * 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. */ #ifndef MAILCOMMON_FOLDERJOB_H #define MAILCOMMON_FOLDERJOB_H #include #include "mailcommon_export.h" namespace MailCommon { class MAILCOMMON_EXPORT FolderJob : public QObject { Q_OBJECT public: - FolderJob(); + explicit FolderJob(QObject *parent = nullptr); virtual ~FolderJob(); /** * Start the job */ void start(); /** * Interrupt the job. Note that the finished() and result() signal * will be emitted, unless you called setPassiveDestructor(true) before. * This kills the job, don't use it afterwards. */ virtual void kill(); /** * @return the error code of the job. This must only be called from * the slot connected to the finished() signal. */ Q_REQUIRED_RESULT int error() const; /** * @return true if this job can be canceled, e.g. to exit the application */ Q_REQUIRED_RESULT bool isCancellable() const; /** * Call this to change the "cancellable" property of this job. * By default, tListMessages, tGetMessage, tGetFolder and tCheckUidValidity * are cancellable, the others are not. But when copying, a non-cancellable * tGetMessage is needed. */ void setCancellable(bool b); Q_SIGNALS: /** * Emitted when the job finishes all processing. */ void finished(); /** * Emitted when the job finishes all processing. * More convenient signal than finished(), since it provides a pointer to the job. * This signal is emitted by the FolderJob destructor => do NOT downcast * the job to a subclass! */ void result(FolderJob *job); protected: /** * Has to be reimplemented. It's called by the start() method. Should * start the processing of the specified job function. */ virtual void execute() = 0; Akonadi::Collection mSrcFolder; int mErrorCode; bool mStarted; bool mCancellable; }; } #endif diff --git a/src/search/searchpatternedit.cpp b/src/search/searchpatternedit.cpp index 3691cba..1132ae1 100644 --- a/src/search/searchpatternedit.cpp +++ b/src/search/searchpatternedit.cpp @@ -1,742 +1,742 @@ /* Author: 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. */ #include "searchpatternedit.h" #include "widgethandler/rulewidgethandlermanager.h" using MailCommon::RuleWidgetHandlerManager; #include "mailcommon_debug.h" #include #include #include #include #include #include #include #include #include #include // Definition of special rule field strings // Note: Also see SearchRule::matches() and ruleFieldToEnglish() if // you change the following i18n-ized strings! // Note: The index of the values in the following array has to correspond to // the value of the entries in the enum in SearchRuleWidget. #undef I18N_NOOP #define I18N_NOOP(t) 0, t #undef I18N_NOOP2 #define I18N_NOOP2(c, t) c, t using namespace MailCommon; static const struct { const char *internalName; const char *context; const char *displayName; QString getLocalizedDisplayName() const { return i18nc(context, displayName); } } SpecialRuleFields[] = { { "", I18N_NOOP("Complete Message") }, { "", I18N_NOOP("Body of Message") }, { "", I18N_NOOP("Anywhere in Headers") }, { "", I18N_NOOP("All Recipients") }, { "", I18N_NOOP("Size in Bytes") }, { "", I18N_NOOP("Age in Days") }, { "", I18N_NOOP("Message Status") }, { "", I18N_NOOP("Message Tag") }, { "Subject", I18N_NOOP2("Subject of an email.", "Subject") }, { "From", I18N_NOOP("From") }, { "To", I18N_NOOP2("Receiver of an email.", "To") }, { "CC", I18N_NOOP("CC") }, { "Reply-To", I18N_NOOP("Reply To") }, { "Organization", I18N_NOOP("Organization") }, { "", I18N_NOOP("Date") }, { "", I18N_NOOP("Encryption") } }; static const int SpecialRuleFieldsCount = sizeof(SpecialRuleFields) / sizeof(*SpecialRuleFields); //============================================================================= // // class SearchRuleWidget // //============================================================================= SearchRuleWidget::SearchRuleWidget(QWidget *parent, SearchRule::Ptr aRule, SearchPatternEdit::SearchPatternEditOptions options, SearchPatternEdit::SearchModeType modeType) : QWidget(parent) , mRuleField(nullptr) , mFunctionStack(nullptr) , mValueStack(nullptr) { initFieldList(options); initWidget(modeType); if (aRule) { setRule(aRule); } else { reset(); } } void SearchRuleWidget::setPatternEditOptions(SearchPatternEdit::SearchPatternEditOptions options) { SearchRule::Ptr srule = rule(); QByteArray currentText = srule->field(); initFieldList(options); mRuleField->clear(); mRuleField->addItems(mFilterFieldList); KCompletion *comp = mRuleField->completionObject(); comp->clear(); comp->insertItems(mFilterFieldList); mRuleField->setMaxCount(mRuleField->count()); mRuleField->adjustSize(); const bool headersOnly = (options & MailCommon::SearchPatternEdit::HeadersOnly); const bool notShowSize = (options & MailCommon::SearchPatternEdit::NotShowSize); const bool notShowDate = (options & MailCommon::SearchPatternEdit::NotShowDate); const bool notShowAbsoluteDates = (options & MailCommon::SearchPatternEdit::NotShowAbsoluteDate); const bool notShowTags = (options & MailCommon::SearchPatternEdit::NotShowTags); if (headersOnly && (currentText != "") && (currentText != "")) { mRuleField->setItemText(0, QString::fromLatin1(currentText)); } else { mRuleField->setItemText(0, QString()); } if (notShowSize && (currentText != "")) { mRuleField->setItemText(0, QString::fromLatin1(currentText)); } else { mRuleField->setItemText(0, QString()); } if (notShowDate && (currentText != "")) { mRuleField->setItemText(0, QString::fromLatin1(currentText)); } else { mRuleField->setItemText(0, QString()); } if (notShowAbsoluteDates && (currentText != "")) { mRuleField->setItemText(0, QString::fromLatin1(currentText)); } else { mRuleField->setItemText(0, QString()); } if (notShowTags && (currentText != "")) { mRuleField->setItemText(0, QString::fromLatin1(currentText)); } else { mRuleField->setItemText(0, QString()); } } void SearchRuleWidget::initWidget(SearchPatternEdit::SearchModeType modeType) { QHBoxLayout *hlay = new QHBoxLayout(this); hlay->setMargin(0); // initialize the header field combo box mRuleField = new PimCommon::MinimumComboBox(this); mRuleField->setObjectName(QStringLiteral("mRuleField")); mRuleField->setEditable(true); KLineEdit *edit = new KLineEdit; edit->setPlaceholderText(i18n("Choose or type your own criteria")); mRuleField->setToolTip(i18n("Choose or type your own criteria")); edit->setClearButtonEnabled(true); mRuleField->setLineEdit(edit); mRuleField->setTrapReturnKey(true); mRuleField->addItems(mFilterFieldList); KCompletion *comp = mRuleField->completionObject(); comp->setIgnoreCase(true); comp->insertItems(mFilterFieldList); comp->setCompletionMode(KCompletion::CompletionPopupAuto); // don't show sliders when popping up this menu mRuleField->setMaxCount(mRuleField->count()); mRuleField->adjustSize(); hlay->addWidget(mRuleField); // initialize the function/value widget stack mFunctionStack = new QStackedWidget(this); //Don't expand the widget in vertical direction mFunctionStack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); hlay->addWidget(mFunctionStack); mValueStack = new QStackedWidget(this); hlay->addWidget(mValueStack); hlay->setStretchFactor(mValueStack, 10); mAdd = new QPushButton(this); mAdd->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); mAdd->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); hlay->addWidget(mAdd); mRemove = new QPushButton(this); mRemove->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); mRemove->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); hlay->addWidget(mRemove); RuleWidgetHandlerManager::instance()->setIsAkonadiSearch(modeType == SearchPatternEdit::BalooMode); RuleWidgetHandlerManager::instance()->createWidgets(mFunctionStack, mValueStack, this); // redirect focus to the header field combo box setFocusProxy(mRuleField); connect(mRuleField, QOverload::of(&KComboBox::activated), this, &SearchRuleWidget::slotRuleFieldChanged); connect(mRuleField, &QComboBox::editTextChanged, this, &SearchRuleWidget::slotRuleFieldChanged); connect(mRuleField, &QComboBox::editTextChanged, this, &SearchRuleWidget::fieldChanged); connect(mAdd, &QAbstractButton::clicked, this, &SearchRuleWidget::slotAddWidget); connect(mRemove, &QAbstractButton::clicked, this, &SearchRuleWidget::slotRemoveWidget); } void SearchRuleWidget::updateAddRemoveButton(bool addButtonEnabled, bool removeButtonEnabled) { mAdd->setEnabled(addButtonEnabled); mRemove->setEnabled(removeButtonEnabled); } void SearchRuleWidget::slotAddWidget() { Q_EMIT addWidget(this); } void SearchRuleWidget::slotRemoveWidget() { Q_EMIT removeWidget(this); } void SearchRuleWidget::setRule(SearchRule::Ptr aRule) { Q_ASSERT(aRule); qCDebug(MAILCOMMON_LOG) << "(" << aRule->asString() << ")"; //--------------set the field int i = indexOfRuleField(aRule->field()); mRuleField->blockSignals(true); if (i < 0) { // not found -> user defined field mRuleField->setItemText(0, QString::fromLatin1(aRule->field())); i = 0; } else { // found in the list of predefined fields mRuleField->setItemText(0, QString()); } mRuleField->setCurrentIndex(i); mRuleField->blockSignals(false); RuleWidgetHandlerManager::instance()->setRule(mFunctionStack, mValueStack, aRule); } SearchRule::Ptr SearchRuleWidget::rule() const { const QByteArray ruleField = ruleFieldToEnglish(mRuleField->currentText()); const SearchRule::Function function = RuleWidgetHandlerManager::instance()->function(ruleField, mFunctionStack); const QString value = RuleWidgetHandlerManager::instance()->value(ruleField, mFunctionStack, mValueStack); return SearchRule::createInstance(ruleField, function, value); } void SearchRuleWidget::reset() { mRuleField->blockSignals(true); mRuleField->setItemText(0, QString()); mRuleField->setCurrentIndex(0); mRuleField->blockSignals(false); RuleWidgetHandlerManager::instance()->reset(mFunctionStack, mValueStack); } void SearchRuleWidget::slotFunctionChanged() { const QByteArray ruleField = ruleFieldToEnglish(mRuleField->currentText()); RuleWidgetHandlerManager::instance()->update(ruleField, mFunctionStack, mValueStack); const QString prettyValue = RuleWidgetHandlerManager::instance()->prettyValue(ruleField, mFunctionStack, mValueStack); Q_EMIT contentsChanged(prettyValue); } void SearchRuleWidget::slotValueChanged() { const QByteArray ruleField = ruleFieldToEnglish(mRuleField->currentText()); const QString prettyValue = RuleWidgetHandlerManager::instance()->prettyValue(ruleField, mFunctionStack, mValueStack); Q_EMIT contentsChanged(prettyValue); } void SearchRuleWidget::slotReturnPressed() { Q_EMIT returnPressed(); } QByteArray SearchRuleWidget::ruleFieldToEnglish(const QString &i18nVal) { for (int i = 0; i < SpecialRuleFieldsCount; ++i) { if (i18nVal == SpecialRuleFields[i].getLocalizedDisplayName()) { return SpecialRuleFields[i].internalName; } } return i18nVal.toLatin1(); } int SearchRuleWidget::ruleFieldToId(const QString &i18nVal) { for (int i = 0; i < SpecialRuleFieldsCount; ++i) { if (i18nVal == SpecialRuleFields[i].getLocalizedDisplayName()) { return i; } } return -1; // no pseudo header } static QString displayNameFromInternalName(const QString &internal) { for (int i = 0; i < SpecialRuleFieldsCount; ++i) { if (internal == QLatin1String(SpecialRuleFields[i].internalName)) { return SpecialRuleFields[i].getLocalizedDisplayName(); } } return QLatin1String(internal.toLatin1()); } int SearchRuleWidget::indexOfRuleField(const QByteArray &aName) const { if (aName.isEmpty()) { return -1; } const QString i18n_aName = displayNameFromInternalName(QLatin1String(aName)); const int nbRuleField = mRuleField->count(); for (int i = 1; i < nbRuleField; ++i) { if (mRuleField->itemText(i) == i18n_aName) { return i; } } return -1; } void SearchRuleWidget::initFieldList(SearchPatternEdit::SearchPatternEditOptions options) { const bool headersOnly = (options & MailCommon::SearchPatternEdit::HeadersOnly); const bool notShowAbsoluteDates = (options & MailCommon::SearchPatternEdit::NotShowAbsoluteDate); const bool notShowSize = (options & MailCommon::SearchPatternEdit::NotShowSize); const bool notShowDate = (options & MailCommon::SearchPatternEdit::NotShowDate); const bool notShowTags = (options & MailCommon::SearchPatternEdit::NotShowTags); mFilterFieldList.clear(); mFilterFieldList.append(QString()); // empty entry for user input if (!headersOnly) { mFilterFieldList.append(SpecialRuleFields[Message].getLocalizedDisplayName()); mFilterFieldList.append(SpecialRuleFields[Body].getLocalizedDisplayName()); } mFilterFieldList.append(SpecialRuleFields[AnyHeader].getLocalizedDisplayName()); mFilterFieldList.append(SpecialRuleFields[Recipients].getLocalizedDisplayName()); if (!notShowSize) { mFilterFieldList.append(SpecialRuleFields[Size].getLocalizedDisplayName()); } if (!notShowAbsoluteDates) { mFilterFieldList.append(SpecialRuleFields[AgeInDays].getLocalizedDisplayName()); } mFilterFieldList.append(SpecialRuleFields[Subject].getLocalizedDisplayName()); mFilterFieldList.append(SpecialRuleFields[From].getLocalizedDisplayName()); mFilterFieldList.append(SpecialRuleFields[To].getLocalizedDisplayName()); mFilterFieldList.append(SpecialRuleFields[CC].getLocalizedDisplayName()); mFilterFieldList.append(SpecialRuleFields[Status].getLocalizedDisplayName()); if (!notShowTags) { mFilterFieldList.append(SpecialRuleFields[Tag].getLocalizedDisplayName()); } mFilterFieldList.append(i18n(SpecialRuleFields[ReplyTo].displayName)); mFilterFieldList.append(i18n(SpecialRuleFields[Organization].displayName)); if (!notShowDate) { mFilterFieldList.append(i18n(SpecialRuleFields[Date].displayName)); } mFilterFieldList.append(i18n(SpecialRuleFields[Encryption].displayName)); // these others only represent message headers and you can add to // them as you like - mFilterFieldList.append(QLatin1String("List-Id")); - mFilterFieldList.append(QLatin1String("Resent-From")); - mFilterFieldList.append(QLatin1String("X-Loop")); - mFilterFieldList.append(QLatin1String("X-Mailing-List")); - mFilterFieldList.append(QLatin1String("X-Spam-Flag")); - mFilterFieldList.append(QLatin1String("X-Spam-Status")); + mFilterFieldList.append(QStringLiteral("List-Id")); + mFilterFieldList.append(QStringLiteral("Resent-From")); + mFilterFieldList.append(QStringLiteral("X-Loop")); + mFilterFieldList.append(QStringLiteral("X-Mailing-List")); + mFilterFieldList.append(QStringLiteral("X-Spam-Flag")); + mFilterFieldList.append(QStringLiteral("X-Spam-Status")); } void SearchRuleWidget::slotRuleFieldChanged(const QString &field) { RuleWidgetHandlerManager::instance()->update( ruleFieldToEnglish(field), mFunctionStack, mValueStack); } //============================================================================= // // class KMFilterActionWidgetLister (the filter action editor) // //============================================================================= SearchRuleWidgetLister::SearchRuleWidgetLister(QWidget *parent, SearchPatternEdit::SearchPatternEditOptions options, SearchPatternEdit::SearchModeType modeType) : KWidgetLister(false, 1, SearchPattern::filterRulesMaximumSize(), parent) { mRuleList = nullptr; mTypeMode = modeType; mOptions = options; } SearchRuleWidgetLister::~SearchRuleWidgetLister() { } void SearchRuleWidgetLister::setPatternEditOptions(SearchPatternEdit::SearchPatternEditOptions options) { mOptions = options; foreach (QWidget *w, widgets()) { qobject_cast(w)->setPatternEditOptions(options); } } void SearchRuleWidgetLister::setRuleList(QList *aList) { Q_ASSERT(aList); if (mRuleList && mRuleList != aList) { regenerateRuleListFromWidgets(); } mRuleList = aList; if (!widgets().isEmpty()) { // move this below next 'if'? widgets().constFirst()->blockSignals(true); } if (aList->isEmpty()) { slotClear(); widgets().constFirst()->blockSignals(false); return; } int superfluousItems = (int)mRuleList->count() - widgetsMaximum(); if (superfluousItems > 0) { qCDebug(MAILCOMMON_LOG) << "Clipping rule list to" << widgetsMaximum() << "items!"; for (; superfluousItems; superfluousItems--) { mRuleList->removeLast(); } } // set the right number of widgets setNumberOfShownWidgetsTo(qMax((int)mRuleList->count(), widgetsMinimum())); // load the actions into the widgets QList widgetList = widgets(); QList::const_iterator rIt; QList::const_iterator rItEnd(mRuleList->constEnd()); QList::const_iterator wIt = widgetList.constBegin(); QList::const_iterator wItEnd = widgetList.constEnd(); for (rIt = mRuleList->constBegin(); rIt != rItEnd && wIt != wItEnd; ++rIt, ++wIt) { qobject_cast(*wIt)->setRule((*rIt)); } for (; wIt != wItEnd; ++wIt) { qobject_cast(*wIt)->reset(); } Q_ASSERT(!widgets().isEmpty()); widgets().constFirst()->blockSignals(false); updateAddRemoveButton(); } void SearchRuleWidgetLister::slotAddWidget(QWidget *w) { addWidgetAfterThisWidget(w); updateAddRemoveButton(); } void SearchRuleWidgetLister::slotRemoveWidget(QWidget *w) { removeWidget(w); updateAddRemoveButton(); } void SearchRuleWidgetLister::reconnectWidget(SearchRuleWidget *w) { connect(w, &SearchRuleWidget::addWidget, this, &SearchRuleWidgetLister::slotAddWidget, Qt::UniqueConnection); connect(w, &SearchRuleWidget::removeWidget, this, &SearchRuleWidgetLister::slotRemoveWidget, Qt::UniqueConnection); } void SearchRuleWidgetLister::updateAddRemoveButton() { QList widgetList = widgets(); const int numberOfWidget(widgetList.count()); bool addButtonEnabled = false; bool removeButtonEnabled = false; if (numberOfWidget <= widgetsMinimum()) { addButtonEnabled = true; removeButtonEnabled = false; } else if (numberOfWidget >= widgetsMaximum()) { addButtonEnabled = false; removeButtonEnabled = true; } else { addButtonEnabled = true; removeButtonEnabled = true; } QList::ConstIterator wIt = widgetList.constBegin(); QList::ConstIterator wEnd = widgetList.constEnd(); for (; wIt != wEnd; ++wIt) { SearchRuleWidget *w = qobject_cast(*wIt); w->updateAddRemoveButton(addButtonEnabled, removeButtonEnabled); } } void SearchRuleWidgetLister::reset() { if (mRuleList) { regenerateRuleListFromWidgets(); } mRuleList = nullptr; slotClear(); updateAddRemoveButton(); } QWidget *SearchRuleWidgetLister::createWidget(QWidget *parent) { SearchRuleWidget *w = new SearchRuleWidget(parent, SearchRule::Ptr(), mOptions, mTypeMode); reconnectWidget(w); return w; } void SearchRuleWidgetLister::clearWidget(QWidget *aWidget) { if (aWidget) { SearchRuleWidget *w = static_cast(aWidget); w->reset(); reconnectWidget(w); updateAddRemoveButton(); } } void SearchRuleWidgetLister::regenerateRuleListFromWidgets() { if (!mRuleList) { return; } mRuleList->clear(); foreach (const QWidget *w, widgets()) { SearchRule::Ptr r = qobject_cast(w)->rule(); if (r && !r->isEmpty()) { mRuleList->append(r); } } updateAddRemoveButton(); } //============================================================================= // // class SearchPatternEdit // //============================================================================= SearchPatternEdit::SearchPatternEdit(QWidget *parent, SearchPatternEditOptions options, SearchModeType modeType) : QWidget(parent) , mAllMessageRBtn(nullptr) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setObjectName(QStringLiteral("SearchPatternEdit")); initLayout(options, modeType); } SearchPatternEdit::~SearchPatternEdit() { } void SearchPatternEdit::updateSearchPattern() { mRuleLister->regenerateRuleListFromWidgets(); } void SearchPatternEdit::setPatternEditOptions(SearchPatternEdit::SearchPatternEditOptions options) { mRuleLister->setPatternEditOptions(options); } void SearchPatternEdit::initLayout(SearchPatternEditOptions options, SearchModeType modeType) { QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(0); const bool matchAllMessages = (options & MailCommon::SearchPatternEdit::MatchAllMessages); //------------the radio buttons mAllRBtn = new QRadioButton(i18n("Match a&ll of the following"), this); mAnyRBtn = new QRadioButton(i18n("Match an&y of the following"), this); if (matchAllMessages) { mAllMessageRBtn = new QRadioButton(i18n("Match all messages"), this); } mAllRBtn->setObjectName(QStringLiteral("mAllRBtn")); mAllRBtn->setChecked(true); mAnyRBtn->setObjectName(QStringLiteral("mAnyRBtn")); mAnyRBtn->setChecked(false); if (matchAllMessages) { mAllMessageRBtn->setObjectName(QStringLiteral("mAllMessageRBtn")); mAllMessageRBtn->setChecked(false); } layout->addWidget(mAllRBtn); layout->addWidget(mAnyRBtn); if (matchAllMessages) { layout->addWidget(mAllMessageRBtn); } QButtonGroup *bg = new QButtonGroup(this); bg->addButton(mAllRBtn); bg->addButton(mAnyRBtn); if (matchAllMessages) { bg->addButton(mAllMessageRBtn); } //------------connect a few signals connect(bg, QOverload::of(&QButtonGroup::buttonClicked), this, &SearchPatternEdit::slotRadioClicked); //------------the list of SearchRuleWidget's mRuleLister = new SearchRuleWidgetLister( this, options, modeType); mRuleLister->slotClear(); if (!mRuleLister->widgets().isEmpty()) { const int numberOfWidget(mRuleLister->widgets().count()); for (int i = 0; i < numberOfWidget; ++i) { SearchRuleWidget *srw = static_cast(mRuleLister->widgets().at(i)); connect(srw, &SearchRuleWidget::fieldChanged, this, &SearchPatternEdit::slotAutoNameHack); connect(srw, &SearchRuleWidget::contentsChanged, this, &SearchPatternEdit::slotAutoNameHack); connect(srw, &SearchRuleWidget::returnPressed, this, &SearchPatternEdit::returnPressed); } } else { qCDebug(MAILCOMMON_LOG) << "No first SearchRuleWidget, though slotClear() has been called!"; } connect(mRuleLister, QOverload::of(&SearchRuleWidgetLister::widgetAdded), this, &SearchPatternEdit::slotRuleAdded); connect(mRuleLister, QOverload<>::of(&SearchRuleWidgetLister::widgetRemoved), this, &SearchPatternEdit::patternChanged); connect(mRuleLister, &KPIM::KWidgetLister::clearWidgets, this, &SearchPatternEdit::patternChanged); layout->addWidget(mRuleLister); } void SearchPatternEdit::setSearchPattern(SearchPattern *aPattern) { Q_ASSERT(aPattern); mRuleLister->setRuleList(aPattern); mPattern = aPattern; blockSignals(true); if (mPattern->op() == SearchPattern::OpOr) { mAnyRBtn->setChecked(true); } else if (mPattern->op() == SearchPattern::OpAnd) { mAllRBtn->setChecked(true); } else if (mAllMessageRBtn && (mPattern->op() == SearchPattern::OpAll)) { mAllMessageRBtn->setChecked(true); } mRuleLister->setEnabled(mPattern->op() != SearchPattern::OpAll); blockSignals(false); setEnabled(true); Q_EMIT patternChanged(); } void SearchPatternEdit::reset() { mRuleLister->reset(); blockSignals(true); mAllRBtn->setChecked(true); blockSignals(false); setEnabled(false); Q_EMIT patternChanged(); } void SearchPatternEdit::slotRadioClicked(QAbstractButton *aRBtn) { if (mPattern) { if (aRBtn == mAllRBtn) { mPattern->setOp(SearchPattern::OpAnd); } else if (aRBtn == mAnyRBtn) { mPattern->setOp(SearchPattern::OpOr); } else if (aRBtn == mAllMessageRBtn) { mPattern->setOp(SearchPattern::OpAll); } mRuleLister->setEnabled(mPattern->op() != SearchPattern::OpAll); Q_EMIT patternChanged(); } } void SearchPatternEdit::slotAutoNameHack() { mRuleLister->regenerateRuleListFromWidgets(); Q_EMIT maybeNameChanged(); Q_EMIT patternChanged(); } void SearchPatternEdit::slotRuleAdded(QWidget *newRuleWidget) { SearchRuleWidget *srw = static_cast(newRuleWidget); connect(srw, &SearchRuleWidget::fieldChanged, this, &SearchPatternEdit::slotAutoNameHack); connect(srw, &SearchRuleWidget::contentsChanged, this, &SearchPatternEdit::slotAutoNameHack); connect(srw, &SearchRuleWidget::returnPressed, this, &SearchPatternEdit::returnPressed); Q_EMIT patternChanged(); } diff --git a/src/search/searchrule/searchrule.cpp b/src/search/searchrule/searchrule.cpp index 886008d..67b0614 100644 --- a/src/search/searchrule/searchrule.cpp +++ b/src/search/searchrule/searchrule.cpp @@ -1,594 +1,594 @@ /* Copyright (c) 2015-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; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "searchrule.h" #include "searchrule/searchrulenumerical.h" #include "searchrule/searchruledate.h" #include "searchrule/searchrulestring.h" #include "searchrule/searchrulestatus.h" #include "searchrule/searchruleencryption.h" #include "mailcommon_debug.h" #include #include #include #include #include using namespace MailCommon; static const char *const funcConfigNames[] = { "contains", "contains-not", "equals", "not-equal", "regexp", "not-regexp", "greater", "less-or-equal", "less", "greater-or-equal", "is-in-addressbook", "is-not-in-addressbook", "is-in-category", "is-not-in-category", "has-attachment", "has-no-attachment", "start-with", "not-start-with", "end-with", "not-end-with" }; static const int numFuncConfigNames = sizeof funcConfigNames / sizeof *funcConfigNames; //================================================== // // class SearchRule (was: KMFilterRule) // //================================================== SearchRule::SearchRule(const QByteArray &field, Function func, const QString &contents) : mField(field) , mFunction(func) , mContents(contents) { } SearchRule::SearchRule(const SearchRule &other) : mField(other.mField) , mFunction(other.mFunction) , mContents(other.mContents) { } const SearchRule &SearchRule::operator=(const SearchRule &other) { if (this == &other) { return *this; } mField = other.mField; mFunction = other.mFunction; mContents = other.mContents; return *this; } SearchRule::Ptr SearchRule::createInstance(const QByteArray &field, Function func, const QString &contents) { SearchRule::Ptr ret; if (field == "") { ret = SearchRule::Ptr(new SearchRuleStatus(field, func, contents)); } else if (field == "" || field == "") { ret = SearchRule::Ptr(new SearchRuleNumerical(field, func, contents)); } else if (field == "") { ret = SearchRule::Ptr(new SearchRuleDate(field, func, contents)); } else if (field == "") { ret = SearchRule::Ptr(new SearchRuleEncryption(field, func, contents)); } else { ret = SearchRule::Ptr(new SearchRuleString(field, func, contents)); } return ret; } SearchRule::Ptr SearchRule::createInstance(const QByteArray &field, const char *func, const QString &contents) { return createInstance(field, configValueToFunc(func), contents); } SearchRule::Ptr SearchRule::createInstance(const SearchRule &other) { return createInstance(other.field(), other.function(), other.contents()); } SearchRule::Ptr SearchRule::createInstanceFromConfig(const KConfigGroup &config, int aIdx) { const char cIdx = char(int('A') + aIdx); static const QString field = QStringLiteral("field"); static const QString func = QStringLiteral("func"); static const QString contents = QStringLiteral("contents"); const QByteArray &field2 = config.readEntry(field + cIdx, QString()).toLatin1(); Function func2 = configValueToFunc(config.readEntry(func + cIdx, QString()).toLatin1().constData()); const QString &contents2 = config.readEntry(contents + cIdx, QString()); if (field2 == "") { // backwards compat return SearchRule::createInstance("", func2, contents2); } else { return SearchRule::createInstance(field2, func2, contents2); } } SearchRule::Ptr SearchRule::createInstance(QDataStream &s) { QByteArray field; s >> field; QString function; s >> function; Function func = configValueToFunc(function.toUtf8().constData()); QString contents; s >> contents; return createInstance(field, func, contents); } SearchRule::~SearchRule() { } SearchRule::Function SearchRule::configValueToFunc(const char *str) { if (!str) { return FuncNone; } for (int i = 0; i < numFuncConfigNames; ++i) { if (qstricmp(funcConfigNames[i], str) == 0) { return (Function)i; } } return FuncNone; } QString SearchRule::functionToString(Function function) { if (function != FuncNone) { return funcConfigNames[int(function)]; } else { return QStringLiteral("invalid"); } } void SearchRule::writeConfig(KConfigGroup &config, int aIdx) const { const char cIdx = char('A' + aIdx); static const QString field = QStringLiteral("field"); static const QString func = QStringLiteral("func"); static const QString contents = QStringLiteral("contents"); config.writeEntry(field + cIdx, /*QString*/ (mField)); config.writeEntry(func + cIdx, functionToString(mFunction)); config.writeEntry(contents + cIdx, mContents); } QString SearchRule::conditionToString(Function function) { QString str; switch (function) { case FuncEquals: str = i18n("equal"); break; case FuncNotEqual: str = i18n("not equal"); break; case FuncIsGreater: str = i18n("is greater"); break; case FuncIsLessOrEqual: str = i18n("is less or equal"); break; case FuncIsLess: str = i18n("is less"); break; case FuncIsGreaterOrEqual: str = i18n("is greater or equal"); break; case FuncIsInAddressbook: str = i18n("is in addressbook"); break; case FuncIsNotInAddressbook: str = i18n("is not in addressbook"); break; case FuncIsInCategory: str = i18n("is in category"); break; case FuncIsNotInCategory: str = i18n("is in category"); break; case FuncHasAttachment: str = i18n("has an attachment"); break; case FuncHasNoAttachment: str = i18n("has not an attachment"); break; case FuncStartWith: str = i18n("start with"); break; case FuncNotStartWith: str = i18n("not start with"); break; case FuncEndWith: str = i18n("end with"); break; case FuncNotEndWith: str = i18n("not end with"); break; case FuncNone: str = i18n("none"); break; case FuncContains: str = i18n("contains"); break; case FuncContainsNot: str = i18n("not contains"); break; case FuncRegExp: str = i18n("has regexp"); break; case FuncNotRegExp: str = i18n("not regexp"); break; } return str; } void SearchRule::generateSieveScript(QStringList &requireModules, QString &code) { QString contentStr = mContents; if (mField == "") { QString comparaison; int offset = 0; switch (mFunction) { case FuncEquals: comparaison = QLatin1Char('"') + i18n("size equals not supported") + QLatin1Char('"'); break; case FuncNotEqual: comparaison = QLatin1Char('"') + i18n("size not equals not supported") + QLatin1Char('"'); break; case FuncIsGreater: comparaison = QStringLiteral(":over"); break; case FuncIsLessOrEqual: comparaison = QStringLiteral(":under"); offset = 1; break; case FuncIsLess: comparaison = QStringLiteral(":under"); break; case FuncIsGreaterOrEqual: comparaison = QStringLiteral(":over"); offset = -1; break; case FuncIsInAddressbook: case FuncIsNotInAddressbook: case FuncIsInCategory: case FuncIsNotInCategory: case FuncHasAttachment: case FuncHasNoAttachment: case FuncStartWith: case FuncNotStartWith: case FuncEndWith: case FuncNotEndWith: case FuncNone: case FuncContains: case FuncContainsNot: case FuncRegExp: case FuncNotRegExp: code += QLatin1Char('"') + i18n("\"%1\" is not supported with condition \"%2\"", QLatin1String(mField), conditionToString(mFunction)) + QLatin1Char('"'); return; } code += QStringLiteral("size %1 %2K").arg(comparaison).arg(QString::number(mContents.toInt() + offset)); } else if (mField == "") { //TODO ? code += QLatin1Char('"') + i18n(" not implemented/supported") + QLatin1Char('"'); } else if (mField == "") { //TODO ? code += QLatin1Char('"') + i18n(" not implemented/supported") + QLatin1Char('"'); } else if (mField == "contents") { //TODO ? code += QLatin1Char('"') + i18n(" not implemented/supported") + QLatin1Char('"'); } else if (mField == "") { //TODO ? code += QLatin1Char('"') + i18n(" not implemented/supported") + QLatin1Char('"'); } else if (mField == "") { //TODO ? code += QLatin1Char('"') + i18n(" not implemented/supported") + QLatin1Char('"'); } else if (mField == "") { //TODO ? code += QLatin1Char('"') + i18n(" not implemented/supported") + QLatin1Char('"'); } else if (mField == "") { code += QLatin1Char('"') + i18n(" is not supported") + QLatin1Char('"'); } else if (mField == "") { //TODO ? code += i18n(" not implemented/supported"); } else if (mField == "") { if (!requireModules.contains(QLatin1String("body"))) { requireModules << QStringLiteral("body"); } QString comparaison; bool negative = false; switch (mFunction) { case FuncNone: break; case FuncContains: comparaison = QStringLiteral(":contains"); break; case FuncContainsNot: negative = true; comparaison = QStringLiteral(":contains"); break; case FuncEquals: comparaison = QStringLiteral(":is"); break; case FuncNotEqual: comparaison = QStringLiteral(":is"); negative = true; break; case FuncRegExp: comparaison = QStringLiteral(":regex"); if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } break; case FuncNotRegExp: if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } comparaison = QStringLiteral(":regex"); negative = true; break; case FuncStartWith: comparaison = QStringLiteral(":regex"); if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } contentStr = QLatin1Char('^') + contentStr; break; case FuncNotStartWith: comparaison = QStringLiteral(":regex"); if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } comparaison = QStringLiteral(":regex"); contentStr = QLatin1Char('^') + contentStr; negative = true; break; case FuncEndWith: comparaison = QStringLiteral(":regex"); if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } comparaison = QStringLiteral(":regex"); contentStr = contentStr + QLatin1Char('$'); break; case FuncNotEndWith: comparaison = QStringLiteral(":regex"); if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } comparaison = QStringLiteral(":regex"); contentStr = contentStr + QLatin1Char('$'); negative = true; break; case FuncIsGreater: case FuncIsLessOrEqual: case FuncIsLess: case FuncIsGreaterOrEqual: case FuncIsInAddressbook: case FuncIsNotInAddressbook: case FuncIsInCategory: case FuncIsNotInCategory: case FuncHasAttachment: case FuncHasNoAttachment: code += QLatin1Char('"') + i18n("\"%1\" is not supported with condition \"%2\"", QLatin1String(mField), conditionToString(mFunction)) + QLatin1Char('"'); return; } - code += (negative ? QStringLiteral("not ") : QString()) + QStringLiteral("body :text %1 \"%2\"").arg(comparaison).arg(contentStr); + code += (negative ? QStringLiteral("not ") : QString()) + QStringLiteral("body :text %1 \"%2\"").arg(comparaison, contentStr); } else { QString comparaison; bool negative = false; switch (mFunction) { case FuncNone: break; case FuncContains: comparaison = QStringLiteral(":contains"); break; case FuncContainsNot: negative = true; comparaison = QStringLiteral(":contains"); break; case FuncEquals: comparaison = QStringLiteral(":is"); break; case FuncNotEqual: comparaison = QStringLiteral(":is"); negative = true; break; case FuncRegExp: comparaison = QStringLiteral(":regex"); if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } break; case FuncNotRegExp: if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } comparaison = QStringLiteral(":regex"); negative = true; break; case FuncStartWith: comparaison = QStringLiteral(":regex"); if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } contentStr = QLatin1Char('^') + contentStr; break; case FuncNotStartWith: comparaison = QStringLiteral(":regex"); if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } comparaison = QStringLiteral(":regex"); contentStr = QLatin1Char('^') + contentStr; negative = true; break; case FuncEndWith: comparaison = QStringLiteral(":regex"); if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } comparaison = QStringLiteral(":regex"); contentStr = contentStr + QLatin1Char('$'); break; case FuncNotEndWith: comparaison = QStringLiteral(":regex"); if (!requireModules.contains(QLatin1String("regex"))) { requireModules << QStringLiteral("regex"); } comparaison = QStringLiteral(":regex"); contentStr = contentStr + QLatin1Char('$'); negative = true; break; case FuncIsGreater: case FuncIsLessOrEqual: case FuncIsLess: case FuncIsGreaterOrEqual: case FuncIsInAddressbook: case FuncIsNotInAddressbook: case FuncIsInCategory: case FuncIsNotInCategory: case FuncHasAttachment: case FuncHasNoAttachment: code += QLatin1Char('"') + i18n("\"%1\" is not supported with condition \"%2\"", QLatin1String(mField), conditionToString(mFunction)) + QLatin1Char('"'); return; } code += (negative ? QStringLiteral("not ") : QString()) + QStringLiteral("header %1 \"%2\" \"%3\"").arg(comparaison).arg(QLatin1String(mField)).arg(contentStr); } } void SearchRule::setFunction(Function function) { mFunction = function; } SearchRule::Function SearchRule::function() const { return mFunction; } void SearchRule::setField(const QByteArray &field) { mField = field; } QByteArray SearchRule::field() const { return mField; } void SearchRule::setContents(const QString &contents) { mContents = contents; } QString SearchRule::contents() const { return mContents; } const QString SearchRule::asString() const { QString result = QLatin1String("\"") + QString::fromLatin1(mField) + QLatin1String("\" <"); result += functionToString(mFunction); result += QStringLiteral("> \"") + mContents + QStringLiteral("\""); return result; } Akonadi::SearchTerm::Condition SearchRule::akonadiComparator() const { switch (function()) { case SearchRule::FuncContains: case SearchRule::FuncContainsNot: return Akonadi::SearchTerm::CondContains; case SearchRule::FuncEquals: case SearchRule::FuncNotEqual: return Akonadi::SearchTerm::CondEqual; case SearchRule::FuncIsGreater: return Akonadi::SearchTerm::CondGreaterThan; case SearchRule::FuncIsGreaterOrEqual: return Akonadi::SearchTerm::CondGreaterOrEqual; case SearchRule::FuncIsLess: return Akonadi::SearchTerm::CondLessThan; case SearchRule::FuncIsLessOrEqual: return Akonadi::SearchTerm::CondLessOrEqual; case SearchRule::FuncRegExp: case SearchRule::FuncNotRegExp: //TODO is this sufficient? return Akonadi::SearchTerm::CondContains; case SearchRule::FuncStartWith: case SearchRule::FuncNotStartWith: case SearchRule::FuncEndWith: case SearchRule::FuncNotEndWith: //TODO is this sufficient? return Akonadi::SearchTerm::CondContains; default: qCDebug(MAILCOMMON_LOG) << "Unhandled function type: " << function(); } return Akonadi::SearchTerm::CondEqual; } bool SearchRule::isNegated() const { bool negate = false; switch (function()) { case SearchRule::FuncContainsNot: case SearchRule::FuncNotEqual: case SearchRule::FuncNotRegExp: case SearchRule::FuncHasNoAttachment: case SearchRule::FuncIsNotInCategory: case SearchRule::FuncIsNotInAddressbook: case SearchRule::FuncNotStartWith: case SearchRule::FuncNotEndWith: negate = true; default: break; } return negate; } QDataStream &SearchRule::operator >>(QDataStream &s) const { s << mField << functionToString(mFunction) << mContents; return s; }