Mailcommon

kmfilterdialog.cpp
1/*
2 Filter Dialog
3
4 SPDX-FileCopyrightText: Marc Mutz <mutz@kde.org>
5 SPDX-FileCopyrightText: 2011-2024 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 mKeySeqWidget->setModifierlessAllowed(true);
272 mKeySeqWidget->setCheckActionCollections(actionCollection);
273 inMenuLayout->addRow(i18n("Shortcut:"), mKeySeqWidget);
274
275 mFilterActionIconButton = new KIconButton(mInMenuWidget);
276 mFilterActionIconButton->setIconType(KIconLoader::NoGroup, KIconLoader::Action, false);
277 mFilterActionIconButton->setIconSize(16);
278 mFilterActionIconButton->setIcon(QIcon::fromTheme(QStringLiteral("system-run")));
279 inMenuLayout->addRow(i18n("Icon for this filter:"), mFilterActionIconButton);
280
281 mAdvOptsGroup->setLayout(gl);
282 mInMenuWidget->setEnabled(false);
283 }
284 vbl2->addWidget(mAdvOptsGroup, 0, Qt::AlignTop);
285
286 auto applySpecificFiltersLayout = new QHBoxLayout;
287 auto lab = new QLabel(i18nc("@label:textbox", "Run selected filter(s) on: "), this);
288 applySpecificFiltersLayout->addWidget(lab);
289 mFolderRequester = new MailCommon::FolderRequester(this);
290 mFolderRequester->setNotAllowToCreateNewFolder(true);
291 applySpecificFiltersLayout->addWidget(mFolderRequester);
292 connect(mFolderRequester, &FolderRequester::folderChanged, this, &KMFilterDialog::slotFolderChanged);
293 mRunNow = new QPushButton(i18nc("@action:button", "Run Now"), this);
294 mRunNow->setEnabled(false);
295 applySpecificFiltersLayout->addWidget(mRunNow);
296 connect(mRunNow, &QPushButton::clicked, this, &KMFilterDialog::slotRunFilters);
297 topVLayout->addLayout(applySpecificFiltersLayout);
298 // spacer:
299 vbl->addStretch(1);
300
301 // load the filter parts into the edit widgets
302 connect(mFilterList, &KMFilterListBox::filterSelected, this, &KMFilterDialog::slotFilterSelected);
303
304 // transfer changes from the 'Apply this filter on...'
305 // combo box to the filter
306 connect(mApplyOnIn, &QCheckBox::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
307 connect(mApplyOnForAll, &QRadioButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
308 connect(mApplyOnForTraditional, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
309 connect(mApplyOnForChecked, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
310 connect(mApplyBeforeOut, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
311 connect(mApplyOnAllFolders, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
312 connect(mApplyOnOut, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
313 connect(mApplyOnCtrlJ, &QAbstractButton::clicked, this, &KMFilterDialog::slotApplicabilityChanged);
314 connect(mAccountList, &KMFilterAccountList::itemChanged, this, &KMFilterDialog::slotApplicableAccountsChanged);
315
316 // transfer changes from the 'stop processing here'
317 // check box to the filter
318 connect(mStopProcessingHere, &QCheckBox::toggled, this, &KMFilterDialog::slotStopProcessingButtonToggled);
319
320 connect(mKeySeqWidget, &KKeySequenceWidget::keySequenceChanged, this, &KMFilterDialog::slotShortcutChanged);
321
322 connect(mConfigureToolbar, &QCheckBox::toggled, this, &KMFilterDialog::slotConfigureToolbarButtonToggled);
323
324 connect(mFilterActionIconButton, &KIconButton::iconChanged, this, &KMFilterDialog::slotFilterActionIconChanged);
325
326 // reset all widgets here
327 connect(mFilterList, &KMFilterListBox::resetWidgets, this, &KMFilterDialog::slotReset);
328
329 connect(mFilterList, &KMFilterListBox::applyWidgets, this, &KMFilterDialog::slotUpdateFilter);
330
331 // support auto-naming the filter
332 connect(mPatternEdit, &MailCommon::SearchPatternEdit::maybeNameChanged, mFilterList, &KMFilterListBox::slotUpdateFilterName);
333
334 // save filters on 'Apply' or 'OK'
335 connect(mApplyButton, &QAbstractButton::clicked, this, &KMFilterDialog::slotApply);
336
337 // save dialog size on 'OK'
338 connect(okButton, &QAbstractButton::clicked, this, &KMFilterDialog::slotSaveSize);
339
340 // destruct the dialog on close and Cancel
341 connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, this, &KMFilterDialog::slotFinished);
342
343 // disable closing when user wants to continue editing
344 connect(mFilterList, &KMFilterListBox::abortClosing, this, &KMFilterDialog::slotDisableAccept);
345
346 connect(mFilterList, &KMFilterListBox::filterCreated, this, &KMFilterDialog::slotDialogUpdated);
347 connect(mFilterList, &KMFilterListBox::filterRemoved, this, &KMFilterDialog::slotDialogUpdated);
348 connect(mFilterList, &KMFilterListBox::filterUpdated, this, &KMFilterDialog::slotDialogUpdated);
349 connect(mFilterList, &KMFilterListBox::filterOrderAltered, this, &KMFilterDialog::slotDialogUpdated);
350 connect(mPatternEdit, &MailCommon::SearchPatternEdit::patternChanged, this, &KMFilterDialog::slotDialogUpdated);
351 connect(mActionLister, qOverload<QWidget *>(&FilterActionWidgetLister::widgetAdded), this, &KMFilterDialog::slotDialogUpdated);
352 connect(mActionLister, qOverload<QWidget *>(&FilterActionWidgetLister::widgetRemoved), this, &KMFilterDialog::slotDialogUpdated);
353 connect(mActionLister, &MailCommon::FilterActionWidgetLister::filterModified, this, &KMFilterDialog::slotDialogUpdated);
354 connect(mActionLister, &MailCommon::FilterActionWidgetLister::clearWidgets, this, &KMFilterDialog::slotDialogUpdated);
355 KConfigGroup myGroup(KSharedConfig::openStateConfig(), QStringLiteral("Geometry"));
356 const QSize size = myGroup.readEntry("filterDialogSize", QSize());
357 if (size != QSize()) {
358 resize(size);
359 } else {
360 adjustSize();
361 }
362
363 // load the filter list (emits filterSelected())
364 mFilterList->loadFilterList(createDummyFilter);
365 mIgnoreFilterUpdates = false;
366}
367
368void KMFilterDialog::createFilter(const QByteArray &field, const QString &value)
369{
370 mFilterList->createFilter(field, value);
371}
372
373void KMFilterDialog::accept()
374{
375 if (mDoNotClose) {
376 mDoNotClose = false; // only abort current close attempt
377 } else {
379 slotFinished();
380 }
381}
382
383bool KMFilterDialog::event(QEvent *e)
384{
385 // Close the bar when pressing Escape.
386 // Not using a QShortcut for this because it could conflict with
387 // window-global actions (e.g. Emil Sedgh binds Esc to "close tab").
388 // With a shortcut override we can catch this before it gets to kactions.
389 const bool shortCutOverride = (e->type() == QEvent::ShortcutOverride);
390 if (shortCutOverride || e->type() == QEvent::KeyPress) {
391 auto kev = static_cast<QKeyEvent *>(e);
392 if (kev->key() == Qt::Key_Escape) {
393 e->ignore();
394 return true;
395 }
396 }
397 return QDialog::event(e);
398}
399
400void KMFilterDialog::slotApply()
401{
402 mApplyButton->setEnabled(false);
403 mFilterList->slotApplied();
404}
405
406void KMFilterDialog::slotFinished()
407{
408 deleteLater();
409}
410
411void KMFilterDialog::slotFolderChanged(const Akonadi::Collection &collection)
412{
413 mRunNow->setEnabled(collection.isValid());
414}
415
416void KMFilterDialog::slotRunFilters()
417{
418 if (!mFolderRequester->collection().isValid()) {
420 i18nc("@info", "Unable to apply this filter since there are no folders selected."),
421 i18nc("@title:window", "No folder selected."));
422 return;
423 }
424
425 if (mApplyButton->isEnabled()) {
427 i18nc("@info",
428 "Some filters were changed and not saved yet. "
429 "You must save your filters before they can be applied."),
430 i18nc("@title:window", "Filters changed."));
431 return;
432 }
434 const QStringList selectedFiltersId = mFilterList->selectedFilterId(requiredPart, mFolderRequester->collection().resource());
435 if (selectedFiltersId.isEmpty()) {
437 i18nc("@info", "Unable to apply a filter since there are no filters currently selected."),
438 i18nc("@title:window", "No filters selected."));
439 return;
440 }
441 auto job = new Akonadi::ItemFetchJob(mFolderRequester->collection(), this);
442 job->setProperty("requiredPart", QVariant::fromValue(requiredPart));
443 job->setProperty("listFilters", QVariant::fromValue(selectedFiltersId));
444
445 connect(job, &KJob::result, this, &KMFilterDialog::slotFetchItemsForFolderDone);
446
447 mRunNow->setEnabled(false); // Disable it
448}
449
450void KMFilterDialog::slotFetchItemsForFolderDone(KJob *job)
451{
453 Q_ASSERT(fjob);
454
455 QStringList filtersId;
456 if (fjob->property("listFilters").isValid()) {
457 filtersId = fjob->property("listFilters").toStringList();
458 }
459
461 if (fjob->property("requiredPart").isValid()) {
462 requiredPart = fjob->property("requiredPart").value<SearchRule::RequiredPart>();
463 }
464 Akonadi::Item::List items = fjob->items();
465 mRunNow->setEnabled(true);
466 MailCommon::FilterManager::instance()->filter(items, requiredPart, filtersId);
467}
468
469void KMFilterDialog::slotSaveSize()
470{
471 mFilterList->slotAccepted();
472 KConfigGroup myGroup(KSharedConfig::openStateConfig(), QStringLiteral("Geometry"));
473 myGroup.writeEntry("filterDialogSize", size());
474 myGroup.sync();
475}
476
477void KMFilterDialog::slotFilterSelected(MailFilter *aFilter)
478{
479 Q_ASSERT(aFilter);
480 mIgnoreFilterUpdates = true;
481 mActionLister->setActionList(aFilter->actions());
482
483 mAdvOptsGroup->setEnabled(true);
484
485 mPatternEdit->setSearchPattern(aFilter->pattern());
486 mFilter = aFilter;
487
488 qCDebug(MAILCOMMON_LOG) << "apply on inbound ==" << aFilter->applyOnInbound();
489 qCDebug(MAILCOMMON_LOG) << "apply on outbound ==" << aFilter->applyOnOutbound();
490 qCDebug(MAILCOMMON_LOG) << "apply before outbound == " << aFilter->applyBeforeOutbound();
491 qCDebug(MAILCOMMON_LOG) << "apply on explicit ==" << aFilter->applyOnExplicit();
492 qCDebug(MAILCOMMON_LOG) << "apply on all folders inboud == " << aFilter->applyOnAllFoldersInbound();
493
494 // NOTE: setting these values activates the slot that sets them in
495 // the filter! So make sure we have the correct values _before_ we
496 // set the first one:
497 const bool applyOnIn = aFilter->applyOnInbound();
498 const bool applyOnForAll = aFilter->applicability() == MailFilter::All;
499 const bool applyOnTraditional = aFilter->applicability() == MailFilter::ButImap;
500 const bool applyBeforeOut = aFilter->applyBeforeOutbound();
501 const bool applyOnOut = aFilter->applyOnOutbound();
502 const bool applyOnAllFolders = aFilter->applyOnAllFoldersInbound();
503 const bool applyOnExplicit = aFilter->applyOnExplicit();
504 const bool stopHere = aFilter->stopProcessingHere();
505 const bool configureShortcut = aFilter->configureShortcut();
506 const bool configureToolbar = aFilter->configureToolbar();
507 const QString icon = aFilter->icon();
508 const QKeySequence shortcut(aFilter->shortcut());
509
510 mApplyOnIn->setChecked(applyOnIn);
511 mApplyOnForAll->setEnabled(applyOnIn);
512 mApplyOnForTraditional->setEnabled(applyOnIn);
513 mApplyOnForChecked->setEnabled(applyOnIn);
514 mApplyOnForAll->setChecked(applyOnForAll);
515 mApplyOnAllFolders->setChecked(applyOnAllFolders);
516 mApplyOnForTraditional->setChecked(applyOnTraditional);
517 mApplyOnForChecked->setChecked(!applyOnForAll && !applyOnTraditional);
518 mAccountList->setEnabled(mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked());
519 slotUpdateAccountList();
520 mApplyBeforeOut->setChecked(applyBeforeOut);
521 mApplyOnOut->setChecked(applyOnOut);
522 mApplyOnCtrlJ->setChecked(applyOnExplicit);
523 mStopProcessingHere->setChecked(stopHere);
524 mConfigureShortcut->setChecked(configureShortcut);
525 mKeySeqWidget->setKeySequence(shortcut, KKeySequenceWidget::NoValidate);
526 mConfigureToolbar->setChecked(configureToolbar);
527 mFilterActionIconButton->setIcon(icon);
528 mIgnoreFilterUpdates = false;
529}
530
531void KMFilterDialog::slotReset()
532{
533 mFilter = nullptr;
534 mPatternEdit->reset();
535
536 mActionLister->reset();
537 mAdvOptsGroup->setEnabled(false);
538 slotUpdateAccountList();
539}
540
541void KMFilterDialog::slotUpdateFilter()
542{
543 mPatternEdit->updateSearchPattern();
544 mActionLister->updateActionList();
545}
546
547void KMFilterDialog::slotApplicabilityChanged()
548{
549 if (mFilter) {
550 mFilter->setApplyOnInbound(mApplyOnIn->isChecked());
551 mFilter->setApplyBeforeOutbound(mApplyBeforeOut->isChecked());
552 mFilter->setApplyOnOutbound(mApplyOnOut->isChecked());
553 mFilter->setApplyOnExplicit(mApplyOnCtrlJ->isChecked());
554 mFilter->setApplyOnAllFoldersInbound(mApplyOnAllFolders->isChecked());
555 if (mApplyOnForAll->isChecked()) {
556 mFilter->setApplicability(MailFilter::All);
557 mFilter->clearApplyOnAccount();
558 } else if (mApplyOnForTraditional->isChecked()) {
559 mFilter->setApplicability(MailFilter::ButImap);
560 } else if (mApplyOnForChecked->isChecked()) {
561 mFilter->setApplicability(MailFilter::Checked);
562 }
563
564 mApplyOnForAll->setEnabled(mApplyOnIn->isChecked());
565 mApplyOnForTraditional->setEnabled(mApplyOnIn->isChecked());
566 mApplyOnForChecked->setEnabled(mApplyOnIn->isChecked());
567 mAccountList->setEnabled(mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked());
568
569 // Advanced tab functionality - Update list of accounts this filter applies to
570 if (!mApplyOnForAll->isChecked()) {
571 mAccountList->applyOnAccount(mFilter);
572 }
573
574 // Enable the apply button
575 slotDialogUpdated();
576
577 qCDebug(MAILCOMMON_LOG) << "Setting filter to be applied at" << (mFilter->applyOnInbound() ? "incoming " : "")
578 << (mFilter->applyOnOutbound() ? "outgoing " : "") << (mFilter->applyBeforeOutbound() ? "before_outgoing " : "")
579 << (mFilter->applyOnAllFoldersInbound() ? "all folders inboud " : "") << (mFilter->applyOnExplicit() ? "explicit CTRL-J" : "");
580 }
581}
582
583void KMFilterDialog::slotApplicableAccountsChanged()
584{
585 // Advanced tab functionality - Update list of accounts this filter applies to
586 if (mFilter && mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked()) {
587 QTreeWidgetItemIterator it(mAccountList);
588
589 while (QTreeWidgetItem *item = *it) {
590 const QString id = item->text(2);
591 mFilter->setApplyOnAccount(id, item->checkState(0) == Qt::Checked);
592 ++it;
593 }
594
595 // Enable the apply button
596 slotDialogUpdated();
597 }
598}
599
600void KMFilterDialog::slotStopProcessingButtonToggled(bool aChecked)
601{
602 if (mFilter) {
603 mFilter->setStopProcessingHere(aChecked);
604
605 // Enable the apply button
606 slotDialogUpdated();
607 }
608}
609
610void KMFilterDialog::slotShortcutChanged(const QKeySequence &newSeq)
611{
612 if (mFilter) {
613 mKeySeqWidget->applyStealShortcut();
614 mFilter->setShortcut(newSeq);
615
616 // Enable the apply button
617 slotDialogUpdated();
618 }
619}
620
621void KMFilterDialog::slotConfigureToolbarButtonToggled(bool aChecked)
622{
623 if (mFilter) {
624 mFilter->setConfigureToolbar(aChecked);
625 // Enable the apply button
626 slotDialogUpdated();
627 }
628}
629
630void KMFilterDialog::slotFilterActionIconChanged(const QString &icon)
631{
632 if (mFilter) {
633 mFilter->setIcon(icon);
634 // Enable the apply button
635 slotDialogUpdated();
636 }
637}
638
639void KMFilterDialog::slotUpdateAccountList()
640{
641 mAccountList->updateAccountList(mFilter);
642}
643
644void KMFilterDialog::slotImportFilter(QAction *act)
645{
646 if (act) {
647 importFilters(act->data().value<MailCommon::FilterImporterExporter::FilterType>());
648 }
649}
650
651void KMFilterDialog::importFilters(MailCommon::FilterImporterExporter::FilterType type)
652{
653 MailCommon::FilterImporterPathCache::self()->clear();
654 FilterImporterExporter importer(this);
655 bool canceled = false;
656 QList<MailFilter *> filters = importer.importFilters(canceled, type);
657 if (canceled) {
658 return;
659 }
660
661 if (filters.isEmpty()) {
662 KMessageBox::information(this, i18n("No filter was imported."));
663 return;
664 }
665 QStringList listOfFilter;
667
668 for (QList<MailFilter *>::ConstIterator it = filters.constBegin(); it != end; ++it) {
669 mFilterList->appendFilter(*it); // no need to deep copy, ownership passes to the list
670 listOfFilter << (*it)->name();
671 }
672
673 KMessageBox::informationList(this, i18n("Filters which were imported:"), listOfFilter);
674}
675
676void KMFilterDialog::slotExportFilters()
677{
678 bool wasCanceled = false;
679 const QList<MailFilter *> filters = mFilterList->filtersForSaving(false, wasCanceled);
680 if (filters.isEmpty()) {
681 KMessageBox::information(this, i18n("Any filter found."));
682 return;
683 }
684 if (wasCanceled) {
685 qDeleteAll(filters);
686 return;
687 }
688 FilterImporterExporter exporter(this);
689 exporter.exportFilters(filters);
690}
691
692void KMFilterDialog::slotDisableAccept()
693{
694 mDoNotClose = true;
695}
696
697void KMFilterDialog::slotDialogUpdated()
698{
699 qCDebug(MAILCOMMON_LOG) << "Detected a change in data bound to the dialog!";
700 if (!mIgnoreFilterUpdates) {
701 mApplyButton->setEnabled(true);
702 }
703}
704
705void KMFilterDialog::slotExportAsSieveScript()
706{
707 if (mApplyButton->isEnabled()) {
709 i18nc("@info",
710 "Some filters were changed and not saved yet.<br>"
711 "You must save your filters before they can be exported."),
712 i18nc("@title:window", "Filters changed."));
713 return;
714 }
716 i18n("We cannot convert all KMail filters to sieve scripts but we can try :)"),
717 i18nc("@title:window", "Convert KMail filters to sieve scripts"));
718 bool wasCanceled = false;
719 const QList<MailFilter *> filters = mFilterList->filtersForSaving(false, wasCanceled);
720 if (filters.isEmpty()) {
721 return;
722 }
723 if (!wasCanceled) {
724 QPointer<FilterSelectionDialog> dlg = new FilterSelectionDialog(this);
725 dlg->setFilters(filters);
726 if (dlg->exec() == QDialog::Accepted) {
727 QList<MailFilter *> lst = dlg->selectedFilters();
728 if (!lst.isEmpty()) {
729 FilterConvertToSieve convert(lst);
730 convert.convert();
731 qDeleteAll(lst);
732 } else {
733 KMessageBox::information(this, i18n("No filters selected."), i18nc("@title:window", "Convert KMail filters to sieve scripts"));
734 }
735 }
736 delete dlg;
737 } else {
738 qDeleteAll(filters);
739 }
740}
741
742void KMFilterDialog::slotHelp()
743{
744 PimCommon::Util::invokeHelp(QStringLiteral("kmail2/filters.html"));
745}
746
747#include "moc_kmfilterdialog.cpp"
QString resource() const
bool isValid() const
void setIcon(const QString &icon)
void iconChanged(const QString &icon)
void result(KJob *job)
void keySequenceChanged(const QKeySequence &seq)
void setKeySequence(const QKeySequence &seq, Validation val=NoValidate)
static KSharedConfig::Ptr openStateConfig(const QString &fileName=QString())
A container widget for a list of FilterActionWidgets.
void setActionList(QList< FilterAction * > *list)
Sets the list of filter actions, the lister will create FilterActionWidgets for.
void reset()
Resets the action widgets.
void updateActionList()
Updates the action list according to the current action widget values.
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
void setIcon(const QString &icon)
Set the icon to be used if plugged into the filter menu or toolbar.
SearchPattern * pattern()
Provides a reference to the internal pattern.
void setApplyBeforeOutbound(bool aApply)
Set whether this filter should be applied on outbound messages before sending (aApply == TRUE) or not...
void setApplyOnInbound(bool aApply)
Set whether this filter should be applied on inbound messages (aApply == true) or not.
AccountType applicability() const
bool configureShortcut() const
void setApplyOnExplicit(bool aApply)
Set whether this filter should be applied on explicit (CTRL-J) filtering (aApply == true) or not.
void setApplicability(AccountType aApply=All)
Set whether this filter should be applied on inbound messages for all accounts (aApply == All) or inb...
bool applyOnInbound() const
bool configureToolbar() const
void setApplyOnOutbound(bool aApply)
Set whether this filter should be applied on outbound messages (aApply == true) or not.
const QKeySequence & shortcut() const
void setApplyOnAllFoldersInbound(bool aApply)
Sets whether the filter should be applied on inbound emails in all folders, not just Inbox.
QString icon() const
bool applyOnExplicit() const
QList< FilterAction * > * actions()
Provides a reference to the internal action list.
void setConfigureToolbar(bool aTool)
Set whether this filter should be plugged into the toolbar.
bool applyOnAllFoldersInbound() const
Returns whether the filter should be applied on inbound emails in all folders, not just Inbox.
void setApplyOnAccount(const QString &id, bool aApply=true)
Set whether this filter should be applied on inbound messages for the account with id (id).
void setShortcut(const QKeySequence &shortcut)
Set the shortcut to be used if plugged into the filter menu or toolbar.
bool applyBeforeOutbound() const
RequiredPart
Possible required parts.
Definition searchrule.h:68
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
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 > & end()
const QList< QKeySequence > & shortcut(StandardShortcut id)
The filter dialog.
void setChecked(bool)
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)
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()
T qobject_cast(QObject *object)
void setDefault(bool)
void setChildrenCollapsible(bool)
AlignTop
Key_Return
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QVariant fromValue(T &&value)
T value() const const
void setEnabled(bool)
virtual bool event(QEvent *event) override
void setFocus()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 6 2024 12:02:04 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.