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(i18n("KMail filters"), this);
99 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::KMailFilter));
100 menu->addAction(act);
101
102 act = new QAction(i18n("Thunderbird filters"), this);
103 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::ThunderBirdFilter));
104 menu->addAction(act);
105
106 act = new QAction(i18n("Evolution filters"), this);
107 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::EvolutionFilter));
108 menu->addAction(act);
109
110 act = new QAction(i18n("Sylpheed filters"), this);
111 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::SylpheedFilter));
112 menu->addAction(act);
113
114 act = new QAction(i18n("Procmail filters"), this);
115 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::ProcmailFilter));
116 menu->addAction(act);
117
118 act = new QAction(i18n("Balsa filters"), this);
119 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::BalsaFilter));
120 menu->addAction(act);
121
122 act = new QAction(i18n("Claws Mail filters"), this);
123 act->setData(QVariant::fromValue(MailCommon::FilterImporterExporter::ClawsMailFilter));
124 menu->addAction(act);
125
126 act = new QAction(i18n("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(i18n("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(i18n("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(i18n("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(i18n("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(i18n("From all accounts"), radioGroupWidget);
204 bg->addButton(mApplyOnForAll);
205 vbl3->addWidget(mApplyOnForAll);
206
207 mApplyOnForTraditional = new QRadioButton(i18n("From all but online IMAP accounts"), radioGroupWidget);
208 bg->addButton(mApplyOnForTraditional);
209 vbl3->addWidget(mApplyOnForTraditional);
210
211 mApplyOnForChecked = new QRadioButton(i18n("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(i18n("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(i18n("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(i18n("Apply this filter on manual &filtering"), mAdvOptsGroup);
235 gl->addWidget(mApplyOnCtrlJ, 7, 0, 1, 4);
236
237 mApplyOnAllFolders = new QCheckBox(i18n("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(i18n("If this filter &matches, stop processing here"), mAdvOptsGroup);
246 gl->addWidget(mStopProcessingHere, 9, 0, 1, 4);
247
248 mConfigureShortcut = new QCheckBox(i18n("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(i18n("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(i18n("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(i18n("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()) {
419 KMessageBox::information(this, i18nc("@info", "Unable to apply this filter since there are no folders selected."), i18n("No folder selected."));
420 return;
421 }
422
423 if (mApplyButton->isEnabled()) {
425 i18nc("@info",
426 "Some filters were changed and not saved yet. "
427 "You must save your filters before they can be applied."),
428 i18n("Filters changed."));
429 return;
430 }
432 const QStringList selectedFiltersId = mFilterList->selectedFilterId(requiredPart, mFolderRequester->collection().resource());
433 if (selectedFiltersId.isEmpty()) {
434 KMessageBox::information(this, i18nc("@info", "Unable to apply a filter since there are no filters currently selected."), i18n("No filters selected."));
435 return;
436 }
437 auto job = new Akonadi::ItemFetchJob(mFolderRequester->collection(), this);
438 job->setProperty("requiredPart", QVariant::fromValue(requiredPart));
439 job->setProperty("listFilters", QVariant::fromValue(selectedFiltersId));
440
441 connect(job, &KJob::result, this, &KMFilterDialog::slotFetchItemsForFolderDone);
442
443 mRunNow->setEnabled(false); // Disable it
444}
445
446void KMFilterDialog::slotFetchItemsForFolderDone(KJob *job)
447{
448 auto fjob = qobject_cast<Akonadi::ItemFetchJob *>(job);
449 Q_ASSERT(fjob);
450
451 QStringList filtersId;
452 if (fjob->property("listFilters").isValid()) {
453 filtersId = fjob->property("listFilters").toStringList();
454 }
455
457 if (fjob->property("requiredPart").isValid()) {
458 requiredPart = fjob->property("requiredPart").value<SearchRule::RequiredPart>();
459 }
460 Akonadi::Item::List items = fjob->items();
461 mRunNow->setEnabled(true);
462 MailCommon::FilterManager::instance()->filter(items, requiredPart, filtersId);
463}
464
465void KMFilterDialog::slotSaveSize()
466{
467 mFilterList->slotAccepted();
468 KConfigGroup myGroup(KSharedConfig::openStateConfig(), QStringLiteral("Geometry"));
469 myGroup.writeEntry("filterDialogSize", size());
470 myGroup.sync();
471}
472
473void KMFilterDialog::slotFilterSelected(MailFilter *aFilter)
474{
475 Q_ASSERT(aFilter);
476 mIgnoreFilterUpdates = true;
477 mActionLister->setActionList(aFilter->actions());
478
479 mAdvOptsGroup->setEnabled(true);
480
481 mPatternEdit->setSearchPattern(aFilter->pattern());
482 mFilter = aFilter;
483
484 qCDebug(MAILCOMMON_LOG) << "apply on inbound ==" << aFilter->applyOnInbound();
485 qCDebug(MAILCOMMON_LOG) << "apply on outbound ==" << aFilter->applyOnOutbound();
486 qCDebug(MAILCOMMON_LOG) << "apply before outbound == " << aFilter->applyBeforeOutbound();
487 qCDebug(MAILCOMMON_LOG) << "apply on explicit ==" << aFilter->applyOnExplicit();
488 qCDebug(MAILCOMMON_LOG) << "apply on all folders inboud == " << aFilter->applyOnAllFoldersInbound();
489
490 // NOTE: setting these values activates the slot that sets them in
491 // the filter! So make sure we have the correct values _before_ we
492 // set the first one:
493 const bool applyOnIn = aFilter->applyOnInbound();
494 const bool applyOnForAll = aFilter->applicability() == MailFilter::All;
495 const bool applyOnTraditional = aFilter->applicability() == MailFilter::ButImap;
496 const bool applyBeforeOut = aFilter->applyBeforeOutbound();
497 const bool applyOnOut = aFilter->applyOnOutbound();
498 const bool applyOnAllFolders = aFilter->applyOnAllFoldersInbound();
499 const bool applyOnExplicit = aFilter->applyOnExplicit();
500 const bool stopHere = aFilter->stopProcessingHere();
501 const bool configureShortcut = aFilter->configureShortcut();
502 const bool configureToolbar = aFilter->configureToolbar();
503 const QString icon = aFilter->icon();
504 const QKeySequence shortcut(aFilter->shortcut());
505
506 mApplyOnIn->setChecked(applyOnIn);
507 mApplyOnForAll->setEnabled(applyOnIn);
508 mApplyOnForTraditional->setEnabled(applyOnIn);
509 mApplyOnForChecked->setEnabled(applyOnIn);
510 mApplyOnForAll->setChecked(applyOnForAll);
511 mApplyOnAllFolders->setChecked(applyOnAllFolders);
512 mApplyOnForTraditional->setChecked(applyOnTraditional);
513 mApplyOnForChecked->setChecked(!applyOnForAll && !applyOnTraditional);
514 mAccountList->setEnabled(mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked());
515 slotUpdateAccountList();
516 mApplyBeforeOut->setChecked(applyBeforeOut);
517 mApplyOnOut->setChecked(applyOnOut);
518 mApplyOnCtrlJ->setChecked(applyOnExplicit);
519 mStopProcessingHere->setChecked(stopHere);
520 mConfigureShortcut->setChecked(configureShortcut);
521 mKeySeqWidget->setKeySequence(shortcut, KKeySequenceWidget::NoValidate);
522 mConfigureToolbar->setChecked(configureToolbar);
523 mFilterActionIconButton->setIcon(icon);
524 mIgnoreFilterUpdates = false;
525}
526
527void KMFilterDialog::slotReset()
528{
529 mFilter = nullptr;
530 mPatternEdit->reset();
531
532 mActionLister->reset();
533 mAdvOptsGroup->setEnabled(false);
534 slotUpdateAccountList();
535}
536
537void KMFilterDialog::slotUpdateFilter()
538{
539 mPatternEdit->updateSearchPattern();
540 mActionLister->updateActionList();
541}
542
543void KMFilterDialog::slotApplicabilityChanged()
544{
545 if (mFilter) {
546 mFilter->setApplyOnInbound(mApplyOnIn->isChecked());
547 mFilter->setApplyBeforeOutbound(mApplyBeforeOut->isChecked());
548 mFilter->setApplyOnOutbound(mApplyOnOut->isChecked());
549 mFilter->setApplyOnExplicit(mApplyOnCtrlJ->isChecked());
550 mFilter->setApplyOnAllFoldersInbound(mApplyOnAllFolders->isChecked());
551 if (mApplyOnForAll->isChecked()) {
552 mFilter->setApplicability(MailFilter::All);
553 mFilter->clearApplyOnAccount();
554 } else if (mApplyOnForTraditional->isChecked()) {
555 mFilter->setApplicability(MailFilter::ButImap);
556 } else if (mApplyOnForChecked->isChecked()) {
557 mFilter->setApplicability(MailFilter::Checked);
558 }
559
560 mApplyOnForAll->setEnabled(mApplyOnIn->isChecked());
561 mApplyOnForTraditional->setEnabled(mApplyOnIn->isChecked());
562 mApplyOnForChecked->setEnabled(mApplyOnIn->isChecked());
563 mAccountList->setEnabled(mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked());
564
565 // Advanced tab functionality - Update list of accounts this filter applies to
566 if (!mApplyOnForAll->isChecked()) {
567 mAccountList->applyOnAccount(mFilter);
568 }
569
570 // Enable the apply button
571 slotDialogUpdated();
572
573 qCDebug(MAILCOMMON_LOG) << "Setting filter to be applied at" << (mFilter->applyOnInbound() ? "incoming " : "")
574 << (mFilter->applyOnOutbound() ? "outgoing " : "") << (mFilter->applyBeforeOutbound() ? "before_outgoing " : "")
575 << (mFilter->applyOnAllFoldersInbound() ? "all folders inboud " : "") << (mFilter->applyOnExplicit() ? "explicit CTRL-J" : "");
576 }
577}
578
579void KMFilterDialog::slotApplicableAccountsChanged()
580{
581 // Advanced tab functionality - Update list of accounts this filter applies to
582 if (mFilter && mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked()) {
583 QTreeWidgetItemIterator it(mAccountList);
584
585 while (QTreeWidgetItem *item = *it) {
586 const QString id = item->text(2);
587 mFilter->setApplyOnAccount(id, item->checkState(0) == Qt::Checked);
588 ++it;
589 }
590
591 // Enable the apply button
592 slotDialogUpdated();
593 }
594}
595
596void KMFilterDialog::slotStopProcessingButtonToggled(bool aChecked)
597{
598 if (mFilter) {
599 mFilter->setStopProcessingHere(aChecked);
600
601 // Enable the apply button
602 slotDialogUpdated();
603 }
604}
605
606void KMFilterDialog::slotShortcutChanged(const QKeySequence &newSeq)
607{
608 if (mFilter) {
609 mKeySeqWidget->applyStealShortcut();
610 mFilter->setShortcut(newSeq);
611
612 // Enable the apply button
613 slotDialogUpdated();
614 }
615}
616
617void KMFilterDialog::slotConfigureToolbarButtonToggled(bool aChecked)
618{
619 if (mFilter) {
620 mFilter->setConfigureToolbar(aChecked);
621 // Enable the apply button
622 slotDialogUpdated();
623 }
624}
625
626void KMFilterDialog::slotFilterActionIconChanged(const QString &icon)
627{
628 if (mFilter) {
629 mFilter->setIcon(icon);
630 // Enable the apply button
631 slotDialogUpdated();
632 }
633}
634
635void KMFilterDialog::slotUpdateAccountList()
636{
637 mAccountList->updateAccountList(mFilter);
638}
639
640void KMFilterDialog::slotImportFilter(QAction *act)
641{
642 if (act) {
643 importFilters(act->data().value<MailCommon::FilterImporterExporter::FilterType>());
644 }
645}
646
647void KMFilterDialog::importFilters(MailCommon::FilterImporterExporter::FilterType type)
648{
649 MailCommon::FilterImporterPathCache::self()->clear();
650 FilterImporterExporter importer(this);
651 bool canceled = false;
652 QList<MailFilter *> filters = importer.importFilters(canceled, type);
653 if (canceled) {
654 return;
655 }
656
657 if (filters.isEmpty()) {
658 KMessageBox::information(this, i18n("No filter was imported."));
659 return;
660 }
661 QStringList listOfFilter;
663
664 for (QList<MailFilter *>::ConstIterator it = filters.constBegin(); it != end; ++it) {
665 mFilterList->appendFilter(*it); // no need to deep copy, ownership passes to the list
666 listOfFilter << (*it)->name();
667 }
668
669 KMessageBox::informationList(this, i18n("Filters which were imported:"), listOfFilter);
670}
671
672void KMFilterDialog::slotExportFilters()
673{
674 bool wasCanceled = false;
675 const QList<MailFilter *> filters = mFilterList->filtersForSaving(false, wasCanceled);
676 if (filters.isEmpty()) {
677 KMessageBox::information(this, i18n("Any filter found."));
678 return;
679 }
680 if (wasCanceled) {
681 qDeleteAll(filters);
682 return;
683 }
684 FilterImporterExporter exporter(this);
685 exporter.exportFilters(filters);
686}
687
688void KMFilterDialog::slotDisableAccept()
689{
690 mDoNotClose = true;
691}
692
693void KMFilterDialog::slotDialogUpdated()
694{
695 qCDebug(MAILCOMMON_LOG) << "Detected a change in data bound to the dialog!";
696 if (!mIgnoreFilterUpdates) {
697 mApplyButton->setEnabled(true);
698 }
699}
700
701void KMFilterDialog::slotExportAsSieveScript()
702{
703 if (mApplyButton->isEnabled()) {
705 i18nc("@info",
706 "Some filters were changed and not saved yet.<br>"
707 "You must save your filters before they can be exported."),
708 i18n("Filters changed."));
709 return;
710 }
712 i18n("We cannot convert all KMail filters to sieve scripts but we can try :)"),
713 i18n("Convert KMail filters to sieve scripts"));
714 bool wasCanceled = false;
715 const QList<MailFilter *> filters = mFilterList->filtersForSaving(false, wasCanceled);
716 if (filters.isEmpty()) {
717 return;
718 }
719 if (!wasCanceled) {
720 QPointer<FilterSelectionDialog> dlg = new FilterSelectionDialog(this);
721 dlg->setFilters(filters);
722 if (dlg->exec() == QDialog::Accepted) {
723 QList<MailFilter *> lst = dlg->selectedFilters();
724 if (!lst.isEmpty()) {
725 FilterConvertToSieve convert(lst);
726 convert.convert();
727 qDeleteAll(lst);
728 } else {
729 KMessageBox::information(this, i18n("No filters selected."), i18n("Convert KMail filters to sieve scripts"));
730 }
731 }
732 delete dlg;
733 } else {
734 qDeleteAll(filters);
735 }
736}
737
738void KMFilterDialog::slotHelp()
739{
740 PimCommon::Util::invokeHelp(QStringLiteral("kmail2/filters.html"));
741}
742
743#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
T value(qsizetype i) const const
void triggered(QAction *action)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
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 Tue Mar 26 2024 11:14:01 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.