Mailcommon

kmfilterdialog.cpp
1/*
2 Filter Dialog
3
4 SPDX-FileCopyrightText: Marc Mutz <mutz@kde.org>
5 SPDX-FileCopyrightText: 2011-2025 Laurent Montel <montel@kde.org>
6
7 based upon work by Stefan Taferner <taferner@kde.org>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
11
12#include "kmfilterdialog.h"
13#include "kmfilterlistbox.h"
14#include "mailcommon_debug.h"
15
16#include "filteractions/filteractionwidget.h"
17#include "filterimporterpathcache.h"
18#include "filterselectiondialog.h"
19#include "kmfilteraccountlist.h"
21#include "filterconverter/filterconverttosieve.h"
22#include "filtermanager.h"
23#include "folder/folderrequester.h"
24#include "kernel/mailkernel.h"
25#include "search/searchpatternedit.h"
26#include <PimCommon/PimUtil>
27
28#include <Akonadi/ItemFetchJob>
29
30#include <KConfigGroup>
31
32#include <KIconButton>
33#include <KIconLoader>
34#include <KJob>
35#include <KKeySequenceWidget>
36#include <KListWidgetSearchLine>
37#include <KLocalizedString>
38#include <KMessageBox>
39#include <QIcon>
40#include <QPushButton>
41#include <QTabWidget>
42
43#include <QButtonGroup>
44#include <QCheckBox>
45#include <QDialogButtonBox>
46#include <QFormLayout>
47#include <QGridLayout>
48#include <QGroupBox>
49#include <QHBoxLayout>
50#include <QKeyEvent>
51#include <QLabel>
52#include <QMenu>
53#include <QPointer>
54#include <QRadioButton>
55#include <QShortcut>
56#include <QSplitter>
57#include <QTreeWidget>
58#include <QVBoxLayout>
59
60Q_DECLARE_METATYPE(MailCommon::FilterImporterExporter::FilterType)
61using namespace MailCommon;
62
63//=============================================================================
64//
65// class KMFilterDialog (the filter dialog)
66//
67//=============================================================================
68
69KMFilterDialog::KMFilterDialog(const QList<KActionCollection *> &actionCollection, QWidget *parent, bool createDummyFilter)
70 : QDialog(parent)
71{
72 setWindowTitle(i18nc("@title:window", "Filter Rules"));
73 auto mainLayout = new QVBoxLayout(this);
74
76 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
77 okButton->setDefault(true);
79 auto user1Button = new QPushButton(this);
80 buttonBox->addButton(user1Button, QDialogButtonBox::ActionRole);
81 auto user2Button = new QPushButton(this);
82 buttonBox->addButton(user2Button, QDialogButtonBox::ActionRole);
83 auto user3Button = new QPushButton(this);
84 buttonBox->addButton(user3Button, QDialogButtonBox::ActionRole);
85 connect(buttonBox, &QDialogButtonBox::accepted, this, &KMFilterDialog::accept);
87 connect(buttonBox->button(QDialogButtonBox::Help), &QAbstractButton::clicked, this, &KMFilterDialog::slotHelp);
88 setModal(false);
89 okButton->setFocus();
90 user1Button->setText(i18n("Import…"));
91 user1Button->setIcon(QIcon::fromTheme("document-import"));
92 user2Button->setText(i18n("Export…"));
93 user2Button->setIcon(QIcon::fromTheme("document-export"));
94 user3Button->setText(i18n("Convert to…"));
95 user3Button->setIcon(QIcon::fromTheme("document-save-as"));
96 auto menu = new QMenu(this);
97
98 auto act = new QAction(i18nc("@action", "KMail filters"), this);
99 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::KMailFilter));
100 menu->addAction(act);
101
102 act = new QAction(i18nc("@action", "Thunderbird filters"), this);
103 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::ThunderBirdFilter));
104 menu->addAction(act);
105
106 act = new QAction(i18nc("@action", "Evolution filters"), this);
107 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::EvolutionFilter));
108 menu->addAction(act);
109
110 act = new QAction(i18nc("@action", "Sylpheed filters"), this);
111 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::SylpheedFilter));
112 menu->addAction(act);
113
114 act = new QAction(i18nc("@action", "Procmail filters"), this);
115 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::ProcmailFilter));
116 menu->addAction(act);
117
118 act = new QAction(i18nc("@action", "Balsa filters"), this);
119 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::BalsaFilter));
120 menu->addAction(act);
121
122 act = new QAction(i18nc("@action", "Claws Mail filters"), this);
123 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::ClawsMailFilter));
124 menu->addAction(act);
125
126 act = new QAction(i18nc("@action", "Icedove Mail filters"), this);
127 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::IcedoveFilter));
128 menu->addAction(act);
129
130 connect(menu, &QMenu::triggered, this, &KMFilterDialog::slotImportFilter);
131
132 act = new QAction(i18nc("@action", "Gmail filters"), this);
133 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::GmailFilter));
134 menu->addAction(act);
135
136 user1Button->setMenu(menu);
137
138 menu = new QMenu(this);
139
140 act = new QAction(i18nc("@action", "Sieve script"), this);
141 connect(act, &QAction::triggered, this, &KMFilterDialog::slotExportAsSieveScript);
142 menu->addAction(act);
143 user3Button->setMenu(menu);
144
145 connect(user2Button, &QAbstractButton::clicked, this, &KMFilterDialog::slotExportFilters);
146 mApplyButton = buttonBox->button(QDialogButtonBox::Apply);
147 mApplyButton->setEnabled(false);
148
149 auto w = new QWidget(this);
150 mainLayout->addWidget(w);
151 mainLayout->addWidget(buttonBox);
152 auto topVLayout = new QVBoxLayout(w);
153 topVLayout->setContentsMargins({});
154 auto topLayout = new QHBoxLayout;
155 topVLayout->addLayout(topLayout);
156 topLayout->setContentsMargins({});
157
158 auto splitter = new QSplitter;
159 splitter->setChildrenCollapsible(false);
160 topLayout->addWidget(splitter);
161
162 mFilterList = new KMFilterListBox(i18n("Available Filters"), this);
163 splitter->addWidget(mFilterList);
164 auto tabWidget = new QTabWidget(this);
165 splitter->addWidget(tabWidget);
166
167 auto page1 = new QWidget(tabWidget);
168 tabWidget->addTab(page1, i18nc("General mail filter settings.", "General"));
169 auto hbl = new QHBoxLayout(page1);
170
171 auto page2 = new QWidget(tabWidget);
172 tabWidget->addTab(page2, i18nc("Advanced mail filter settings.", "Advanced"));
173 auto vbl2 = new QVBoxLayout(page2);
174
175 auto vbl = new QVBoxLayout();
176 hbl->addLayout(vbl);
177 hbl->setStretchFactor(vbl, 2);
178
179 mPatternEdit = new MailCommon::SearchPatternEdit(page1, MailCommon::SearchPatternEdit::MatchAllMessages);
180 vbl->addWidget(mPatternEdit, 0, Qt::AlignTop);
181
182 auto actionLabel = new QLabel(i18nc("@label:textbox", "Filter actions:"), page1);
183 vbl->addWidget(actionLabel);
184
185 mActionLister = new MailCommon::FilterActionWidgetLister(page1);
186 vbl->addWidget(mActionLister);
187
188 mAdvOptsGroup = new QGroupBox(QString(), page2);
189 mAdvOptsGroup->setFlat(true);
190 mAdvOptsGroup->setContentsMargins({});
191 {
192 auto gl = new QGridLayout();
193 mApplyOnIn = new QCheckBox(i18nc("@option:check", "Apply this filter to incoming messages:"), mAdvOptsGroup);
194 gl->addWidget(mApplyOnIn, 0, 0);
195
196 auto radioGroupWidget = new QWidget(page2);
197 radioGroupWidget->setContentsMargins(20, 0, 0, 0);
198 gl->addWidget(radioGroupWidget, 1, 0);
199 auto vbl3 = new QVBoxLayout(radioGroupWidget);
200
201 auto bg = new QButtonGroup(mAdvOptsGroup);
202
203 mApplyOnForAll = new QRadioButton(i18nc("@option:radio", "From all accounts"), radioGroupWidget);
204 bg->addButton(mApplyOnForAll);
205 vbl3->addWidget(mApplyOnForAll);
206
207 mApplyOnForTraditional = new QRadioButton(i18nc("@option:radio", "From all but online IMAP accounts"), radioGroupWidget);
208 bg->addButton(mApplyOnForTraditional);
209 vbl3->addWidget(mApplyOnForTraditional);
210
211 mApplyOnForChecked = new QRadioButton(i18nc("@option:radio", "From selected accounts only"), radioGroupWidget);
212 bg->addButton(mApplyOnForChecked);
213 vbl3->addWidget(mApplyOnForChecked);
214 vbl3->addStretch(100);
215
216 mAccountList = new KMFilterAccountList(mAdvOptsGroup);
217 gl->addWidget(mAccountList, 0, 1, 4, 3);
218
219 mApplyOnOut = new QCheckBox(i18nc("@option:check", "Apply this filter to &sent messages"), mAdvOptsGroup);
220 mApplyOnOut->setToolTip(
221 i18n("<p>The filter will be triggered <b>after</b> the message is sent "
222 "and it will only affect the local copy of the message.</p>"
223 "<p>If the recipient's copy also needs to be modified, "
224 "please use \"Apply this filter <b>before</b> sending messages\".</p>"));
225 gl->addWidget(mApplyOnOut, 5, 0, 1, 4);
226
227 mApplyBeforeOut = new QCheckBox(i18nc("@option:check", "Apply this filter &before sending messages"), mAdvOptsGroup);
228 mApplyBeforeOut->setToolTip(
229 i18n("<p>The filter will be triggered <b>before</b> the message is sent "
230 "and it will affect both the local copy and the sent copy of the message.</p>"
231 "<p>This is required if the recipient's copy also needs to be modified.</p>"));
232 gl->addWidget(mApplyBeforeOut, 6, 0, 1, 4);
233
234 mApplyOnCtrlJ = new QCheckBox(i18nc("@option:check", "Apply this filter on manual &filtering"), mAdvOptsGroup);
235 gl->addWidget(mApplyOnCtrlJ, 7, 0, 1, 4);
236
237 mApplyOnAllFolders = new QCheckBox(i18nc("@option:check", "Apply this filter on inbound emails in all folders"), mAdvOptsGroup);
238 mApplyOnAllFolders->setToolTip(
239 i18n("<p>The filter will be applied on inbound emails from all folders "
240 "belonging to all accounts selected above. This is useful when using local filters "
241 "with IMAP accounts where new emails may have already been moved to different folders "
242 "by server-side filters.</p>"));
243 gl->addWidget(mApplyOnAllFolders, 8, 0, 1, 4);
244
245 mStopProcessingHere = new QCheckBox(i18nc("@option:check", "If this filter &matches, stop processing here"), mAdvOptsGroup);
246 gl->addWidget(mStopProcessingHere, 9, 0, 1, 4);
247
248 mConfigureShortcut = new QCheckBox(i18nc("@option:check", "Add this filter to the Apply Filter menu"), mAdvOptsGroup);
249 gl->addWidget(mConfigureShortcut, 10, 0, 1, 2);
250
251 mInMenuWidget = new QWidget(mAdvOptsGroup);
252 mInMenuWidget->setContentsMargins(20, 0, 0, 0);
253 gl->addWidget(mInMenuWidget, 11, 0, 1, 4);
254 auto inMenuLayout = new QFormLayout(mInMenuWidget);
255
256 connect(mConfigureShortcut, &QCheckBox::toggled, this, [this](bool aChecked) {
257 if (mFilter) {
258 mFilter->setConfigureShortcut(aChecked);
259 mInMenuWidget->setEnabled(aChecked);
260
261 // Enable the apply button
262 slotDialogUpdated();
263 }
264 });
265
266 mConfigureToolbar = new QCheckBox(i18nc("@option:check", "Additionally add this filter to the toolbar"), mInMenuWidget);
267 inMenuLayout->addRow(mConfigureToolbar);
268
269 mKeySeqWidget = new KKeySequenceWidget(mInMenuWidget);
270 mKeySeqWidget->setObjectName(QLatin1StringView("FilterShortcutSelector"));
271#if KXMLGUI_VERSION >= QT_VERSION_CHECK(6, 12, 0)
272 mKeySeqWidget->setPatterns(KKeySequenceRecorder::Key);
273#else
274 mKeySeqWidget->setModifierlessAllowed(true);
275#endif
276 mKeySeqWidget->setCheckActionCollections(actionCollection);
277 inMenuLayout->addRow(i18n("Shortcut:"), mKeySeqWidget);
278
279 mFilterActionIconButton = new KIconButton(mInMenuWidget);
280 mFilterActionIconButton->setIconType(KIconLoader::NoGroup, KIconLoader::Action, false);
281 mFilterActionIconButton->setIconSize(16);
282 mFilterActionIconButton->setIcon(QIcon::fromTheme(QStringLiteral("system-run")));
283 inMenuLayout->addRow(i18n("Icon for this filter:"), mFilterActionIconButton);
284
285 mAdvOptsGroup->setLayout(gl);
286 mInMenuWidget->setEnabled(false);
287 }
288 vbl2->addWidget(mAdvOptsGroup, 0, Qt::AlignTop);
289
290 auto applySpecificFiltersLayout = new QHBoxLayout;
291 auto lab = new QLabel(i18nc("@label:textbox", "Run selected filter(s) on: "), this);
292 applySpecificFiltersLayout->addWidget(lab);
293 mFolderRequester = new MailCommon::FolderRequester(this);
294 mFolderRequester->setNotAllowToCreateNewFolder(true);
295 applySpecificFiltersLayout->addWidget(mFolderRequester);
296 connect(mFolderRequester, &FolderRequester::folderChanged, this, &KMFilterDialog::slotFolderChanged);
297 mRunNow = new QPushButton(i18nc("@action:button", "Run Now"), this);
298 mRunNow->setEnabled(false);
299 applySpecificFiltersLayout->addWidget(mRunNow);
300 connect(mRunNow, &QPushButton::clicked, this, &KMFilterDialog::slotRunFilters);
301 topVLayout->addLayout(applySpecificFiltersLayout);
302 // spacer:
303 vbl->addStretch(1);
304
305 // load the filter parts into the edit widgets
306 connect(mFilterList, &KMFilterListBox::filterSelected, this, &KMFilterDialog::slotFilterSelected);
307
308 // transfer changes from the 'Apply this filter on...'
309 // combo box to the filter
310 connect(mApplyOnIn, &QCheckBox::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
311 connect(mApplyOnForAll, &QRadioButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
312 connect(mApplyOnForTraditional, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
313 connect(mApplyOnForChecked, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
314 connect(mApplyBeforeOut, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
315 connect(mApplyOnAllFolders, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
316 connect(mApplyOnOut, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
317 connect(mApplyOnCtrlJ, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
318 connect(mAccountList, &KMFilterAccountList::itemChanged, this, &KMFilterDialog::slotApplicableAccountsChanged);
319
320 // transfer changes from the 'stop processing here'
321 // check box to the filter
322 connect(mStopProcessingHere, &QCheckBox::toggled, this, &KMFilterDialog::slotStopProcessingButtonToggled);
323
324 connect(mKeySeqWidget, &KKeySequenceWidget::keySequenceChanged, this, &KMFilterDialog::slotShortcutChanged);
325
326 connect(mConfigureToolbar, &QCheckBox::toggled, this, &KMFilterDialog::slotConfigureToolbarButtonToggled);
327
328 connect(mFilterActionIconButton, &KIconButton::iconChanged, this, &KMFilterDialog::slotFilterActionIconChanged);
329
330 // reset all widgets here
331 connect(mFilterList, &KMFilterListBox::resetWidgets, this, &KMFilterDialog::slotReset);
332
333 connect(mFilterList, &KMFilterListBox::applyWidgets, this, &KMFilterDialog::slotUpdateFilter);
334
335 // support auto-naming the filter
336 connect(mPatternEdit, &MailCommon::SearchPatternEdit::maybeNameChanged, mFilterList, &KMFilterListBox::slotUpdateFilterName);
337
338 // save filters on 'Apply' or 'OK'
339 connect(mApplyButton, &QAbstractButton::clicked, this, &KMFilterDialog::slotApply);
340
341 // save dialog size on 'OK'
342 connect(okButton, &QAbstractButton::clicked, this, &KMFilterDialog::slotSaveSize);
343
344 // destruct the dialog on close and Cancel
345 connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, this, &KMFilterDialog::slotFinished);
346
347 // disable closing when user wants to continue editing
348 connect(mFilterList, &KMFilterListBox::abortClosing, this, &KMFilterDialog::slotDisableAccept);
349
350 connect(mFilterList, &KMFilterListBox::filterCreated, this, &KMFilterDialog::slotDialogUpdated);
351 connect(mFilterList, &KMFilterListBox::filterRemoved, this, &KMFilterDialog::slotDialogUpdated);
352 connect(mFilterList, &KMFilterListBox::filterUpdated, this, &KMFilterDialog::slotDialogUpdated);
353 connect(mFilterList, &KMFilterListBox::filterOrderAltered, this, &KMFilterDialog::slotDialogUpdated);
354 connect(mPatternEdit, &MailCommon::SearchPatternEdit::patternChanged, this, &KMFilterDialog::slotDialogUpdated);
355 connect(mActionLister, qOverload<QWidget *>(&FilterActionWidgetLister::widgetAdded), this, &KMFilterDialog::slotDialogUpdated);
356 connect(mActionLister, qOverload<QWidget *>(&FilterActionWidgetLister::widgetRemoved), this, &KMFilterDialog::slotDialogUpdated);
357 connect(mActionLister, &MailCommon::FilterActionWidgetLister::filterModified, this, &KMFilterDialog::slotDialogUpdated);
358 connect(mActionLister, &MailCommon::FilterActionWidgetLister::clearWidgets, this, &KMFilterDialog::slotDialogUpdated);
359 KConfigGroup myGroup(KSharedConfig::openStateConfig(), QStringLiteral("Geometry"));
360 const QSize size = myGroup.readEntry("filterDialogSize", QSize());
361 if (size != QSize()) {
362 resize(size);
363 } else {
364 adjustSize();
365 }
366
367 // load the filter list (emits filterSelected())
368 mFilterList->loadFilterList(createDummyFilter);
369 mIgnoreFilterUpdates = false;
370}
371
372void KMFilterDialog::createFilter(const QByteArray &field, const QString &value)
373{
374 mFilterList->createFilter(field, value);
375}
376
377void KMFilterDialog::accept()
378{
379 if (mDoNotClose) {
380 mDoNotClose = false; // only abort current close attempt
381 } else {
383 slotFinished();
384 }
385}
386
387bool KMFilterDialog::event(QEvent *e)
388{
389 // Close the bar when pressing Escape.
390 // Not using a QShortcut for this because it could conflict with
391 // window-global actions (e.g. Emil Sedgh binds Esc to "close tab").
392 // With a shortcut override we can catch this before it gets to kactions.
393 const bool shortCutOverride = (e->type() == QEvent::ShortcutOverride);
394 if (shortCutOverride || e->type() == QEvent::KeyPress) {
395 auto kev = static_cast<QKeyEvent *>(e);
396 if (kev->key() == Qt::Key_Escape) {
397 e->ignore();
398 return true;
399 }
400 }
401 return QDialog::event(e);
402}
403
404void KMFilterDialog::slotApply()
405{
406 mApplyButton->setEnabled(false);
407 mFilterList->slotApplied();
408}
409
410void KMFilterDialog::slotFinished()
411{
412 deleteLater();
413}
414
415void KMFilterDialog::slotFolderChanged(const Akonadi::Collection &collection)
416{
417 mRunNow->setEnabled(collection.isValid());
418}
419
420void KMFilterDialog::slotRunFilters()
421{
422 if (!mFolderRequester->collection().isValid()) {
424 i18nc("@info", "Unable to apply this filter since there are no folders selected."),
425 i18nc("@title:window", "No folder selected."));
426 return;
427 }
428
429 if (mApplyButton->isEnabled()) {
431 i18nc("@info",
432 "Some filters were changed and not saved yet. "
433 "You must save your filters before they can be applied."),
434 i18nc("@title:window", "Filters changed."));
435 return;
436 }
438 const QStringList selectedFiltersId = mFilterList->selectedFilterId(requiredPart, mFolderRequester->collection().resource());
439 if (selectedFiltersId.isEmpty()) {
441 i18nc("@info", "Unable to apply a filter since there are no filters currently selected."),
442 i18nc("@title:window", "No filters selected."));
443 return;
444 }
445 auto job = new Akonadi::ItemFetchJob(mFolderRequester->collection(), this);
446 job->setProperty("requiredPart", QVariant::fromValue(requiredPart));
447 job->setProperty("listFilters", QVariant::fromValue(selectedFiltersId));
448
449 connect(job, &KJob::result, this, &KMFilterDialog::slotFetchItemsForFolderDone);
450
451 mRunNow->setEnabled(false); // Disable it
452}
453
454void KMFilterDialog::slotFetchItemsForFolderDone(KJob *job)
455{
457 Q_ASSERT(fjob);
458
459 QStringList filtersId;
460 if (fjob->property("listFilters").isValid()) {
461 filtersId = fjob->property("listFilters").toStringList();
462 }
463
465 if (fjob->property("requiredPart").isValid()) {
466 requiredPart = fjob->property("requiredPart").value<SearchRule::RequiredPart>();
467 }
468 Akonadi::Item::List items = fjob->items();
469 mRunNow->setEnabled(true);
470 MailCommon::FilterManager::instance()->filter(items, requiredPart, filtersId);
471}
472
473void KMFilterDialog::slotSaveSize()
474{
475 mFilterList->slotAccepted();
476 KConfigGroup myGroup(KSharedConfig::openStateConfig(), QStringLiteral("Geometry"));
477 myGroup.writeEntry("filterDialogSize", size());
478 myGroup.sync();
479}
480
481void KMFilterDialog::slotFilterSelected(MailFilter *aFilter)
482{
483 Q_ASSERT(aFilter);
484 mIgnoreFilterUpdates = true;
485 mActionLister->setActionList(aFilter->actions());
486
487 mAdvOptsGroup->setEnabled(true);
488
489 mPatternEdit->setSearchPattern(aFilter->pattern());
490 mFilter = aFilter;
491
492 qCDebug(MAILCOMMON_LOG) << "apply on inbound ==" << aFilter->applyOnInbound();
493 qCDebug(MAILCOMMON_LOG) << "apply on outbound ==" << aFilter->applyOnOutbound();
494 qCDebug(MAILCOMMON_LOG) << "apply before outbound == " << aFilter->applyBeforeOutbound();
495 qCDebug(MAILCOMMON_LOG) << "apply on explicit ==" << aFilter->applyOnExplicit();
496 qCDebug(MAILCOMMON_LOG) << "apply on all folders inboud == " << aFilter->applyOnAllFoldersInbound();
497
498 // NOTE: setting these values activates the slot that sets them in
499 // the filter! So make sure we have the correct values _before_ we
500 // set the first one:
501 const bool applyOnIn = aFilter->applyOnInbound();
502 const bool applyOnForAll = aFilter->applicability() == MailFilter::All;
503 const bool applyOnTraditional = aFilter->applicability() == MailFilter::ButImap;
504 const bool applyBeforeOut = aFilter->applyBeforeOutbound();
505 const bool applyOnOut = aFilter->applyOnOutbound();
506 const bool applyOnAllFolders = aFilter->applyOnAllFoldersInbound();
507 const bool applyOnExplicit = aFilter->applyOnExplicit();
508 const bool stopHere = aFilter->stopProcessingHere();
509 const bool configureShortcut = aFilter->configureShortcut();
510 const bool configureToolbar = aFilter->configureToolbar();
511 const QString icon = aFilter->icon();
512 const QKeySequence shortcut(aFilter->shortcut());
513
514 mApplyOnIn->setChecked(applyOnIn);
515 mApplyOnForAll->setEnabled(applyOnIn);
516 mApplyOnForTraditional->setEnabled(applyOnIn);
517 mApplyOnForChecked->setEnabled(applyOnIn);
518 mApplyOnForAll->setChecked(applyOnForAll);
519 mApplyOnAllFolders->setChecked(applyOnAllFolders);
520 mApplyOnForTraditional->setChecked(applyOnTraditional);
521 mApplyOnForChecked->setChecked(!applyOnForAll && !applyOnTraditional);
522 mAccountList->setEnabled(mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked());
523 slotUpdateAccountList();
524 mApplyBeforeOut->setChecked(applyBeforeOut);
525 mApplyOnOut->setChecked(applyOnOut);
526 mApplyOnCtrlJ->setChecked(applyOnExplicit);
527 mStopProcessingHere->setChecked(stopHere);
528 mConfigureShortcut->setChecked(configureShortcut);
529 mKeySeqWidget->setKeySequence(shortcut, KKeySequenceWidget::NoValidate);
530 mConfigureToolbar->setChecked(configureToolbar);
531 mFilterActionIconButton->setIcon(icon);
532 mIgnoreFilterUpdates = false;
533}
534
535void KMFilterDialog::slotReset()
536{
537 mFilter = nullptr;
538 mPatternEdit->reset();
539
540 mActionLister->reset();
541 mAdvOptsGroup->setEnabled(false);
542 slotUpdateAccountList();
543}
544
545void KMFilterDialog::slotUpdateFilter()
546{
547 mPatternEdit->updateSearchPattern();
548 mActionLister->updateActionList();
549}
550
551void KMFilterDialog::slotApplicabilityChanged()
552{
553 if (mFilter) {
554 mFilter->setApplyOnInbound(mApplyOnIn->isChecked());
555 mFilter->setApplyBeforeOutbound(mApplyBeforeOut->isChecked());
556 mFilter->setApplyOnOutbound(mApplyOnOut->isChecked());
557 mFilter->setApplyOnExplicit(mApplyOnCtrlJ->isChecked());
558 mFilter->setApplyOnAllFoldersInbound(mApplyOnAllFolders->isChecked());
559 if (mApplyOnForAll->isChecked()) {
560 mFilter->setApplicability(MailFilter::All);
561 mFilter->clearApplyOnAccount();
562 } else if (mApplyOnForTraditional->isChecked()) {
563 mFilter->setApplicability(MailFilter::ButImap);
564 } else if (mApplyOnForChecked->isChecked()) {
565 mFilter->setApplicability(MailFilter::Checked);
566 }
567
568 mApplyOnForAll->setEnabled(mApplyOnIn->isChecked());
569 mApplyOnForTraditional->setEnabled(mApplyOnIn->isChecked());
570 mApplyOnForChecked->setEnabled(mApplyOnIn->isChecked());
571 mAccountList->setEnabled(mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked());
572
573 // Advanced tab functionality - Update list of accounts this filter applies to
574 if (!mApplyOnForAll->isChecked()) {
575 mAccountList->applyOnAccount(mFilter);
576 }
577
578 // Enable the apply button
579 slotDialogUpdated();
580
581 qCDebug(MAILCOMMON_LOG) << "Setting filter to be applied at" << (mFilter->applyOnInbound() ? "incoming " : "")
582 << (mFilter->applyOnOutbound() ? "outgoing " : "") << (mFilter->applyBeforeOutbound() ? "before_outgoing " : "")
583 << (mFilter->applyOnAllFoldersInbound() ? "all folders inboud " : "") << (mFilter->applyOnExplicit() ? "explicit CTRL-J" : "");
584 }
585}
586
587void KMFilterDialog::slotApplicableAccountsChanged()
588{
589 // Advanced tab functionality - Update list of accounts this filter applies to
590 if (mFilter && mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked()) {
591 QTreeWidgetItemIterator it(mAccountList);
592
593 while (QTreeWidgetItem *item = *it) {
594 const QString id = item->text(2);
595 mFilter->setApplyOnAccount(id, item->checkState(0) == Qt::Checked);
596 ++it;
597 }
598
599 // Enable the apply button
600 slotDialogUpdated();
601 }
602}
603
604void KMFilterDialog::slotStopProcessingButtonToggled(bool aChecked)
605{
606 if (mFilter) {
607 mFilter->setStopProcessingHere(aChecked);
608
609 // Enable the apply button
610 slotDialogUpdated();
611 }
612}
613
614void KMFilterDialog::slotShortcutChanged(const QKeySequence &newSeq)
615{
616 if (mFilter) {
617 mKeySeqWidget->applyStealShortcut();
618 mFilter->setShortcut(newSeq);
619
620 // Enable the apply button
621 slotDialogUpdated();
622 }
623}
624
625void KMFilterDialog::slotConfigureToolbarButtonToggled(bool aChecked)
626{
627 if (mFilter) {
628 mFilter->setConfigureToolbar(aChecked);
629 // Enable the apply button
630 slotDialogUpdated();
631 }
632}
633
634void KMFilterDialog::slotFilterActionIconChanged(const QString &icon)
635{
636 if (mFilter) {
637 mFilter->setIcon(icon);
638 // Enable the apply button
639 slotDialogUpdated();
640 }
641}
642
643void KMFilterDialog::slotUpdateAccountList()
644{
645 mAccountList->updateAccountList(mFilter);
646}
647
648void KMFilterDialog::slotImportFilter(QAction *act)
649{
650 if (act) {
651 importFilters(act->data().value<MailCommon::FilterImporterExporter::FilterType>());
652 }
653}
654
655void KMFilterDialog::importFilters(MailCommon::FilterImporterExporter::FilterType type)
656{
657 MailCommon::FilterImporterPathCache::self()->clear();
658 FilterImporterExporter importer(this);
659 bool canceled = false;
660 QList<MailFilter *> filters = importer.importFilters(canceled, type);
661 if (canceled) {
662 return;
663 }
664
665 if (filters.isEmpty()) {
666 KMessageBox::information(this, i18n("No filter was imported."));
667 return;
668 }
669 QStringList listOfFilter;
671
672 for (QList<MailFilter *>::ConstIterator it = filters.constBegin(); it != end; ++it) {
673 mFilterList->appendFilter(*it); // no need to deep copy, ownership passes to the list
674 listOfFilter << (*it)->name();
675 }
676
677 KMessageBox::informationList(this, i18n("Filters which were imported:"), listOfFilter);
678}
679
680void KMFilterDialog::slotExportFilters()
681{
682 bool wasCanceled = false;
683 const QList<MailFilter *> filters = mFilterList->filtersForSaving(false, wasCanceled);
684 if (filters.isEmpty()) {
685 KMessageBox::information(this, i18n("Any filter found."));
686 return;
687 }
688 if (wasCanceled) {
689 qDeleteAll(filters);
690 return;
691 }
692 FilterImporterExporter exporter(this);
693 exporter.exportFilters(filters);
694}
695
696void KMFilterDialog::slotDisableAccept()
697{
698 mDoNotClose = true;
699}
700
701void KMFilterDialog::slotDialogUpdated()
702{
703 qCDebug(MAILCOMMON_LOG) << "Detected a change in data bound to the dialog!";
704 if (!mIgnoreFilterUpdates) {
705 mApplyButton->setEnabled(true);
706 }
707}
708
709void KMFilterDialog::slotExportAsSieveScript()
710{
711 if (mApplyButton->isEnabled()) {
713 i18nc("@info",
714 "Some filters were changed and not saved yet.<br>"
715 "You must save your filters before they can be exported."),
716 i18nc("@title:window", "Filters changed."));
717 return;
718 }
720 i18n("We cannot convert all KMail filters to sieve scripts but we can try :)"),
721 i18nc("@title:window", "Convert KMail filters to sieve scripts"));
722 bool wasCanceled = false;
723 const QList<MailFilter *> filters = mFilterList->filtersForSaving(false, wasCanceled);
724 if (filters.isEmpty()) {
725 return;
726 }
727 if (!wasCanceled) {
728 QPointer<FilterSelectionDialog> dlg = new FilterSelectionDialog(this);
729 dlg->setFilters(filters);
730 if (dlg->exec() == QDialog::Accepted) {
731 QList<MailFilter *> lst = dlg->selectedFilters();
732 if (!lst.isEmpty()) {
733 FilterConvertToSieve convert(lst);
734 convert.convert();
735 qDeleteAll(lst);
736 } else {
737 KMessageBox::information(this, i18n("No filters selected."), i18nc("@title:window", "Convert KMail filters to sieve scripts"));
738 }
739 }
740 delete dlg;
741 } else {
742 qDeleteAll(filters);
743 }
744}
745
746void KMFilterDialog::slotHelp()
747{
748 PimCommon::Util::invokeHelp(QStringLiteral("kmail2/filters.html"));
749}
750
751#include "moc_kmfilterdialog.cpp"
bool isValid() const
QList< Item > List
void iconChanged(const QString &icon)
void result(KJob *job)
void keySequenceChanged(const QKeySequence &seq)
static KSharedConfig::Ptr openStateConfig(const QString &fileName=QString())
A container widget for a list of FilterActionWidgets.
Utility class that provides persisting of filters to/from KConfig.
void filter(const Akonadi::Item &item, const QString &identifier, const QString &resourceId) const
Apply filters interface.
static FilterManager * instance()
Returns the global filter manager object.
The MailFilter class.
Definition mailfilter.h:29
bool applyOnOutbound() const
SearchPattern * pattern()
Provides a reference to the internal pattern.
AccountType applicability() const
bool configureShortcut() const
bool applyOnInbound() const
bool configureToolbar() const
const QKeySequence & shortcut() const
QString icon() const
bool applyOnExplicit() const
QList< FilterAction * > * actions()
Provides a reference to the internal action list.
bool applyOnAllFoldersInbound() const
Returns whether the filter should be applied on inbound emails in all folders, not just Inbox.
bool applyBeforeOutbound() const
RequiredPart
Possible required parts.
Definition searchrule.h:70
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
T convert(const QVariant &value)
void informationList(QWidget *parent, const QString &text, const QStringList &strlist, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
void information(QWidget *parent, const QString &text, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
const QList< QKeySequence > & shortcut(StandardShortcut id)
The filter dialog.
void clicked(bool checked)
void setShortcut(const QKeySequence &key)
void toggled(bool checked)
QVariant data() const const
void triggered(bool checked)
void addLayout(QLayout *layout, int stretch)
virtual void accept()
virtual void reject()
ShortcutOverride
void ignore()
Type type() const const
QIcon fromTheme(const QString &name)
typedef ConstIterator
const_iterator constBegin() const const
const_iterator constEnd() const const
bool isEmpty() const const
void triggered(QAction *action)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
virtual bool event(QEvent *e)
T qobject_cast(QObject *object)
void setDefault(bool)
void setChildrenCollapsible(bool)
AlignTop
Key_Return
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void itemChanged(QTreeWidgetItem *item, int column)
QVariant fromValue(T &&value)
T value() const const
void setFocus()
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Mar 7 2025 11:50:34 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.