3 Author: Marc Mutz <Marc@Mutz.com>
4 based upon work by Stefan Taferner <taferner@kde.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "kmfilterdlg.h"
23 // other KMail headers:
24 #include "mailcommon/searchpatternedit.h"
25 #include "mailcommon/filtermanager.h"
26 #include "kmmainwidget.h"
27 #include "mailcommon/filterimporterexporter.h"
28 #include "mailcommon/filteractionwidget.h"
29 #include "mailcommon/mailutil.h"
30 using MailCommon::FilterImporterExporter
;
33 #include <Akonadi/AgentType>
34 #include <Akonadi/AgentInstance>
37 #include <kcombobox.h>
38 #include <kmessagebox.h>
41 #include <kinputdialog.h>
42 #include <kiconloader.h>
43 #include <kwindowsystem.h>
45 #include <kicondialog.h>
46 #include <kkeysequencewidget.h>
47 #include <kpushbutton.h>
48 #include <kconfiggroup.h>
49 #include <ktabwidget.h>
51 #include <klistwidgetsearchline.h>
55 #include <QGridLayout>
57 #include <QListWidget>
58 #include <QButtonGroup>
59 #include <QVBoxLayout>
60 #include <QTreeWidget>
65 using namespace MailCommon
;
67 // What's this help texts
68 const char * _wt_filterlist
=
69 I18N_NOOP( "<qt><p>This is the list of defined filters. "
70 "They are processed top-to-bottom.</p>"
71 "<p>Click on any filter to edit it "
72 "using the controls in the right-hand half "
73 "of the dialog.</p></qt>" );
74 const char * _wt_filterlist_new
=
75 I18N_NOOP( "<qt><p>Click this button to create a new filter.</p>"
76 "<p>The filter will be inserted just before the currently-"
77 "selected one, but you can always change that "
79 "<p>If you have clicked this button accidentally, you can undo this "
80 "by clicking on the <em>Delete</em> button.</p></qt>" );
81 const char * _wt_filterlist_copy
=
82 I18N_NOOP( "<qt><p>Click this button to copy a filter.</p>"
83 "<p>If you have clicked this button accidentally, you can undo this "
84 "by clicking on the <em>Delete</em> button.</p></qt>" );
85 const char * _wt_filterlist_delete
=
86 I18N_NOOP( "<qt><p>Click this button to <em>delete</em> the currently-"
87 "selected filter from the list above.</p>"
88 "<p>There is no way to get the filter back once "
89 "it is deleted, but you can always leave the "
90 "dialog by clicking <em>Cancel</em> to discard the "
91 "changes made.</p></qt>" );
92 const char * _wt_filterlist_up
=
93 I18N_NOOP( "<qt><p>Click this button to move the currently-"
94 "selected filter <em>up</em> one in the list above.</p>"
95 "<p>This is useful since the order of the filters in the list "
96 "determines the order in which they are tried on messages: "
97 "The topmost filter gets tried first.</p>"
98 "<p>If you have clicked this button accidentally, you can undo this "
99 "by clicking on the <em>Down</em> button.</p></qt>" );
100 const char * _wt_filterlist_down
=
101 I18N_NOOP( "<qt><p>Click this button to move the currently-"
102 "selected filter <em>down</em> one in the list above.</p>"
103 "<p>This is useful since the order of the filters in the list "
104 "determines the order in which they are tried on messages: "
105 "The topmost filter gets tried first.</p>"
106 "<p>If you have clicked this button accidentally, you can undo this "
107 "by clicking on the <em>Up</em> button.</p></qt>" );
108 const char * _wt_filterlist_rename
=
109 I18N_NOOP( "<qt><p>Click this button to rename the currently-selected filter.</p>"
110 "<p>Filters are named automatically, as long as they start with "
112 "<p>If you have renamed a filter accidentally and want automatic "
113 "naming back, click this button and select <em>Clear</em> followed "
114 "by <em>OK</em> in the appearing dialog.</p></qt>" );
115 const char * _wt_filterdlg_showLater
=
116 I18N_NOOP( "<qt><p>Check this button to force the confirmation dialog to be "
117 "displayed.</p><p>This is useful if you have defined a ruleset that tags "
118 "messages to be downloaded later. Without the possibility to force "
119 "the dialog popup, these messages could never be downloaded if no "
120 "other large messages were waiting on the server, or if you wanted to "
121 "change the ruleset to tag the messages differently.</p></qt>" );
123 // The anchor of the filter dialog's help.
124 const char * KMFilterDlgHelpAnchor
= "filters" ;
126 //=============================================================================
128 // class KMFilterDlg (the filter dialog)
130 //=============================================================================
132 KMFilterDlg::KMFilterDlg(QWidget
* parent
, bool createDummyFilter
)
134 mDoNotClose( false ),
135 mIgnoreFilterUpdates( true )
137 setCaption( i18n("Filter Rules") );
138 setButtons( Help
|Ok
|Apply
|Cancel
|User1
|User2
);
140 setButtonFocus( Ok
);
141 KWindowSystem::setIcons( winId(), qApp
->windowIcon().pixmap(IconSize(KIconLoader::Desktop
),IconSize(KIconLoader::Desktop
)), qApp
->windowIcon().pixmap(IconSize(KIconLoader::Small
),IconSize(KIconLoader::Small
)) );
142 setHelp( KMFilterDlgHelpAnchor
, "kmail" );
143 setButtonText( User1
, i18n("Import...") );
144 setButtonText( User2
, i18n("Export...") );
145 connect( this, SIGNAL(user1Clicked()),
146 this, SLOT(slotImportFilters()) );
147 connect( this, SIGNAL(user2Clicked()),
148 this, SLOT(slotExportFilters()) );
149 enableButtonApply( false );
151 QWidget
*w
= new QWidget( this );
153 QHBoxLayout
*topLayout
= new QHBoxLayout( w
);
154 topLayout
->setObjectName( "topLayout" );
155 topLayout
->setSpacing( spacingHint() );
156 topLayout
->setMargin( 0 );
157 QHBoxLayout
*hbl
= topLayout
;
158 QVBoxLayout
*vbl2
= 0;
162 mFilterList
= new KMFilterListBox( i18n("Available Filters"), w
);
163 topLayout
->addWidget( mFilterList
, 1 /*stretch*/ );
165 KTabWidget
*tabWidget
= new KTabWidget( w
);
166 tabWidget
->setObjectName( "kmfd_tab" );
167 topLayout
->addWidget( tabWidget
);
169 page1
= new QWidget( tabWidget
);
170 tabWidget
->addTab( page1
, i18nc("General mail filter settings.", "General") );
171 hbl
= new QHBoxLayout( page1
);
172 hbl
->setObjectName( "kmfd_hbl" );
173 hbl
->setSpacing( spacingHint() );
174 hbl
->setMargin( marginHint() );
176 page2
= new QWidget( tabWidget
);
177 tabWidget
->addTab( page2
, i18nc("Advanced mail filter settings.","Advanced") );
178 vbl2
= new QVBoxLayout( page2
);
179 vbl2
->setObjectName( "kmfd_vbl2" );
180 vbl2
->setSpacing( spacingHint() );
181 vbl2
->setMargin( marginHint() );
183 QVBoxLayout
*vbl
= new QVBoxLayout();
184 hbl
->addLayout( vbl
);
185 vbl
->setObjectName( "kmfd_vbl" );
186 vbl
->setSpacing( spacingHint() );
187 hbl
->setStretchFactor( vbl
, 2 );
189 QGroupBox
*patternGroupBox
= new QGroupBox( i18n("Filter Criteria"), page1
);
190 QHBoxLayout
*layout
= new QHBoxLayout( patternGroupBox
);
191 layout
->setContentsMargins( 0, 0, 0, 0 );
192 mPatternEdit
= new MailCommon::SearchPatternEdit(patternGroupBox
);
193 layout
->addWidget( mPatternEdit
);
195 vbl
->addWidget( patternGroupBox
, 0, Qt::AlignTop
);
197 QGroupBox
*agb
= new QGroupBox( i18n("Filter Actions"), page1
);
198 QHBoxLayout
*layout2
= new QHBoxLayout
;
199 mActionLister
= new MailCommon::FilterActionWidgetLister( agb
);
200 layout2
->addWidget( mActionLister
);
201 agb
->setLayout( layout2
);
202 vbl
->addWidget( agb
, 0, Qt::AlignTop
);
204 mAdvOptsGroup
= new QGroupBox (i18n("Advanced Options"), page2
);
206 QGridLayout
*gl
= new QGridLayout();
207 QVBoxLayout
*vbl3
= new QVBoxLayout();
208 gl
->addLayout( vbl3
, 0, 0 );
209 vbl3
->setObjectName( "vbl3" );
210 vbl3
->setSpacing( spacingHint() );
211 vbl3
->addStretch( 1 );
212 mApplyOnIn
= new QCheckBox( i18n("Apply this filter to incoming messages:"), mAdvOptsGroup
);
213 vbl3
->addWidget( mApplyOnIn
);
214 QButtonGroup
*bg
= new QButtonGroup( mAdvOptsGroup
);
215 bg
->setObjectName( "bg" );
216 mApplyOnForAll
= new QRadioButton( i18n("from all accounts"), mAdvOptsGroup
);
217 bg
->addButton( mApplyOnForAll
);
218 vbl3
->addWidget( mApplyOnForAll
);
219 mApplyOnForTraditional
= new QRadioButton( i18n("from all but online IMAP accounts"), mAdvOptsGroup
);
220 bg
->addButton( mApplyOnForTraditional
);
221 vbl3
->addWidget( mApplyOnForTraditional
);
222 mApplyOnForChecked
= new QRadioButton( i18n("from checked accounts only"), mAdvOptsGroup
);
223 bg
->addButton( mApplyOnForChecked
);
224 vbl3
->addWidget( mApplyOnForChecked
);
225 vbl3
->addStretch( 2 );
227 mAccountList
= new QTreeWidget( mAdvOptsGroup
);
228 mAccountList
->setObjectName( "accountList" );
229 mAccountList
->setColumnCount( 2 );
230 QStringList headerNames
;
231 headerNames
<< i18n("Account Name") << i18n("Type");
232 mAccountList
->setHeaderItem( new QTreeWidgetItem( headerNames
) );
233 mAccountList
->setAllColumnsShowFocus( true );
234 mAccountList
->setFrameStyle( QFrame::WinPanel
+ QFrame::Sunken
);
235 mAccountList
->setSortingEnabled( false );
236 mAccountList
->setRootIsDecorated( false );
237 gl
->addWidget( mAccountList
, 0, 1, 4, 3 );
239 mApplyBeforeOut
= new QCheckBox( i18n("Apply this filter &before sending messages"), mAdvOptsGroup
);
240 mApplyBeforeOut
->setToolTip( i18n( "<p>The filter will be triggered <b>before</b> the message is sent and it will affect both the local copy and the sent copy of the message.</p>"
241 "<p>This is required if the recipient's copy also needs to be modified.</p>" ) );
242 gl
->addWidget( mApplyBeforeOut
, 5, 0, 1, 4 );
244 mApplyOnOut
= new QCheckBox( i18n("Apply this filter to &sent messages"), mAdvOptsGroup
);
245 mApplyOnOut
->setToolTip( i18n( "<p>The filter will be triggered <b>after</b> the message is sent and it will only affect the local copy of the message.</p>"
246 "<p>If the recipient's copy also needs to be modified, please use \"Apply this filter <b>before</b> sending messages\".</p>" ) );
247 gl
->addWidget( mApplyOnOut
, 4, 0, 1, 4 );
249 mApplyOnCtrlJ
= new QCheckBox( i18n("Apply this filter on manual &filtering"), mAdvOptsGroup
);
250 gl
->addWidget( mApplyOnCtrlJ
, 6, 0, 1, 4 );
252 mStopProcessingHere
= new QCheckBox( i18n("If this filter &matches, stop processing here"), mAdvOptsGroup
);
253 gl
->addWidget( mStopProcessingHere
, 7, 0, 1, 4 );
254 mConfigureShortcut
= new QCheckBox( i18n("Add this filter to the Apply Filter menu"), mAdvOptsGroup
);
255 gl
->addWidget( mConfigureShortcut
, 8, 0, 1, 2 );
256 QLabel
*keyButtonLabel
= new QLabel( i18n( "Shortcut:" ), mAdvOptsGroup
);
257 keyButtonLabel
->setAlignment( Qt::AlignVCenter
| Qt::AlignRight
);
258 gl
->addWidget( keyButtonLabel
, 8, 2, 1, 1);
259 mKeySeqWidget
= new KKeySequenceWidget( mAdvOptsGroup
);
260 mKeySeqWidget
->setObjectName( "FilterShortcutSelector" );
261 gl
->addWidget( mKeySeqWidget
, 8, 3, 1, 1);
262 mKeySeqWidget
->setEnabled( false );
263 mKeySeqWidget
->setModifierlessAllowed( true );
264 mKeySeqWidget
->setCheckActionCollections(
265 kmkernel
->getKMMainWidget()->actionCollections() );
266 mConfigureToolbar
= new QCheckBox( i18n("Additionally add this filter to the toolbar"), mAdvOptsGroup
);
267 gl
->addWidget( mConfigureToolbar
, 9, 0, 1, 4 );
268 mConfigureToolbar
->setEnabled( false );
270 KHBox
*hbox
= new KHBox( mAdvOptsGroup
);
271 mFilterActionLabel
= new QLabel( i18n( "Icon for this filter:" ),
273 mFilterActionLabel
->setEnabled( false );
275 mFilterActionIconButton
= new KIconButton( hbox
);
276 mFilterActionLabel
->setBuddy( mFilterActionIconButton
);
277 mFilterActionIconButton
->setIconType( KIconLoader::NoGroup
, KIconLoader::Action
, false );
278 mFilterActionIconButton
->setIconSize( 16 );
279 mFilterActionIconButton
->setIcon( "system-run" );
280 mFilterActionIconButton
->setEnabled( false );
282 gl
->addWidget( hbox
, 10, 0, 1, 4 );
284 mAdvOptsGroup
->setLayout( gl
);
286 vbl2
->addWidget( mAdvOptsGroup
, 0, Qt::AlignTop
);
289 vbl
->addStretch( 1 );
291 // load the filter parts into the edit widgets
292 connect( mFilterList
, SIGNAL(filterSelected(MailCommon::MailFilter
*)),
293 this, SLOT(slotFilterSelected(MailCommon::MailFilter
*)) );
295 // transfer changes from the 'Apply this filter on...'
296 // combo box to the filter
297 connect( mApplyOnIn
, SIGNAL(clicked()),
298 this, SLOT(slotApplicabilityChanged()) );
299 connect( mApplyOnForAll
, SIGNAL(clicked()),
300 this, SLOT(slotApplicabilityChanged()) );
301 connect( mApplyOnForTraditional
, SIGNAL(clicked()),
302 this, SLOT(slotApplicabilityChanged()) );
303 connect( mApplyOnForChecked
, SIGNAL(clicked()),
304 this, SLOT(slotApplicabilityChanged()) );
305 connect( mApplyBeforeOut
, SIGNAL(clicked()),
306 this, SLOT(slotApplicabilityChanged()) );
307 connect( mApplyOnOut
, SIGNAL(clicked()),
308 this, SLOT(slotApplicabilityChanged()) );
309 connect( mApplyOnCtrlJ
, SIGNAL(clicked()),
310 this, SLOT(slotApplicabilityChanged()) );
311 connect( mAccountList
, SIGNAL(itemChanged(QTreeWidgetItem
*,int)),
312 this, SLOT(slotApplicableAccountsChanged()) );
314 // transfer changes from the 'stop processing here'
315 // check box to the filter
316 connect( mStopProcessingHere
, SIGNAL(toggled(bool)),
317 this, SLOT(slotStopProcessingButtonToggled(bool)) );
319 connect( mConfigureShortcut
, SIGNAL(toggled(bool)),
320 this, SLOT(slotConfigureShortcutButtonToggled(bool)) );
322 connect( mKeySeqWidget
, SIGNAL(keySequenceChanged(QKeySequence
)),
323 this, SLOT(slotShortcutChanged(QKeySequence
)) );
325 connect( mConfigureToolbar
, SIGNAL(toggled(bool)),
326 this, SLOT(slotConfigureToolbarButtonToggled(bool)) );
328 connect( mFilterActionIconButton
, SIGNAL(iconChanged(QString
)),
329 this, SLOT(slotFilterActionIconChanged(QString
)) );
331 // reset all widgets here
332 connect( mFilterList
, SIGNAL(resetWidgets()),
333 this, SLOT(slotReset()) );
335 connect( mFilterList
, SIGNAL(applyWidgets()),
336 this, SLOT(slotUpdateFilter()) );
338 // support auto-naming the filter
339 connect( mPatternEdit
, SIGNAL(maybeNameChanged()),
340 mFilterList
, SLOT(slotUpdateFilterName()) );
342 // save filters on 'Apply' or 'OK'
343 connect( this, SIGNAL(buttonClicked(KDialog::ButtonCode
)),
344 mFilterList
, SLOT(slotApplyFilterChanges(KDialog::ButtonCode
)) );
345 connect( button( KDialog::Apply
), SIGNAL(clicked(bool)), this, SLOT(slotApply()) );
347 // save dialog size on 'OK'
348 connect( this, SIGNAL(okClicked()),
349 this, SLOT(slotSaveSize()) );
351 // destruct the dialog on close and Cancel
352 connect( this, SIGNAL(closeClicked()),
353 this, SLOT(slotFinished()) );
354 connect( this, SIGNAL(cancelClicked()),
355 this, SLOT(slotFinished()) );
357 // disable closing when user wants to continue editing
358 connect( mFilterList
, SIGNAL(abortClosing()),
359 this, SLOT(slotDisableAccept()) );
361 connect( mFilterList
, SIGNAL(filterCreated()), this, SLOT(slotDialogUpdated()) );
362 connect( mFilterList
, SIGNAL(filterRemoved(MailCommon::MailFilter
*)),
363 this, SLOT(slotDialogUpdated()) );
364 connect( mFilterList
, SIGNAL(filterUpdated(MailCommon::MailFilter
*)),
365 this, SLOT(slotDialogUpdated()) );
366 connect( mFilterList
, SIGNAL(filterOrderAltered()), this, SLOT(slotDialogUpdated()) );
367 connect( mPatternEdit
, SIGNAL(patternChanged()), this, SLOT(slotDialogUpdated()) );
368 connect( mActionLister
, SIGNAL(widgetAdded(QWidget
*)), this, SLOT(slotDialogUpdated()) );
369 connect( mActionLister
, SIGNAL(widgetRemoved()), this, SLOT(slotDialogUpdated()) );
370 connect( mActionLister
, SIGNAL(filterModified()), this, SLOT(slotDialogUpdated()) );
372 if ( GlobalSettings::self()->filterDialogSize() != QSize() )
373 resize( GlobalSettings::self()->filterDialogSize() );
377 // load the filter list (emits filterSelected())
378 mFilterList
->loadFilterList( createDummyFilter
);
379 mIgnoreFilterUpdates
= false;
382 void KMFilterDlg::accept()
385 mDoNotClose
= false; // only abort current close attempt
392 void KMFilterDlg::slotApply()
394 enableButtonApply( false );
397 void KMFilterDlg::slotFinished() {
401 void KMFilterDlg::slotSaveSize() {
402 GlobalSettings::self()->setFilterDialogSize( size() );
405 void KMFilterDlg::slotFilterSelected( MailFilter
* aFilter
)
408 mIgnoreFilterUpdates
= true;
409 mActionLister
->setActionList( aFilter
->actions() );
411 mAdvOptsGroup
->setEnabled( true );
413 mPatternEdit
->setSearchPattern( aFilter
->pattern() );
416 kDebug() << "apply on inbound ==" << aFilter
->applyOnInbound();
417 kDebug() << "apply on outbound ==" << aFilter
->applyOnOutbound();
418 kDebug() << "apply before outbound == " << aFilter
->applyBeforeOutbound();
419 kDebug() << "apply on explicit ==" << aFilter
->applyOnExplicit();
421 // NOTE: setting these values activates the slot that sets them in
422 // the filter! So make sure we have the correct values _before_ we
423 // set the first one:
424 const bool applyOnIn
= aFilter
->applyOnInbound();
425 const bool applyOnForAll
= aFilter
->applicability() == MailFilter::All
;
426 const bool applyOnTraditional
= aFilter
->applicability() == MailFilter::ButImap
;
427 const bool applyBeforeOut
= aFilter
->applyBeforeOutbound();
428 const bool applyOnOut
= aFilter
->applyOnOutbound();
429 const bool applyOnExplicit
= aFilter
->applyOnExplicit();
430 const bool stopHere
= aFilter
->stopProcessingHere();
431 const bool configureShortcut
= aFilter
->configureShortcut();
432 const bool configureToolbar
= aFilter
->configureToolbar();
433 const QString icon
= aFilter
->icon();
434 const KShortcut
shortcut( aFilter
->shortcut() );
436 mApplyOnIn
->setChecked( applyOnIn
);
437 mApplyOnForAll
->setEnabled( applyOnIn
);
438 mApplyOnForTraditional
->setEnabled( applyOnIn
);
439 mApplyOnForChecked
->setEnabled( applyOnIn
);
440 mApplyOnForAll
->setChecked( applyOnForAll
);
441 mApplyOnForTraditional
->setChecked( applyOnTraditional
);
442 mApplyOnForChecked
->setChecked( !applyOnForAll
&& !applyOnTraditional
);
443 mAccountList
->setEnabled( mApplyOnForChecked
->isEnabled() && mApplyOnForChecked
->isChecked() );
444 slotUpdateAccountList();
445 mApplyBeforeOut
->setChecked( applyBeforeOut
);
446 mApplyOnOut
->setChecked( applyOnOut
);
447 mApplyOnCtrlJ
->setChecked( applyOnExplicit
);
448 mStopProcessingHere
->setChecked( stopHere
);
449 mConfigureShortcut
->setChecked( configureShortcut
);
450 mKeySeqWidget
->setKeySequence( shortcut
.primary(),
451 KKeySequenceWidget::NoValidate
);
452 mConfigureToolbar
->setChecked( configureToolbar
);
453 mFilterActionIconButton
->setIcon( icon
);
454 mIgnoreFilterUpdates
= false;
457 void KMFilterDlg::slotReset()
460 mPatternEdit
->reset();
462 mActionLister
->reset();
463 mAdvOptsGroup
->setEnabled( false );
464 slotUpdateAccountList();
467 void KMFilterDlg::slotUpdateFilter()
469 mPatternEdit
->updateSearchPattern();
470 mActionLister
->updateActionList();
473 void KMFilterDlg::slotApplicabilityChanged()
476 mFilter
->setApplyOnInbound( mApplyOnIn
->isChecked() );
477 mFilter
->setApplyBeforeOutbound( mApplyBeforeOut
->isChecked() );
478 mFilter
->setApplyOnOutbound( mApplyOnOut
->isChecked() );
479 mFilter
->setApplyOnExplicit( mApplyOnCtrlJ
->isChecked() );
480 if ( mApplyOnForAll
->isChecked() )
481 mFilter
->setApplicability( MailFilter::All
);
482 else if ( mApplyOnForTraditional
->isChecked() )
483 mFilter
->setApplicability( MailFilter::ButImap
);
484 else if ( mApplyOnForChecked
->isChecked() )
485 mFilter
->setApplicability( MailFilter::Checked
);
487 mApplyOnForAll
->setEnabled( mApplyOnIn
->isChecked() );
488 mApplyOnForTraditional
->setEnabled( mApplyOnIn
->isChecked() );
489 mApplyOnForChecked
->setEnabled( mApplyOnIn
->isChecked() );
490 mAccountList
->setEnabled( mApplyOnForChecked
->isEnabled() && mApplyOnForChecked
->isChecked() );
492 // Advanced tab functionality - Update list of accounts this filter applies to
493 QTreeWidgetItemIterator
it( mAccountList
);
494 while( QTreeWidgetItem
* item
= *it
) {
495 QString id
= item
->text( 2 );
496 item
->setCheckState( 0, mFilter
->applyOnAccount( id
) ? Qt::Checked
:
501 // Enable the apply button
504 kDebug() << "Setting filter to be applied at"
505 << ( mFilter
->applyOnInbound() ? "incoming " : "" )
506 << ( mFilter
->applyOnOutbound() ? "outgoing " : "" )
507 << ( mFilter
->applyBeforeOutbound() ? "before_outgoing " : "" )
508 << ( mFilter
->applyOnExplicit() ? "explicit CTRL-J" : "" );
512 void KMFilterDlg::slotApplicableAccountsChanged()
514 // Advanced tab functionality - Update list of accounts this filter applies to
515 if ( mFilter
&& mApplyOnForChecked
->isEnabled() && mApplyOnForChecked
->isChecked() ) {
517 QTreeWidgetItemIterator
it( mAccountList
);
519 while( QTreeWidgetItem
*item
= *it
) {
520 const QString id
= item
->text( 2 );
521 mFilter
->setApplyOnAccount( id
, item
->checkState( 0 ) == Qt::Checked
);
525 // Enable the apply button
530 void KMFilterDlg::slotStopProcessingButtonToggled( bool aChecked
)
533 mFilter
->setStopProcessingHere( aChecked
);
535 // Enable the apply button
540 void KMFilterDlg::slotConfigureShortcutButtonToggled( bool aChecked
)
543 mFilter
->setConfigureShortcut( aChecked
);
544 mKeySeqWidget
->setEnabled( aChecked
);
545 mConfigureToolbar
->setEnabled( aChecked
);
546 mFilterActionIconButton
->setEnabled( aChecked
);
547 mFilterActionLabel
->setEnabled( aChecked
);
549 // Enable the apply button
554 void KMFilterDlg::slotShortcutChanged( const QKeySequence
&newSeq
)
557 mKeySeqWidget
->applyStealShortcut();
558 mFilter
->setShortcut( KShortcut( newSeq
) );
560 // Enable the apply button
565 void KMFilterDlg::slotConfigureToolbarButtonToggled( bool aChecked
)
568 mFilter
->setConfigureToolbar( aChecked
);
571 void KMFilterDlg::slotFilterActionIconChanged( const QString
&icon
)
574 mFilter
->setIcon( icon
);
577 void KMFilterDlg::slotUpdateAccountList()
579 mAccountList
->clear();
581 QTreeWidgetItem
*top
= 0;
582 // Block the signals here, otherwise we end up calling
583 // slotApplicableAccountsChanged(), which will read the incomplete item
584 // state and write that back to the filter
585 mAccountList
->blockSignals( true );
586 const Akonadi::AgentInstance::List lst
= MailCommon::Util::agentInstances();
587 const int nbAccount
= lst
.count();
588 for ( int i
= 0; i
<nbAccount
; ++i
) {
589 if( lst
.at( i
).type().identifier() == QLatin1String( "akonadi_nepomuktag_resource" ) )
591 QTreeWidgetItem
*listItem
= new QTreeWidgetItem( mAccountList
, top
);
592 listItem
->setText( 0, lst
.at( i
).name() );
593 listItem
->setText( 1, lst
.at( i
).type().name() );
594 listItem
->setText( 2, QString( "%1" ).arg( lst
.at( i
).identifier() ) );
596 listItem
->setCheckState( 0, mFilter
->applyOnAccount( lst
.at( i
).identifier() ) ?
597 Qt::Checked
: Qt::Unchecked
);
600 mAccountList
->blockSignals( false );
602 // make sure our hidden column is really hidden (Qt tends to re-show it)
603 mAccountList
->hideColumn( 2 );
604 mAccountList
->resizeColumnToContents( 0 );
605 mAccountList
->resizeColumnToContents( 1 );
607 top
= mAccountList
->topLevelItem( 0 );
609 mAccountList
->setCurrentItem( top
);
613 //=============================================================================
615 // class KMFilterListBox (the filter list manipulator)
617 //=============================================================================
619 KMFilterListBox::KMFilterListBox( const QString
& title
, QWidget
*parent
)
620 : QGroupBox( title
, parent
)
622 QVBoxLayout
*layout
= new QVBoxLayout();
626 //----------- the list box
627 mListWidget
= new QListWidget(this);
628 mListWidget
->setMinimumWidth(150);
629 mListWidget
->setWhatsThis( i18n(_wt_filterlist
) );
631 KListWidgetSearchLine
* mSearchListWidget
= new KListWidgetSearchLine( this, mListWidget
);
632 mSearchListWidget
->setClickMessage( i18nc( "@info/plain Displayed grayed-out inside the "
633 "textbox, verb to search", "Search" ) );
635 layout
->addWidget( mSearchListWidget
);
636 layout
->addWidget( mListWidget
);
638 //----------- the first row of buttons
639 KHBox
*hb
= new KHBox(this);
641 mBtnUp
= new KPushButton( QString(), hb
);
642 mBtnUp
->setAutoRepeat( true );
643 mBtnUp
->setIcon( KIcon( "go-up" ) );
644 mBtnUp
->setIconSize( QSize( KIconLoader::SizeSmall
, KIconLoader::SizeSmall
) );
645 mBtnUp
->setMinimumSize( mBtnUp
->sizeHint() * 1.2 );
646 mBtnDown
= new KPushButton( QString(), hb
);
647 mBtnDown
->setAutoRepeat( true );
648 mBtnDown
->setIcon( KIcon( "go-down" ) );
649 mBtnDown
->setIconSize( QSize( KIconLoader::SizeSmall
, KIconLoader::SizeSmall
) );
650 mBtnDown
->setMinimumSize( mBtnDown
->sizeHint() * 1.2 );
651 mBtnUp
->setToolTip( i18nc("Move selected filter up.", "Up") );
652 mBtnDown
->setToolTip( i18nc("Move selected filter down.", "Down") );
653 mBtnUp
->setWhatsThis( i18n(_wt_filterlist_up
) );
654 mBtnDown
->setWhatsThis( i18n(_wt_filterlist_down
) );
656 layout
->addWidget( hb
);
658 //----------- the second row of buttons
659 hb
= new KHBox(this);
661 mBtnNew
= new QPushButton( QString(), hb
);
662 mBtnNew
->setIcon( KIcon( "document-new" ) );
663 mBtnNew
->setIconSize( QSize( KIconLoader::SizeSmall
, KIconLoader::SizeSmall
) );
664 mBtnNew
->setMinimumSize( mBtnNew
->sizeHint() * 1.2 );
665 mBtnCopy
= new QPushButton( QString(), hb
);
666 mBtnCopy
->setIcon( KIcon( "edit-copy" ) );
667 mBtnCopy
->setIconSize( QSize( KIconLoader::SizeSmall
, KIconLoader::SizeSmall
) );
668 mBtnCopy
->setMinimumSize( mBtnCopy
->sizeHint() * 1.2 );
669 mBtnDelete
= new QPushButton( QString(), hb
);
670 mBtnDelete
->setIcon( KIcon( "edit-delete" ) );
671 mBtnDelete
->setIconSize( QSize( KIconLoader::SizeSmall
, KIconLoader::SizeSmall
) );
672 mBtnDelete
->setMinimumSize( mBtnDelete
->sizeHint() * 1.2 );
673 mBtnRename
= new QPushButton( i18n("Rename..."), hb
);
674 mBtnNew
->setToolTip( i18nc("@action:button in filter list manipulator", "New") );
675 mBtnCopy
->setToolTip( i18n("Copy") );
676 mBtnDelete
->setToolTip( i18n("Delete"));
677 mBtnNew
->setWhatsThis( i18n(_wt_filterlist_new
) );
678 mBtnCopy
->setWhatsThis( i18n(_wt_filterlist_copy
) );
679 mBtnDelete
->setWhatsThis( i18n(_wt_filterlist_delete
) );
680 mBtnRename
->setWhatsThis( i18n(_wt_filterlist_rename
) );
682 layout
->addWidget( hb
);
685 //----------- now connect everything
686 connect( mListWidget
, SIGNAL(currentRowChanged(int)),
687 this, SLOT(slotSelected(int)) );
688 connect( mListWidget
, SIGNAL(itemDoubleClicked(QListWidgetItem
*)),
689 this, SLOT(slotRename()) );
690 connect( mBtnUp
, SIGNAL(clicked()),
691 this, SLOT(slotUp()) );
692 connect( mBtnDown
, SIGNAL(clicked()),
693 this, SLOT(slotDown()) );
694 connect( mBtnNew
, SIGNAL(clicked()),
695 this, SLOT(slotNew()) );
696 connect( mBtnCopy
, SIGNAL(clicked()),
697 this, SLOT(slotCopy()) );
698 connect( mBtnDelete
, SIGNAL(clicked()),
699 this, SLOT(slotDelete()) );
700 connect( mBtnRename
, SIGNAL(clicked()),
701 this, SLOT(slotRename()) );
703 // the dialog should call loadFilterList()
704 // when all signals are connected.
709 KMFilterListBox::~KMFilterListBox()
711 qDeleteAll( mFilterList
);
715 void KMFilterListBox::createFilter( const QByteArray
& field
,
716 const QString
& value
)
718 SearchRule::Ptr newRule
= SearchRule::createInstance( field
, SearchRule::FuncContains
, value
);
720 MailFilter
*newFilter
= new MailFilter();
721 newFilter
->pattern()->append( newRule
);
722 newFilter
->pattern()->setName( QString("<%1>:%2").arg( QString::fromLatin1( field
) ).arg( value
) );
724 FilterActionDesc
*desc
= kmkernel
->filterActionDict()->value( "transfer" );
726 newFilter
->actions()->append( desc
->create() );
728 insertFilter( newFilter
);
732 void KMFilterListBox::slotUpdateFilterName()
734 if ( mIdxSelItem
< 0 ) {
735 kDebug() << "Called while no filter is selected, ignoring. idx=" << mIdxSelItem
;
739 SearchPattern
*p
= mFilterList
.at(mIdxSelItem
)->pattern();
742 QString shouldBeName
= p
->name();
743 QString displayedName
= mListWidget
->item( mIdxSelItem
)->text();
745 if ( shouldBeName
.trimmed().isEmpty() ) {
746 mFilterList
.at(mIdxSelItem
)->setAutoNaming( true );
749 if ( mFilterList
.at(mIdxSelItem
)->isAutoNaming() ) {
750 // auto-naming of patterns
751 if ( !p
->isEmpty() && p
->first() && !p
->first()->field().trimmed().isEmpty() )
752 shouldBeName
= QString( "<%1>: %2" ).arg( QString::fromLatin1( p
->first()->field() ) ).arg( p
->first()->contents() );
754 shouldBeName
= '<' + i18n("unnamed") + '>';
755 p
->setName( shouldBeName
);
758 if ( displayedName
== shouldBeName
) return;
760 mListWidget
->blockSignals(true);
761 mListWidget
->item( mIdxSelItem
)->setText( shouldBeName
);
762 mListWidget
->blockSignals(false);
765 void KMFilterListBox::slotApplyFilterChanges( KDialog::ButtonCode button
)
767 bool closeAfterSaving
;
768 if ( button
== KDialog::Ok
)
769 closeAfterSaving
= true;
770 else if ( button
== KDialog::Apply
)
771 closeAfterSaving
= false;
773 return; // ignore close and cancel
775 if ( mIdxSelItem
>= 0 ) {
777 slotSelected( mListWidget
->currentRow() );
780 // by now all edit widgets should have written back
781 // their widget's data into our filter list.
783 FilterManager
*fm
= kmkernel
->filterManager();
785 const QList
<MailFilter
*> newFilters
= filtersForSaving( closeAfterSaving
);
787 fm
->setFilters( newFilters
);
790 QList
<MailFilter
*> KMFilterListBox::filtersForSaving( bool closeAfterSaving
) const
792 const_cast<KMFilterListBox
*>( this )->applyWidgets(); // signals aren't const
793 QList
<MailFilter
*> filters
;
794 QStringList emptyFilters
;
795 foreach ( MailFilter
*const it
, mFilterList
) {
796 MailFilter
*f
= new MailFilter( *it
); // deep copy
799 // the filter is valid:
802 // the filter is invalid:
803 emptyFilters
<< f
->name();
808 // report on invalid filters:
809 if ( !emptyFilters
.empty() ) {
810 if ( closeAfterSaving
) {
811 // Ok clicked. Give option to continue editing
812 int response
= KMessageBox::warningContinueCancelList(
814 i18n( "The following filters are invalid (e.g. containing no actions "
815 "or no search rules). Discard or edit invalid filters?" ),
818 KGuiItem( i18n( "Discard" ) ),
819 KStandardGuiItem::cancel(),
820 "ShowInvalidFilterWarning" );
821 if ( response
== KMessageBox::Cancel
)
824 // Apply clicked. Just warn.
825 KMessageBox::informationList(
827 i18n( "The following filters have not been saved because they were invalid "
828 "(e.g. containing no actions or no search rules)." ),
831 "ShowInvalidFilterWarning" );
837 void KMFilterListBox::slotSelected( int aIdx
)
839 kDebug() << "idx=" << aIdx
;
842 if ( mIdxSelItem
>= 0 && mIdxSelItem
< mFilterList
.count() ) {
843 MailFilter
*f
= mFilterList
.at(aIdx
);
845 emit
filterSelected( f
);
854 void KMFilterListBox::slotNew()
856 // just insert a new filter.
857 insertFilter( new MailFilter() );
861 void KMFilterListBox::slotCopy()
863 if ( mIdxSelItem
< 0 ) {
864 kDebug() << "Called while no filter is selected, ignoring.";
868 // make sure that all changes are written to the filter before we copy it
871 MailFilter
*filter
= mFilterList
.at( mIdxSelItem
);
873 // enableControls should make sure this method is
874 // never called when no filter is selected.
877 // inserts a copy of the current filter.
878 insertFilter( new MailFilter( *filter
) );
882 void KMFilterListBox::slotDelete()
884 if ( mIdxSelItem
< 0 ) {
885 kDebug() << "Called while no filter is selected, ignoring.";
889 int oIdxSelItem
= mIdxSelItem
;
892 // TODO remove this line: mListWidget->clearSelection();
893 // broadcast that all widgets let go
897 // remove the filter from both the listbox
898 mListWidget
->takeItem( oIdxSelItem
);
899 // and the filter list...
900 MailCommon::MailFilter
*deletedFilter
= mFilterList
.takeAt( oIdxSelItem
);
903 int count
= mListWidget
->count();
904 // and set the new current item.
905 if ( count
> oIdxSelItem
)
906 // oIdxItem is still a valid index
907 mListWidget
->setCurrentRow( oIdxSelItem
);
909 // oIdxSelIdx is no longer valid, but the
910 // list box isn't empty
911 mListWidget
->setCurrentRow( count
- 1 );
913 // work around a problem when deleting the first item in a QListWidget:
914 // after takeItem, slotSelectionChanged is emitted with 1, but the row 0
915 // remains selected and another selectCurrentRow(0) does not trigger the
916 // selectionChanged signal
917 // (qt-copy as of 2006-12-22 / gungl)
918 if ( oIdxSelItem
== 0 )
921 mIdxSelItem
= mListWidget
->currentRow();
924 emit
filterRemoved( deletedFilter
);
927 void KMFilterListBox::slotUp()
929 if ( mIdxSelItem
< 0 ) {
930 kDebug() << "Called while no filter is selected, ignoring.";
933 if ( mIdxSelItem
== 0 ) {
934 kDebug() << "Called while the _topmost_ filter is selected, ignoring.";
938 swapNeighbouringFilters( mIdxSelItem
, mIdxSelItem
- 1 );
941 emit
filterOrderAltered();
944 void KMFilterListBox::slotDown()
946 if ( mIdxSelItem
< 0 ) {
947 kDebug() << "Called while no filter is selected, ignoring.";
950 if ( mIdxSelItem
== (int)mListWidget
->count() - 1 ) {
951 kDebug() << "Called while the _last_ filter is selected, ignoring.";
955 swapNeighbouringFilters( mIdxSelItem
, mIdxSelItem
+ 1);
958 emit
filterOrderAltered();
961 void KMFilterListBox::slotRename()
963 if ( mIdxSelItem
< 0 ) {
964 kDebug() << "Called while no filter is selected, ignoring.";
968 bool okPressed
= false;
969 MailFilter
*filter
= mFilterList
.at( mIdxSelItem
);
971 // enableControls should make sure this method is
972 // never called when no filter is selected.
975 // allow empty names - those will turn auto-naming on again
976 QValidator
*validator
= new QRegExpValidator( QRegExp( ".*" ), 0 );
977 QString newName
= KInputDialog::getText
979 i18n("Rename Filter"),
980 i18n("Rename filter \"%1\" to:\n(leave the field empty for automatic naming)",
981 filter
->pattern()->name() ) /*label*/,
982 filter
->pattern()->name() /* initial value */,
983 &okPressed
, window(), validator
987 if ( !okPressed
) return;
989 if ( newName
.isEmpty() ) {
990 // bait for slotUpdateFilterName to
991 // use automatic naming again.
992 filter
->pattern()->setName( "<>" );
993 filter
->setAutoNaming( true );
995 filter
->pattern()->setName( newName
);
996 filter
->setAutoNaming( false );
999 slotUpdateFilterName();
1001 emit
filterUpdated( filter
);
1004 void KMFilterListBox::enableControls()
1006 bool theFirst
= ( mIdxSelItem
== 0 );
1007 bool theLast
= ( mIdxSelItem
>= (int)mFilterList
.count() - 1 );
1008 bool aFilterIsSelected
= ( mIdxSelItem
>= 0 );
1010 mBtnUp
->setEnabled( aFilterIsSelected
&& !theFirst
);
1011 mBtnDown
->setEnabled( aFilterIsSelected
&& !theLast
);
1012 mBtnCopy
->setEnabled( aFilterIsSelected
);
1013 mBtnDelete
->setEnabled( aFilterIsSelected
);
1014 mBtnRename
->setEnabled( aFilterIsSelected
);
1016 if ( aFilterIsSelected
)
1017 mListWidget
->scrollToItem( mListWidget
->currentItem() );
1020 void KMFilterListBox::loadFilterList( bool createDummyFilter
)
1022 assert(mListWidget
);
1024 emit
resetWidgets();
1025 // we don't want the insertion to
1026 // cause flicker in the edit widgets.
1030 mFilterList
.clear();
1031 mListWidget
->clear();
1033 const FilterManager
*manager
= kmkernel
->filterManager();
1034 Q_ASSERT( manager
);
1036 QList
<MailFilter
*>::const_iterator it
;
1037 for ( it
= manager
->filters().constBegin() ;
1038 it
!= manager
->filters().constEnd();
1040 mFilterList
.append( new MailFilter( **it
) ); // deep copy
1041 mListWidget
->addItem( (*it
)->pattern()->name() );
1044 blockSignals(false);
1047 // create an empty filter when there's none, to avoid a completely
1048 // disabled dialog (usability tests indicated that the new-filter
1049 // button is too hard to find that way):
1050 if ( !mListWidget
->count() && createDummyFilter
)
1053 if ( mListWidget
->count() > 0 )
1054 mListWidget
->setCurrentRow( 0 );
1059 void KMFilterListBox::insertFilter( MailFilter
* aFilter
)
1061 // must be really a filter...
1064 // if mIdxSelItem < 0, QListBox::insertItem will append.
1065 mListWidget
->insertItem( mIdxSelItem
, aFilter
->pattern()->name() );
1066 if ( mIdxSelItem
< 0 ) {
1067 // none selected -> append
1068 mFilterList
.append( aFilter
);
1069 mListWidget
->setCurrentRow( mListWidget
->count() - 1 );
1071 // insert just before selected
1072 mFilterList
.insert( mIdxSelItem
, aFilter
);
1073 mListWidget
->setCurrentRow( mIdxSelItem
);
1076 emit
filterCreated();
1077 emit
filterOrderAltered();
1080 void KMFilterListBox::appendFilter( MailFilter
* aFilter
)
1082 mFilterList
.append( aFilter
);
1083 mListWidget
->addItems( QStringList( aFilter
->pattern()->name() ) );
1085 emit
filterCreated();
1088 void KMFilterListBox::swapNeighbouringFilters( int untouchedOne
, int movedOne
)
1090 // must be neighbours...
1091 assert( untouchedOne
- movedOne
== 1 || movedOne
- untouchedOne
== 1 );
1093 // untouchedOne is at idx. to move it down(up),
1094 // remove item at idx+(-)1 w/o deleting it.
1095 QListWidgetItem
*item
= mListWidget
->item( movedOne
);
1096 mListWidget
->takeItem( movedOne
);
1097 // now selected item is at idx(idx-1), so
1098 // insert the other item at idx, ie. above(below).
1099 mListWidget
->insertItem( untouchedOne
, item
);
1101 MailFilter
* filter
= mFilterList
.takeAt( movedOne
);
1102 mFilterList
.insert( untouchedOne
, filter
);
1104 mIdxSelItem
+= movedOne
- untouchedOne
;
1108 void KMFilterDlg::slotImportFilters()
1110 FilterImporterExporter
importer( this );
1111 QList
<MailFilter
*> filters
= importer
.importFilters();
1113 // FIXME message box how many were imported?
1114 if ( filters
.isEmpty() ) return;
1116 QList
<MailFilter
*>::ConstIterator it
;
1118 for ( it
= filters
.constBegin() ; it
!= filters
.constEnd() ; ++it
) {
1119 mFilterList
->appendFilter( *it
); // no need to deep copy, ownership passes to the list
1123 void KMFilterDlg::slotExportFilters()
1125 FilterImporterExporter
exporter( this );
1126 QList
<MailFilter
*> filters
= mFilterList
->filtersForSaving( false );
1127 exporter
.exportFilters( filters
);
1128 QList
<MailFilter
*>::ConstIterator it
;
1129 for ( it
= filters
.constBegin(); it
!= filters
.constEnd(); ++it
)
1133 void KMFilterDlg::slotDisableAccept()
1138 void KMFilterDlg::slotDialogUpdated()
1140 kDebug() << "Detected a change in data bound to the dialog!";
1141 if ( !mIgnoreFilterUpdates
) {
1142 enableButtonApply( true );
1147 #include "kmfilterdlg.moc"