kmail

kmfilterdlg.cpp
1 // -*- mode: C++; c-file-style: "gnu" -*-
2 // kmfilterdlg.cpp
3 // Author: Marc Mutz <Marc@Mutz.com>
4 // based on work by Stefan Taferner <taferner@kde.org>
5 // This code is under the GPL
6 
7 #include <config.h>
8 #include "kmfilterdlg.h"
9 
10 // other KMail headers:
11 #include "kmsearchpatternedit.h"
12 #include "kmfiltermgr.h"
13 #include "kmmainwidget.h"
14 #include "accountmanager.h"
16 #include "filterimporterexporter.h"
18 #include "foldersetselector.h"
19 #include "globalsettings.h"
20 
21 // other KDE headers:
22 #include <kmessagebox.h>
23 #include <kdebug.h>
24 #include <klocale.h>
25 #include <kinputdialog.h>
26 #include <kiconloader.h>
27 #include <kapplication.h>
28 #include <kwin.h>
29 #include <kconfig.h>
30 #include <kicondialog.h>
31 #include <kkeybutton.h>
32 #include <klistview.h>
33 #include <kpushbutton.h>
34 
35 // other TQt headers:
36 #include <tqlayout.h>
37 #include <tqlabel.h>
38 #include <tqcombobox.h>
39 #include <tqwidgetstack.h>
40 #include <tqtooltip.h>
41 #include <tqwhatsthis.h>
42 #include <tqcheckbox.h>
43 #include <tqhbox.h>
44 #include <tqvalidator.h>
45 #include <tqtabwidget.h>
46 
47 // other headers:
48 #include <assert.h>
49 
50 using namespace KMail;
51 
52 
53 // What's this help texts
54 const char * _wt_filterlist =
55 I18N_NOOP( "<qt><p>This is the list of defined filters. "
56  "They are processed top-to-bottom.</p>"
57  "<p>Click on any filter to edit it "
58  "using the controls in the right-hand half "
59  "of the dialog.</p></qt>" );
60 const char * _wt_filterlist_new =
61 I18N_NOOP( "<qt><p>Click this button to create a new filter.</p>"
62  "<p>The filter will be inserted just before the currently-"
63  "selected one, but you can always change that "
64  "later on.</p>"
65  "<p>If you have clicked this button accidentally, you can undo this "
66  "by clicking on the <em>Delete</em> button.</p></qt>" );
67 const char * _wt_filterlist_copy =
68 I18N_NOOP( "<qt><p>Click this button to copy a filter.</p>"
69  "<p>If you have clicked this button accidentally, you can undo this "
70  "by clicking on the <em>Delete</em> button.</p></qt>" );
71 const char * _wt_filterlist_delete =
72 I18N_NOOP( "<qt><p>Click this button to <em>delete</em> the currently-"
73  "selected filter from the list above.</p>"
74  "<p>There is no way to get the filter back once "
75  "it is deleted, but you can always leave the "
76  "dialog by clicking <em>Cancel</em> to discard the "
77  "changes made.</p></qt>" );
78 const char * _wt_filterlist_top =
79 I18N_NOOP( "<qt><p>Click this button to move the currently-"
80  "selected filter to the <em>top</em> of the list above.</p>"
81  "<p>This is useful since the order of the filters in the list "
82  "determines the order in which they are tried on messages: "
83  "The topmost filter gets tried first.</p></qt>" );
84 const char * _wt_filterlist_up =
85 I18N_NOOP( "<qt><p>Click this button to move the currently-"
86  "selected filter <em>up</em> one in the list above.</p>"
87  "<p>This is useful since the order of the filters in the list "
88  "determines the order in which they are tried on messages: "
89  "The topmost filter gets tried first.</p>"
90  "<p>If you have clicked this button accidentally, you can undo this "
91  "by clicking on the <em>Down</em> button.</p></qt>" );
92 const char * _wt_filterlist_down =
93 I18N_NOOP( "<qt><p>Click this button to move the currently-"
94  "selected filter <em>down</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>Up</em> button.</p></qt>" );
100 const char * _wt_filterlist_bot =
101 I18N_NOOP( "<qt><p>Click this button to move the currently-"
102  "selected filter to the <em>bottom</em> of 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></qt>" );
106 const char * _wt_filterlist_rename =
107 I18N_NOOP( "<qt><p>Click this button to rename the currently-selected filter.</p>"
108  "<p>Filters are named automatically, as long as they start with "
109  "\"&lt;\".</p>"
110  "<p>If you have renamed a filter accidentally and want automatic "
111  "naming back, click this button and select <em>Clear</em> followed "
112  "by <em>OK</em> in the appearing dialog.</p></qt>" );
113 const char * _wt_filterdlg_showLater =
114 I18N_NOOP( "<qt><p>Check this button to force the confirmation dialog to be "
115  "displayed.</p><p>This is useful if you have defined a ruleset that tags "
116  "messages to be downloaded later. Without the possibility to force "
117  "the dialog popup, these messages could never be downloaded if no "
118  "other large messages were waiting on the server, or if you wanted to "
119  "change the ruleset to tag the messages differently.</p></qt>" );
120 
121 // The anchor of the filter dialog's help.
122 const char * KMFilterDlgHelpAnchor = "filters-id" ;
123 const char * KMPopFilterDlgHelpAnchor = "popfilters-id" ;
124 
125 //=============================================================================
126 //
127 // class KMFilterDlg (the filter dialog)
128 //
129 //=============================================================================
130 
131 KMFilterDlg::KMFilterDlg(TQWidget* parent, const char* name, bool popFilter, bool createDummyFilter )
132  : KDialogBase( parent, name, false /* modality */,
133  (popFilter)? i18n("POP3 Filter Rules"): i18n("Filter Rules") /* caption*/,
134  Help|Ok|Apply|Cancel|User1|User2 /* button mask */,
135  Ok /* default btn */, false /* separator */),
136  bPopFilter(popFilter)
137 {
138  KWin::setIcons( winId(), kapp->icon(), kapp->miniIcon() );
139  setHelp( (bPopFilter)? KMPopFilterDlgHelpAnchor: KMFilterDlgHelpAnchor );
140  setButtonText( User1, i18n("Import") );
141  setButtonText( User2, i18n("Export") );
142  connect( this, TQT_SIGNAL(user1Clicked()),
143  this, TQT_SLOT( slotImportFilters()) );
144  connect( this, TQT_SIGNAL(user2Clicked()),
145  this, TQT_SLOT( slotExportFilters()) );
146 
147  TQWidget *w = new TQWidget( this );
148  setMainWidget( w );
149  TQHBoxLayout *topLayout = new TQHBoxLayout( w, 0, spacingHint(), "topLayout" );
150  TQHBoxLayout *hbl = topLayout;
151  TQVBoxLayout *vbl2 = 0;
152  TQWidget *page1 = 0;
153  TQWidget *page2 = 0;
154 
155  mFilterList = new KMFilterListBox( i18n("Available Filters"), w, 0, bPopFilter);
156  topLayout->addWidget( mFilterList, 1 /*stretch*/ );
157 
158  if(!bPopFilter) {
159  TQTabWidget *tabWidget = new TQTabWidget( w, "kmfd_tab" );
160  tabWidget->setMargin( KDialog::marginHint() );
161  topLayout->addWidget( tabWidget );
162 
163  page1 = new TQWidget( tabWidget );
164  tabWidget->addTab( page1, i18n("&General") );
165  hbl = new TQHBoxLayout( page1, 0, spacingHint(), "kmfd_hbl" );
166 
167  page2 = new TQWidget( tabWidget );
168  tabWidget->addTab( page2, i18n("A&dvanced") );
169  vbl2 = new TQVBoxLayout( page2, 0, spacingHint(), "kmfd_vbl2" );
170  }
171 
172  TQVBoxLayout *vbl = new TQVBoxLayout( hbl, spacingHint(), "kmfd_vbl" );
173  hbl->setStretchFactor( vbl, 2 );
174 
175  mPatternEdit = new KMSearchPatternEdit( i18n("Filter Criteria"), bPopFilter ? w : page1 , "spe", bPopFilter);
176  vbl->addWidget( mPatternEdit, 0, TQt::AlignTop );
177 
178  if(bPopFilter){
179  mActionGroup = new KMPopFilterActionWidget( i18n("Filter Action"), w );
180  vbl->addWidget( mActionGroup, 0, TQt::AlignTop );
181 
182  mGlobalsBox = new TQVGroupBox(i18n("Global Options"), w);
183  mShowLaterBtn = new TQCheckBox(i18n("Always &show matched 'Download Later' messages in confirmation dialog"), mGlobalsBox);
184  TQWhatsThis::add( mShowLaterBtn, i18n(_wt_filterdlg_showLater) );
185  vbl->addWidget( mGlobalsBox, 0, TQt::AlignTop );
186  }
187  else {
188  TQGroupBox *agb = new TQGroupBox( 1 /*column*/, Qt::Vertical, i18n("Filter Actions"), page1 );
189  mActionLister = new KMFilterActionWidgetLister( agb );
190  vbl->addWidget( agb, 0, TQt::AlignTop );
191 
192  mAdvOptsGroup = new TQGroupBox ( 1 /*columns*/, Qt::Vertical,
193  i18n("Advanced Options"), page2);
194  {
195  TQWidget *adv_w = new TQWidget( mAdvOptsGroup );
196  TQGridLayout *gl = new TQGridLayout( adv_w, 8 /*rows*/, 3 /*cols*/,
197  0 /*border*/, spacingHint() );
198 
199  TQVBoxLayout *vbl3 = new TQVBoxLayout( gl, spacingHint(), "vbl3" );
200  vbl3->addStretch( 1 );
201  mApplyOnIn = new TQCheckBox( i18n("Apply this filter to incoming messages:"), adv_w );
202  vbl3->addWidget( mApplyOnIn );
203  TQButtonGroup *bg = new TQButtonGroup( 0, "bg" );
204  bg->setExclusive( true );
205  mApplyOnForAll = new TQRadioButton( i18n("from all accounts"), adv_w );
206  bg->insert( mApplyOnForAll );
207  vbl3->addWidget( mApplyOnForAll );
208  mApplyOnForTraditional = new TQRadioButton( i18n("from all but online IMAP accounts"), adv_w );
209  bg->insert( mApplyOnForTraditional );
210  vbl3->addWidget( mApplyOnForTraditional );
211  mApplyOnForChecked = new TQRadioButton( i18n("from checked accounts only"), adv_w );
212  bg->insert( mApplyOnForChecked );
213  vbl3->addWidget( mApplyOnForChecked );
214  vbl3->addStretch( 2 );
215 
216  mAccountList = new KListView( adv_w, "accountList" );
217  mAccountList->addColumn( i18n("Account Name") );
218  mAccountList->addColumn( i18n("Type") );
219  mAccountList->setAllColumnsShowFocus( true );
220  mAccountList->setFrameStyle( TQFrame::WinPanel + TQFrame::Sunken );
221  mAccountList->setSorting( -1 );
222  gl->addMultiCellWidget( mAccountList, 0, 3, 1, 3 );
223 
224  mApplyOnOut = new TQCheckBox( i18n("Apply this filter to &sent messages"), adv_w );
225  gl->addMultiCellWidget( mApplyOnOut, 4, 4, 0, 3 );
226 
227  mApplyOnCtrlJ = new TQCheckBox( i18n("Apply this filter on manual &filtering"), adv_w );
228  gl->addMultiCellWidget( mApplyOnCtrlJ, 5, 5, 0, 3 );
229 
230  mStopProcessingHere = new TQCheckBox( i18n("If this filter &matches, stop processing here"), adv_w );
231  gl->addMultiCellWidget( mStopProcessingHere,
232  6, 6, /*from to row*/
233  0, 3 /*from to col*/ );
234  mConfigureShortcut = new TQCheckBox( i18n("Add this filter to the Apply Filter menu"), adv_w );
235  gl->addMultiCellWidget( mConfigureShortcut, 7, 7, 0, 1 );
236  TQLabel *keyButtonLabel = new TQLabel( i18n( "Shortcut:" ), adv_w );
237  keyButtonLabel->setAlignment( AlignVCenter | AlignRight );
238  gl->addMultiCellWidget( keyButtonLabel, 7, 7, 2, 2 );
239  mKeyButton = new KKeyButton( adv_w, "FilterShortcutSelector" );
240  gl->addMultiCellWidget( mKeyButton, 7, 7, 3, 3 );
241  mKeyButton->setEnabled( false );
242  mConfigureToolbar = new TQCheckBox( i18n("Additionally add this filter to the toolbar"), adv_w );
243  gl->addMultiCellWidget( mConfigureToolbar, 8, 8, 0, 3 );
244  mConfigureToolbar->setEnabled( false );
245 
246  TQHBox *hbox = new TQHBox( adv_w );
247  mFilterActionLabel = new TQLabel( i18n( "Icon for this filter:" ),
248  hbox );
249  mFilterActionLabel->setEnabled( false );
250 
251  mFilterActionIconButton = new KIconButton( hbox );
252  mFilterActionLabel->setBuddy( mFilterActionIconButton );
253  mFilterActionIconButton->setIconType( KIcon::NoGroup, KIcon::Any, true );
254  mFilterActionIconButton->setIconSize( 16 );
255  mFilterActionIconButton->setIcon( "gear" );
256  mFilterActionIconButton->setEnabled( false );
257 
258  gl->addMultiCellWidget( hbox, 9, 9, 0, 3 );
259  }
260  vbl2->addWidget( mAdvOptsGroup, 0, TQt::AlignTop );
261  }
262  // spacer:
263  vbl->addStretch( 1 );
264 
265  // load the filter parts into the edit widgets
266  connect( mFilterList, TQT_SIGNAL(filterSelected(KMFilter*)),
267  this, TQT_SLOT(slotFilterSelected(KMFilter*)) );
268 
269  if (bPopFilter){
270  // set the state of the global setting 'show later msgs'
271  connect( mShowLaterBtn, TQT_SIGNAL(toggled(bool)),
272  mFilterList, TQT_SLOT(slotShowLaterToggled(bool)));
273 
274  // set the action in the filter when changed
275  connect( mActionGroup, TQT_SIGNAL(actionChanged(const KMPopFilterAction)),
276  this, TQT_SLOT(slotActionChanged(const KMPopFilterAction)) );
277  } else {
278  // transfer changes from the 'Apply this filter on...'
279  // combo box to the filter
280  connect( mApplyOnIn, TQT_SIGNAL(clicked()),
281  this, TQT_SLOT(slotApplicabilityChanged()) );
282  connect( mApplyOnForAll, TQT_SIGNAL(clicked()),
283  this, TQT_SLOT(slotApplicabilityChanged()) );
284  connect( mApplyOnForTraditional, TQT_SIGNAL(clicked()),
285  this, TQT_SLOT(slotApplicabilityChanged()) );
286  connect( mApplyOnForChecked, TQT_SIGNAL(clicked()),
287  this, TQT_SLOT(slotApplicabilityChanged()) );
288  connect( mApplyOnOut, TQT_SIGNAL(clicked()),
289  this, TQT_SLOT(slotApplicabilityChanged()) );
290  connect( mApplyOnCtrlJ, TQT_SIGNAL(clicked()),
291  this, TQT_SLOT(slotApplicabilityChanged()) );
292  connect( mAccountList, TQT_SIGNAL(clicked(TQListViewItem*)),
293  this, TQT_SLOT(slotApplicableAccountsChanged()) );
294  connect( mAccountList, TQT_SIGNAL(spacePressed(TQListViewItem*)),
295  this, TQT_SLOT(slotApplicableAccountsChanged()) );
296 
297  // transfer changes from the 'stop processing here'
298  // check box to the filter
299  connect( mStopProcessingHere, TQT_SIGNAL(toggled(bool)),
300  this, TQT_SLOT(slotStopProcessingButtonToggled(bool)) );
301 
302  connect( mConfigureShortcut, TQT_SIGNAL(toggled(bool)),
303  this, TQT_SLOT(slotConfigureShortcutButtonToggled(bool)) );
304 
305  connect( mKeyButton, TQT_SIGNAL( capturedShortcut( const KShortcut& ) ),
306  this, TQT_SLOT( slotCapturedShortcutChanged( const KShortcut& ) ) );
307 
308  connect( mConfigureToolbar, TQT_SIGNAL(toggled(bool)),
309  this, TQT_SLOT(slotConfigureToolbarButtonToggled(bool)) );
310 
311  connect( mFilterActionIconButton, TQT_SIGNAL( iconChanged( TQString ) ),
312  this, TQT_SLOT( slotFilterActionIconChanged( TQString ) ) );
313  }
314 
315  // reset all widgets here
316  connect( mFilterList, TQT_SIGNAL(resetWidgets()),
317  this, TQT_SLOT(slotReset()) );
318 
319  connect( mFilterList, TQT_SIGNAL( applyWidgets() ),
320  this, TQT_SLOT( slotUpdateFilter() ) );
321 
322  // support auto-naming the filter
323  connect( mPatternEdit, TQT_SIGNAL(maybeNameChanged()),
324  mFilterList, TQT_SLOT(slotUpdateFilterName()) );
325 
326  // apply changes on 'Apply'
327  connect( this, TQT_SIGNAL(applyClicked()),
328  mFilterList, TQT_SLOT(slotApplyFilterChanges()) );
329 
330  // apply changes on 'OK'
331  connect( this, TQT_SIGNAL(okClicked()),
332  mFilterList, TQT_SLOT(slotApplyFilterChanges()) );
333 
334  // save dialog size on 'OK'
335  connect( this, TQT_SIGNAL(okClicked()),
336  this, TQT_SLOT(slotSaveSize()) );
337 
338  // destruct the dialog on OK, close and Cancel
339  connect( this, TQT_SIGNAL(finished()),
340  this, TQT_SLOT(slotFinished()) );
341 
342  KConfigGroup geometry( KMKernel::config(), "Geometry");
343  const char * configKey
344  = bPopFilter ? "popFilterDialogSize" : "filterDialogSize";
345  if ( geometry.hasKey( configKey ) )
346  resize( geometry.readSizeEntry( configKey ) );
347  else
348  adjustSize();
349 
350  // load the filter list (emits filterSelected())
351  mFilterList->loadFilterList( createDummyFilter );
352 }
353 
354 void KMFilterDlg::slotFinished() {
355  delayedDestruct();
356 }
357 
358 void KMFilterDlg::slotSaveSize() {
359  KConfigGroup geometry( KMKernel::config(), "Geometry" );
360  geometry.writeEntry( bPopFilter ? "popFilterDialogSize" : "filterDialogSize", size() );
361 }
362 
364 void KMFilterDlg::slotActionChanged(const KMPopFilterAction aAction)
365 {
366  mFilter->setAction(aAction);
367 }
368 
369 void KMFilterDlg::slotFilterSelected( KMFilter* aFilter )
370 {
371  assert( aFilter );
372 
373  if (bPopFilter){
374  mActionGroup->setAction( aFilter->action() );
375  mGlobalsBox->setEnabled( true );
376  mShowLaterBtn->setChecked(mFilterList->showLaterMsgs());
377  } else {
378  mActionLister->setActionList( aFilter->actions() );
379 
380  mAdvOptsGroup->setEnabled( true );
381  }
382 
383  mPatternEdit->setSearchPattern( aFilter->pattern() );
384  mFilter = aFilter;
385 
386  if (!bPopFilter) {
387  kdDebug(5006) << "apply on inbound == "
388  << aFilter->applyOnInbound() << endl;
389  kdDebug(5006) << "apply on outbound == "
390  << aFilter->applyOnOutbound() << endl;
391  kdDebug(5006) << "apply on explicit == "
392  << aFilter->applyOnExplicit() << endl;
393 
394  // NOTE: setting these values activates the slot that sets them in
395  // the filter! So make sure we have the correct values _before_ we
396  // set the first one:
397  const bool applyOnIn = aFilter->applyOnInbound();
398  const bool applyOnForAll = aFilter->applicability() == KMFilter::All;
399  const bool applyOnTraditional = aFilter->applicability() == KMFilter::ButImap;
400  const bool applyOnOut = aFilter->applyOnOutbound();
401  const bool applyOnExplicit = aFilter->applyOnExplicit();
402  const bool stopHere = aFilter->stopProcessingHere();
403  const bool configureShortcut = aFilter->configureShortcut();
404  const bool configureToolbar = aFilter->configureToolbar();
405  const TQString icon = aFilter->icon();
406  const KShortcut shortcut( aFilter->shortcut() );
407 
408  mApplyOnIn->setChecked( applyOnIn );
409  mApplyOnForAll->setEnabled( applyOnIn );
410  mApplyOnForTraditional->setEnabled( applyOnIn );
411  mApplyOnForChecked->setEnabled( applyOnIn );
412  mApplyOnForAll->setChecked( applyOnForAll );
413  mApplyOnForTraditional->setChecked( applyOnTraditional );
414  mApplyOnForChecked->setChecked( !applyOnForAll && !applyOnTraditional );
415  mAccountList->setEnabled( mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked() );
416  slotUpdateAccountList();
417  mApplyOnOut->setChecked( applyOnOut );
418  mApplyOnCtrlJ->setChecked( applyOnExplicit );
419  mStopProcessingHere->setChecked( stopHere );
420  mConfigureShortcut->setChecked( configureShortcut );
421  mKeyButton->setShortcut( shortcut, false );
422  mConfigureToolbar->setChecked( configureToolbar );
423  mFilterActionIconButton->setIcon( icon );
424  }
425 }
426 
427 void KMFilterDlg::slotReset()
428 {
429  mFilter = 0;
430  mPatternEdit->reset();
431 
432  if(bPopFilter) {
433  mActionGroup->reset();
434  mGlobalsBox->setEnabled( false );
435  } else {
436  mActionLister->reset();
437  mAdvOptsGroup->setEnabled( false );
438  slotUpdateAccountList();
439  }
440 }
441 
442 void KMFilterDlg::slotUpdateFilter()
443 {
445  if ( !bPopFilter ) {
446  mActionLister->updateActionList();
447  }
448 }
449 
450 void KMFilterDlg::slotApplicabilityChanged()
451 {
452  if ( mFilter ) {
453  mFilter->setApplyOnInbound( mApplyOnIn->isChecked() );
454  mFilter->setApplyOnOutbound( mApplyOnOut->isChecked() );
455  mFilter->setApplyOnExplicit( mApplyOnCtrlJ->isChecked() );
456  if ( mApplyOnForAll->isChecked() )
457  mFilter->setApplicability( KMFilter::All );
458  else if ( mApplyOnForTraditional->isChecked() )
459  mFilter->setApplicability( KMFilter::ButImap );
460  else if ( mApplyOnForChecked->isChecked() )
461  mFilter->setApplicability( KMFilter::Checked );
462 
463  mApplyOnForAll->setEnabled( mApplyOnIn->isChecked() );
464  mApplyOnForTraditional->setEnabled( mApplyOnIn->isChecked() );
465  mApplyOnForChecked->setEnabled( mApplyOnIn->isChecked() );
466  mAccountList->setEnabled( mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked() );
467 
468  // Advanced tab functionality - Update list of accounts this filter applies to
469  TQListViewItemIterator it( mAccountList );
470  while ( it.current() ) {
471  TQCheckListItem *item = dynamic_cast<TQCheckListItem*>( it.current() );
472  if (item) {
473  int id = item->text( 2 ).toInt();
474  item->setOn( mFilter->applyOnAccount( id ) );
475  }
476  ++it;
477  }
478 
479  kdDebug(5006) << "KMFilterDlg: setting filter to be applied at "
480  << ( mFilter->applyOnInbound() ? "incoming " : "" )
481  << ( mFilter->applyOnOutbound() ? "outgoing " : "" )
482  << ( mFilter->applyOnExplicit() ? "explicit CTRL-J" : "" )
483  << endl;
484  }
485 }
486 
487 void KMFilterDlg::slotApplicableAccountsChanged()
488 {
489  if ( mFilter && mApplyOnForChecked->isEnabled() && mApplyOnForChecked->isChecked() ) {
490  // Advanced tab functionality - Update list of accounts this filter applies to
491  TQListViewItemIterator it( mAccountList );
492  while ( it.current() ) {
493  TQCheckListItem *item = dynamic_cast<TQCheckListItem*>( it.current() );
494  if (item) {
495  int id = item->text( 2 ).toInt();
496  mFilter->setApplyOnAccount( id, item->isOn() );
497  }
498  ++it;
499  }
500  }
501 }
502 
503 void KMFilterDlg::slotStopProcessingButtonToggled( bool aChecked )
504 {
505  if ( mFilter )
506  mFilter->setStopProcessingHere( aChecked );
507 }
508 
509 void KMFilterDlg::slotConfigureShortcutButtonToggled( bool aChecked )
510 {
511  if ( mFilter ) {
512  mFilter->setConfigureShortcut( aChecked );
513  mKeyButton->setEnabled( aChecked );
514  mConfigureToolbar->setEnabled( aChecked );
515  mFilterActionIconButton->setEnabled( aChecked );
516  mFilterActionLabel->setEnabled( aChecked );
517  }
518 }
519 
520 void KMFilterDlg::slotCapturedShortcutChanged( const KShortcut& sc )
521 {
522  KShortcut mySc(sc);
523  if ( mySc == mKeyButton->shortcut() ) return;
524  // FIXME work around a problem when reseting the shortcut via the shortcut dialog
525  // somehow the returned shortcut does not evaluate to true in KShortcut::isNull(),
526  // so we additionally have to check for an empty string
527  if ( mySc.isNull() || mySc.toString().isEmpty() )
528  mySc.clear();
529  if ( !mySc.isNull() && !( kmkernel->getKMMainWidget()->shortcutIsValid( mySc ) ) ) {
530  TQString msg( i18n( "The selected shortcut is already used, "
531  "please select a different one." ) );
532  KMessageBox::sorry( this, msg );
533  } else {
534  mKeyButton->setShortcut( mySc, false );
535  if ( mFilter )
536  mFilter->setShortcut( mKeyButton->shortcut() );
537  }
538 }
539 
540 void KMFilterDlg::slotConfigureToolbarButtonToggled( bool aChecked )
541 {
542  if ( mFilter )
543  mFilter->setConfigureToolbar( aChecked );
544 }
545 
546 void KMFilterDlg::slotFilterActionIconChanged( TQString icon )
547 {
548  if ( mFilter )
549  mFilter->setIcon( icon );
550 }
551 
552 void KMFilterDlg::slotUpdateAccountList()
553 {
554  mAccountList->clear();
555  TQListViewItem *top = 0;
556  for( KMAccount *a = kmkernel->acctMgr()->first(); a!=0;
557  a = kmkernel->acctMgr()->next() ) {
558  TQCheckListItem *listItem =
559  new TQCheckListItem( mAccountList, top, a->name(), TQCheckListItem::CheckBox );
560  listItem->setText( 1, a->type() );
561  listItem->setText( 2, TQString( "%1" ).arg( a->id() ) );
562  if ( mFilter )
563  listItem->setOn( mFilter->applyOnAccount( a->id() ) );
564  top = listItem;
565  }
566 
567  TQListViewItem *listItem = mAccountList->firstChild();
568  if ( listItem ) {
569  mAccountList->setCurrentItem( listItem );
570  mAccountList->setSelected( listItem, true );
571  }
572 }
573 
574 //=============================================================================
575 //
576 // class KMFilterListBox (the filter list manipulator)
577 //
578 //=============================================================================
579 
580 KMFilterListBox::KMFilterListBox( const TQString & title, TQWidget *parent, const char* name, bool popFilter )
581  : TQGroupBox( 1, Qt::Horizontal, title, parent, name ),
582  bPopFilter(popFilter)
583 {
584  mFilterList.setAutoDelete( true );
585  mIdxSelItem = -1;
586 
587  //----------- the list box
588  mListBox = new TQListBox(this);
589  mListBox->setMinimumWidth(150);
590  TQWhatsThis::add( mListBox, i18n(_wt_filterlist) );
591 
592  //----------- the first row of buttons
593  TQHBox *hb = new TQHBox(this);
594  hb->setSpacing(4);
595  mBtnTop = new KPushButton( TQString(), hb );
596  mBtnTop->setAutoRepeat( true );
597  mBtnTop->setIconSet( BarIconSet( "top", KIcon::SizeSmall ) );
598  mBtnTop->setMinimumSize( mBtnTop->sizeHint() * 1.2 );
599  mBtnUp = new KPushButton( TQString(), hb );
600  mBtnUp->setAutoRepeat( true );
601  mBtnUp->setIconSet( BarIconSet( "up", KIcon::SizeSmall ) );
602  mBtnUp->setMinimumSize( mBtnUp->sizeHint() * 1.2 );
603  mBtnDown = new KPushButton( TQString(), hb );
604  mBtnDown->setAutoRepeat( true );
605  mBtnDown->setIconSet( BarIconSet( "down", KIcon::SizeSmall ) );
606  mBtnDown->setMinimumSize( mBtnDown->sizeHint() * 1.2 );
607  mBtnBot = new KPushButton( TQString(), hb );
608  mBtnBot->setAutoRepeat( true );
609  mBtnBot->setIconSet( BarIconSet( "bottom", KIcon::SizeSmall ) );
610  mBtnBot->setMinimumSize( mBtnBot->sizeHint() * 1.2 );
611  TQToolTip::add( mBtnTop, i18n("Top") );
612  TQToolTip::add( mBtnUp, i18n("Up") );
613  TQToolTip::add( mBtnDown, i18n("Down") );
614  TQToolTip::add( mBtnBot, i18n("Bottom") );
615  TQWhatsThis::add( mBtnTop, i18n(_wt_filterlist_top) );
616  TQWhatsThis::add( mBtnUp, i18n(_wt_filterlist_up) );
617  TQWhatsThis::add( mBtnDown, i18n(_wt_filterlist_down) );
618  TQWhatsThis::add( mBtnBot, i18n(_wt_filterlist_bot) );
619 
620  //----------- the second row of buttons
621  hb = new TQHBox(this);
622  hb->setSpacing(4);
623  mBtnNew = new TQPushButton( TQString(), hb );
624  mBtnNew->setPixmap( BarIcon( "filenew", KIcon::SizeSmall ) );
625  mBtnNew->setMinimumSize( mBtnNew->sizeHint() * 1.2 );
626  mBtnCopy = new TQPushButton( TQString(), hb );
627  mBtnCopy->setIconSet( BarIconSet( "editcopy", KIcon::SizeSmall ) );
628  mBtnCopy->setMinimumSize( mBtnCopy->sizeHint() * 1.2 );
629  mBtnDelete = new TQPushButton( TQString(), hb );
630  mBtnDelete->setIconSet( BarIconSet( "editdelete", KIcon::SizeSmall ) );
631  mBtnDelete->setMinimumSize( mBtnDelete->sizeHint() * 1.2 );
632  mBtnRename = new TQPushButton( i18n("Rename..."), hb );
633  TQToolTip::add( mBtnNew, i18n("New") );
634  TQToolTip::add( mBtnCopy, i18n("Copy") );
635  TQToolTip::add( mBtnDelete, i18n("Delete"));
636  TQWhatsThis::add( mBtnNew, i18n(_wt_filterlist_new) );
637  TQWhatsThis::add( mBtnCopy, i18n(_wt_filterlist_copy) );
638  TQWhatsThis::add( mBtnDelete, i18n(_wt_filterlist_delete) );
639  TQWhatsThis::add( mBtnRename, i18n(_wt_filterlist_rename) );
640 
641  // third row
642  if ( !popFilter ) {
643  hb = new TQHBox( this );
644  hb->setSpacing( 4 );
645  TQPushButton *btn = new TQPushButton( i18n("Select Source Folders"), hb );
646  connect( btn, TQT_SIGNAL(clicked()), TQT_SLOT(slotSelectSourceFolders()) );
647  }
648 
649 
650  //----------- now connect everything
651  connect( mListBox, TQT_SIGNAL(highlighted(int)),
652  this, TQT_SLOT(slotSelected(int)) );
653  connect( mListBox, TQT_SIGNAL( doubleClicked ( TQListBoxItem * )),
654  this, TQT_SLOT( slotRename()) );
655  connect( mBtnTop, TQT_SIGNAL(clicked()),
656  this, TQT_SLOT(slotTop()) );
657  connect( mBtnUp, TQT_SIGNAL(clicked()),
658  this, TQT_SLOT(slotUp()) );
659  connect( mBtnDown, TQT_SIGNAL(clicked()),
660  this, TQT_SLOT(slotDown()) );
661  connect( mBtnBot, TQT_SIGNAL(clicked()),
662  this, TQT_SLOT(slotBottom()) );
663  connect( mBtnNew, TQT_SIGNAL(clicked()),
664  this, TQT_SLOT(slotNew()) );
665  connect( mBtnCopy, TQT_SIGNAL(clicked()),
666  this, TQT_SLOT(slotCopy()) );
667  connect( mBtnDelete, TQT_SIGNAL(clicked()),
668  this, TQT_SLOT(slotDelete()) );
669  connect( mBtnRename, TQT_SIGNAL(clicked()),
670  this, TQT_SLOT(slotRename()) );
671 
672  // the dialog should call loadFilterList()
673  // when all signals are connected.
674  enableControls();
675 }
676 
677 
678 void KMFilterListBox::createFilter( const TQCString & field,
679  const TQString & value )
680 {
681  KMSearchRule *newRule = KMSearchRule::createInstance( field, KMSearchRule::FuncContains, value );
682 
683  KMFilter *newFilter = new KMFilter(0, bPopFilter);
684  newFilter->pattern()->append( newRule );
685  newFilter->pattern()->setName( TQString("<%1>:%2").arg( TQString(field) ).arg( TQString(value) ) );
686 
687  KMFilterActionDesc *desc = (*kmkernel->filterActionDict())["transfer"];
688  if ( desc )
689  newFilter->actions()->append( desc->create() );
690 
691  insertFilter( newFilter );
692  enableControls();
693 }
694 
696 {
697  return mShowLater;
698 }
699 
701 {
702  KMSearchPattern *p = mFilterList.at(mIdxSelItem)->pattern();
703  if ( !p ) return;
704 
705  TQString shouldBeName = p->name();
706  TQString displayedName = mListBox->text( mIdxSelItem );
707 
708  if ( shouldBeName.stripWhiteSpace().isEmpty() ) {
709  mFilterList.at(mIdxSelItem)->setAutoNaming( true );
710  }
711 
712  if ( mFilterList.at(mIdxSelItem)->isAutoNaming() ) {
713  // auto-naming of patterns
714  if ( !p->isEmpty() && p->first() && !p->first()->field().stripWhiteSpace().isEmpty() )
715  shouldBeName = TQString( "<%1>: %2" ).arg( TQString(p->first()->field()) ).arg( TQString(p->first()->contents()) );
716  else
717  shouldBeName = "<" + i18n("unnamed") + ">";
718  p->setName( shouldBeName );
719  }
720 
721  if ( displayedName == shouldBeName ) return;
722 
723  mListBox->blockSignals( true );
724  mListBox->changeItem( shouldBeName, mIdxSelItem );
725  mListBox->blockSignals( false );
726 }
727 
729 {
730  mShowLater = aOn;
731 }
732 
734 {
735  if ( mIdxSelItem >= 0 ) {
736  emit applyWidgets();
737  slotSelected( mListBox->currentItem() );
738  }
739 
740  // by now all edit widgets should have written back
741  // their widget's data into our filter list.
742 
743  KMFilterMgr *fm;
744  if (bPopFilter)
745  fm = kmkernel->popFilterMgr();
746  else
747  fm = kmkernel->filterMgr();
748 
749  TQValueList<KMFilter*> newFilters = filtersForSaving();
750 
751  if (bPopFilter)
752  fm->setShowLaterMsgs(mShowLater);
753 
754  fm->setFilters( newFilters );
755  if (fm->atLeastOneOnlineImapFolderTarget()) {
756  TQString str = i18n("At least one filter targets a folder on an online "
757  "IMAP account. Such filters will only be applied "
758  "when manually filtering and when filtering "
759  "incoming online IMAP mail.");
760  KMessageBox::information( this, str, TQString(),
761  "filterDlgOnlineImapCheck" );
762  }
763 }
764 
765 TQValueList<KMFilter*> KMFilterListBox::filtersForSaving() const
766 {
767  const_cast<KMFilterListBox*>( this )->applyWidgets(); // signals aren't const
768  TQValueList<KMFilter*> filters;
769  TQStringList emptyFilters;
770  TQPtrListIterator<KMFilter> it( mFilterList );
771  for ( it.toFirst() ; it.current() ; ++it ) {
772  KMFilter *f = new KMFilter( **it ); // deep copy
773  f->purify();
774  if ( !f->isEmpty() )
775  // the filter is valid:
776  filters.append( f );
777  else {
778  // the filter is invalid:
779  emptyFilters << f->name();
780  delete f;
781  }
782  }
783 
784  // report on invalid filters:
785  if ( !emptyFilters.empty() ) {
786  TQString msg = i18n("The following filters have not been saved because they "
787  "were invalid (e.g. containing no actions or no search "
788  "rules).");
789  KMessageBox::informationList( 0, msg, emptyFilters, TQString(),
790  "ShowInvalidFilterWarning" );
791  }
792  return filters;
793 }
794 
796 {
797  mIdxSelItem = aIdx;
798  // TQPtrList::at(i) will return 0 if i is out of range.
799  KMFilter *f = mFilterList.at(aIdx);
800  if ( f )
801  emit filterSelected( f );
802  else
803  emit resetWidgets();
804  enableControls();
805 }
806 
808 {
809  // just insert a new filter.
810  insertFilter( new KMFilter(0, bPopFilter) );
811  enableControls();
812 }
813 
815 {
816  if ( mIdxSelItem < 0 ) {
817  kdDebug(5006) << "KMFilterListBox::slotCopy called while no filter is selected, ignoring." << endl;
818  return;
819  }
820 
821  // make sure that all changes are written to the filter before we copy it
822  emit applyWidgets();
823 
824  KMFilter *filter = mFilterList.at( mIdxSelItem );
825 
826  // enableControls should make sure this method is
827  // never called when no filter is selected.
828  assert( filter );
829 
830  // inserts a copy of the current filter.
831  insertFilter( new KMFilter( *filter ) );
832  enableControls();
833 }
834 
836 {
837  if ( mIdxSelItem < 0 ) {
838  kdDebug(5006) << "KMFilterListBox::slotDelete called while no filter is selected, ignoring." << endl;
839  return;
840  }
841 
842  int oIdxSelItem = mIdxSelItem;
843  mIdxSelItem = -1;
844  // unselect all
845  mListBox->selectAll( false );
846  // broadcast that all widgets let go
847  // of the filter
848  emit resetWidgets();
849 
850  // remove the filter from both the filter list...
851  mFilterList.remove( oIdxSelItem );
852  // and the listbox
853  mListBox->removeItem( oIdxSelItem );
854 
855  int count = (int)mListBox->count();
856  // and set the new current item.
857  if ( count > oIdxSelItem )
858  // oIdxItem is still a valid index
859  mListBox->setSelected( oIdxSelItem, true );
860  else if ( count )
861  // oIdxSelIdx is no longer valid, but the
862  // list box isn't empty
863  mListBox->setSelected( count - 1, true );
864  // the list is empty - keep index -1
865 
866  enableControls();
867 }
868 
870 {
871  if ( mIdxSelItem < 0 ) {
872  kdDebug(5006) << "KMFilterListBox::slotTop called while no filter is selected, ignoring." << endl;
873  return;
874  }
875  if ( mIdxSelItem == 0 ) {
876  kdDebug(5006) << "KMFilterListBox::slotTop called while the _topmost_ filter is selected, ignoring." << endl;
877  return;
878  }
879 
880  swapFilters( mIdxSelItem, 0 );
881  enableControls();
882 }
883 
885 {
886  if ( mIdxSelItem < 0 ) {
887  kdDebug(5006) << "KMFilterListBox::slotUp called while no filter is selected, ignoring." << endl;
888  return;
889  }
890  if ( mIdxSelItem == 0 ) {
891  kdDebug(5006) << "KMFilterListBox::slotUp called while the _topmost_ filter is selected, ignoring." << endl;
892  return;
893  }
894 
895  swapNeighbouringFilters( mIdxSelItem, mIdxSelItem - 1 );
896  enableControls();
897 }
898 
900 {
901  if ( mIdxSelItem < 0 ) {
902  kdDebug(5006) << "KMFilterListBox::slotDown called while no filter is selected, ignoring." << endl;
903  return;
904  }
905  if ( mIdxSelItem == (int)mListBox->count() - 1 ) {
906  kdDebug(5006) << "KMFilterListBox::slotDown called while the _last_ filter is selected, ignoring." << endl;
907  return;
908  }
909 
910  swapNeighbouringFilters( mIdxSelItem, mIdxSelItem + 1);
911  enableControls();
912 }
913 
915 {
916  if ( mIdxSelItem < 0 ) {
917  kdDebug(5006) << "KMFilterListBox::slotBottom called while no filter is selected, ignoring." << endl;
918  return;
919  }
920  if ( mIdxSelItem == (int)mListBox->count() - 1 ) {
921  kdDebug(5006) << "KMFilterListBox::slotBottom called while the _last_ filter is selected, ignoring." << endl;
922  return;
923  }
924 
925  swapFilters( mIdxSelItem, mListBox->count()-1 );
926  enableControls();
927 }
928 
930 {
931  if ( mIdxSelItem < 0 ) {
932  kdDebug(5006) << "KMFilterListBox::slotRename called while no filter is selected, ignoring." << endl;
933  return;
934  }
935 
936  bool okPressed = false ;
937  KMFilter *filter = mFilterList.at( mIdxSelItem );
938 
939  // enableControls should make sure this method is
940  // never called when no filter is selected.
941  assert( filter );
942 
943  // allow empty names - those will turn auto-naming on again
944  TQValidator *validator = new TQRegExpValidator( TQRegExp( ".*" ), 0 );
945  TQString newName = KInputDialog::getText
946  (
947  i18n("Rename Filter"),
948  i18n("Rename filter \"%1\" to:\n(leave the field empty for automatic naming)")
949  .arg( filter->pattern()->name() ) /*label*/,
950  filter->pattern()->name() /* initial value */,
951  &okPressed, topLevelWidget(), 0, validator
952  );
953  delete validator;
954 
955  if ( !okPressed ) return;
956 
957  if ( newName.isEmpty() ) {
958  // bait for slotUpdateFilterName to
959  // use automatic naming again.
960  filter->pattern()->setName( "<>" );
961  filter->setAutoNaming( true );
962  } else {
963  filter->pattern()->setName( newName );
964  filter->setAutoNaming( false );
965  }
966 
968 }
969 
970 void KMFilterListBox::slotSelectSourceFolders()
971 {
972  FolderSetSelector dlg( kmkernel->getKMMainWidget()->folderTree(), this );
973  dlg.setCaption( i18n( "Select Folders to Filter" ) );
974  if ( !GlobalSettings::filterSourceFolders().isEmpty() )
975  dlg.setSelectedFolders( GlobalSettings::filterSourceFolders() );
976  if ( dlg.exec() == TQDialog::Accepted ) {
977  GlobalSettings::setFilterSourceFolders( dlg.selectedFolders() );
978  }
979 }
980 
981 void KMFilterListBox::enableControls()
982 {
983  bool theFirst = ( mIdxSelItem == 0 );
984  bool theLast = ( mIdxSelItem >= (int)mFilterList.count() - 1 );
985  bool aFilterIsSelected = ( mIdxSelItem >= 0 );
986 
987  mBtnTop->setEnabled( aFilterIsSelected && !theFirst );
988  mBtnUp->setEnabled( aFilterIsSelected && !theFirst );
989  mBtnDown->setEnabled( aFilterIsSelected && !theLast );
990  mBtnBot->setEnabled( aFilterIsSelected && !theLast );
991  mBtnCopy->setEnabled( aFilterIsSelected );
992  mBtnDelete->setEnabled( aFilterIsSelected );
993  mBtnRename->setEnabled( aFilterIsSelected );
994 
995  if ( aFilterIsSelected )
996  mListBox->ensureCurrentVisible();
997 }
998 
999 void KMFilterListBox::loadFilterList( bool createDummyFilter )
1000 {
1001  assert(mListBox);
1002  setEnabled( false );
1003  emit resetWidgets();
1004  // we don't want the insertion to
1005  // cause flicker in the edit widgets.
1006  blockSignals( true );
1007 
1008  // clear both lists
1009  mFilterList.clear();
1010  mListBox->clear();
1011 
1012  const KMFilterMgr *manager = 0;
1013  if(bPopFilter)
1014  {
1015  mShowLater = kmkernel->popFilterMgr()->showLaterMsgs();
1016  manager = kmkernel->popFilterMgr();
1017  }
1018  else
1019  {
1020  manager = kmkernel->filterMgr();
1021  }
1022  Q_ASSERT( manager );
1023 
1024  TQValueListConstIterator<KMFilter*> it;
1025  for ( it = manager->filters().constBegin() ; it != manager->filters().constEnd() ; ++it ) {
1026  mFilterList.append( new KMFilter( **it ) ); // deep copy
1027  mListBox->insertItem( (*it)->pattern()->name() );
1028  }
1029 
1030  blockSignals( false );
1031  setEnabled( true );
1032 
1033  // create an empty filter when there's none, to avoid a completely
1034  // disabled dialog (usability tests indicated that the new-filter
1035  // button is too hard to find that way):
1036  if ( !mListBox->count() && createDummyFilter )
1037  slotNew();
1038 
1039  if ( mListBox->count() > 0 )
1040  mListBox->setSelected( 0, true );
1041 
1042  enableControls();
1043 }
1044 
1045 void KMFilterListBox::insertFilter( KMFilter* aFilter )
1046 {
1047  // must be really a filter...
1048  assert( aFilter );
1049 
1050  // if mIdxSelItem < 0, TQListBox::insertItem will append.
1051  mListBox->insertItem( aFilter->pattern()->name(), mIdxSelItem );
1052  if ( mIdxSelItem < 0 ) {
1053  // none selected -> append
1054  mFilterList.append( aFilter );
1055  mListBox->setSelected( mListBox->count() - 1, true );
1056  // slotSelected( mListBox->count() - 1 );
1057  } else {
1058  // insert just before selected
1059  mFilterList.insert( mIdxSelItem, aFilter );
1060  mListBox->setSelected( mIdxSelItem, true );
1061  // slotSelected( mIdxSelItem );
1062  }
1063 
1064 }
1065 
1066 void KMFilterListBox::appendFilter( KMFilter* aFilter )
1067 {
1068  mFilterList.append( aFilter );
1069  mListBox->insertItem( aFilter->pattern()->name(), -1 );
1070 }
1071 
1072 void KMFilterListBox::swapNeighbouringFilters( int untouchedOne, int movedOne )
1073 {
1074  // must be neighbours...
1075  assert( untouchedOne - movedOne == 1 || movedOne - untouchedOne == 1 );
1076 
1077  // untouchedOne is at idx. to move it down(up),
1078  // remove item at idx+(-)1 w/o deleting it.
1079  TQListBoxItem *item = mListBox->item( movedOne );
1080  mListBox->takeItem( item );
1081  // now selected item is at idx(idx-1), so
1082  // insert the other item at idx, ie. above(below).
1083  mListBox->insertItem( item, untouchedOne );
1084 
1085  KMFilter* filter = mFilterList.take( movedOne );
1086  mFilterList.insert( untouchedOne, filter );
1087 
1088  mIdxSelItem += movedOne - untouchedOne;
1089 }
1090 
1091 void KMFilterListBox::swapFilters( int from, int to )
1092 {
1093  TQListBoxItem *item = mListBox->item( from );
1094  mListBox->takeItem( item );
1095  mListBox->insertItem( item, to );
1096 
1097  KMFilter* filter = mFilterList.take( from );
1098  mFilterList.insert( to, filter );
1099 
1100  mIdxSelItem = to;
1101  mListBox->setCurrentItem( mIdxSelItem );
1102  mListBox->setSelected( mIdxSelItem, true );
1103 }
1104 
1105 //=============================================================================
1106 //
1107 // class KMFilterActionWidget
1108 //
1109 //=============================================================================
1110 
1111 KMFilterActionWidget::KMFilterActionWidget( TQWidget *parent, const char* name )
1112  : TQHBox( parent, name )
1113 {
1114  int i;
1115  mActionList.setAutoDelete( true );
1116 
1117  mComboBox = new TQComboBox( false , this );
1118  assert( mComboBox );
1119  mWidgetStack = new TQWidgetStack(this);
1120  assert( mWidgetStack );
1121 
1122  setSpacing( 4 );
1123 
1124  TQPtrListIterator<KMFilterActionDesc> it ( kmkernel->filterActionDict()->list() );
1125  for ( i=0, it.toFirst() ; it.current() ; ++it, ++i ) {
1126  //create an instance:
1127  KMFilterAction *a = (*it)->create();
1128  // append to the list of actions:
1129  mActionList.append( a );
1130  // add parameter widget to widget stack:
1131  mWidgetStack->addWidget( a->createParamWidget( mWidgetStack ), i );
1132  // add (i18n-ized) name to combo box
1133  mComboBox->insertItem( (*it)->label );
1134  }
1135  // widget for the case where no action is selected.
1136  mWidgetStack->addWidget( new TQLabel( i18n("Please select an action."), mWidgetStack ), i );
1137  mWidgetStack->raiseWidget(i);
1138  mComboBox->insertItem( " " );
1139  mComboBox->setCurrentItem(i);
1140 
1141  // don't show scroll bars.
1142  mComboBox->setSizeLimit( mComboBox->count() );
1143  // layout management:
1144  // o the combo box is not to be made larger than it's sizeHint(),
1145  // the parameter widget should grow instead.
1146  // o the whole widget takes all space horizontally, but is fixed vertically.
1147  mComboBox->adjustSize();
1148  mComboBox->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed ) );
1149  setSizePolicy( TQSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Fixed ) );
1150  updateGeometry();
1151 
1152  // redirect focus to the filter action combo box
1153  setFocusProxy( mComboBox );
1154 
1155  // now connect the combo box and the widget stack
1156  connect( mComboBox, TQT_SIGNAL(activated(int)),
1157  mWidgetStack, TQT_SLOT(raiseWidget(int)) );
1158 }
1159 
1161 {
1162  int i=0;
1163  bool found = false ;
1164  int count = mComboBox->count() - 1 ; // last entry is the empty one
1165  TQString label = ( aAction ) ? aAction->label() : TQString() ;
1166 
1167  // find the index of typeOf(aAction) in mComboBox
1168  // and clear the other widgets on the way.
1169  for ( ; i < count ; i++ )
1170  if ( aAction && mComboBox->text(i) == label ) {
1171  //...set the parameter widget to the settings
1172  // of aAction...
1173  aAction->setParamWidgetValue( mWidgetStack->widget(i) );
1174  //...and show the correct entry of
1175  // the combo box
1176  mComboBox->setCurrentItem(i); // (mm) also raise the widget, but doesn't
1177  mWidgetStack->raiseWidget(i);
1178  found = true;
1179  } else // clear the parameter widget
1180  mActionList.at(i)->clearParamWidget( mWidgetStack->widget(i) );
1181  if ( found ) return;
1182 
1183  // not found, so set the empty widget
1184  mComboBox->setCurrentItem( count ); // last item
1185  mWidgetStack->raiseWidget( count) ;
1186 }
1187 
1189 {
1190  // look up the action description via the label
1191  // returned by TQComboBox::currentText()...
1192  KMFilterActionDesc *desc = (*kmkernel->filterActionDict())[ mComboBox->currentText() ];
1193  if ( desc ) {
1194  // ...create an instance...
1195  KMFilterAction *fa = desc->create();
1196  if ( fa ) {
1197  // ...and apply the setting of the parameter widget.
1198  fa->applyParamWidgetValue( mWidgetStack->visibleWidget() );
1199  return fa;
1200  }
1201  }
1202 
1203  return 0;
1204 }
1205 
1206 //=============================================================================
1207 //
1208 // class KMFilterActionWidgetLister (the filter action editor)
1209 //
1210 //=============================================================================
1211 
1212 KMFilterActionWidgetLister::KMFilterActionWidgetLister( TQWidget *parent, const char* name )
1213  : KWidgetLister( 1, FILTER_MAX_ACTIONS, parent, name )
1214 {
1215  mActionList = 0;
1216 }
1217 
1218 KMFilterActionWidgetLister::~KMFilterActionWidgetLister()
1219 {
1220 }
1221 
1222 void KMFilterActionWidgetLister::setActionList( TQPtrList<KMFilterAction> *aList )
1223 {
1224  assert ( aList );
1225 
1226  if ( mActionList )
1227  regenerateActionListFromWidgets();
1228 
1229  mActionList = aList;
1230 
1231  ((TQWidget*)parent())->setEnabled( true );
1232 
1233  if ( aList->count() == 0 ) {
1234  slotClear();
1235  return;
1236  }
1237 
1238  int superfluousItems = (int)mActionList->count() - mMaxWidgets ;
1239  if ( superfluousItems > 0 ) {
1240  kdDebug(5006) << "KMFilterActionWidgetLister: Clipping action list to "
1241  << mMaxWidgets << " items!" << endl;
1242 
1243  for ( ; superfluousItems ; superfluousItems-- )
1244  mActionList->removeLast();
1245  }
1246 
1247  // set the right number of widgets
1248  setNumberOfShownWidgetsTo( mActionList->count() );
1249 
1250  // load the actions into the widgets
1251  TQPtrListIterator<KMFilterAction> aIt( *mActionList );
1252  TQPtrListIterator<TQWidget> wIt( mWidgetList );
1253  for ( aIt.toFirst(), wIt.toFirst() ;
1254  aIt.current() && wIt.current() ; ++aIt, ++wIt )
1255  ((KMFilterActionWidget*)(*wIt))->setAction( (*aIt) );
1256 }
1257 
1258 void KMFilterActionWidgetLister::reset()
1259 {
1260  if ( mActionList )
1261  regenerateActionListFromWidgets();
1262 
1263  mActionList = 0;
1264  slotClear();
1265  ((TQWidget*)parent())->setEnabled( false );
1266 }
1267 
1268 TQWidget* KMFilterActionWidgetLister::createWidget( TQWidget *parent )
1269 {
1270  return new KMFilterActionWidget(parent);
1271 }
1272 
1273 void KMFilterActionWidgetLister::clearWidget( TQWidget *aWidget )
1274 {
1275  if ( aWidget )
1276  ((KMFilterActionWidget*)aWidget)->setAction(0);
1277 }
1278 
1279 void KMFilterActionWidgetLister::regenerateActionListFromWidgets()
1280 {
1281  if ( !mActionList ) return;
1282 
1283  mActionList->clear();
1284 
1285  TQPtrListIterator<TQWidget> it( mWidgetList );
1286  for ( it.toFirst() ; it.current() ; ++it ) {
1287  KMFilterAction *a = ((KMFilterActionWidget*)(*it))->action();
1288  if ( a )
1289  mActionList->append( a );
1290  }
1291 
1292 }
1293 
1294 //=============================================================================
1295 //
1296 // class KMPopFilterActionWidget
1297 //
1298 //=============================================================================
1299 
1300 KMPopFilterActionWidget::KMPopFilterActionWidget( const TQString& title, TQWidget *parent, const char* name )
1301  : TQVButtonGroup( title, parent, name )
1302 {
1303  mActionMap[Down] = new TQRadioButton( i18n("&Download mail"), this );
1304  mActionMap[Later] = new TQRadioButton( i18n("Download mail la&ter"), this );
1305  mActionMap[Delete] = new TQRadioButton( i18n("D&elete mail from server"), this );
1306  mIdMap[id(mActionMap[Later])] = Later;
1307  mIdMap[id(mActionMap[Down])] = Down;
1308  mIdMap[id(mActionMap[Delete])] = Delete;
1309 
1310  connect( this, TQT_SIGNAL(clicked(int)),
1311  this, TQT_SLOT( slotActionClicked(int)) );
1312 }
1313 
1314 void KMPopFilterActionWidget::setAction( KMPopFilterAction aAction )
1315 {
1316  if( aAction == NoAction)
1317  {
1318  aAction = Later;
1319  }
1320 
1321  mAction = aAction;
1322 
1323  blockSignals( true );
1324  if(!mActionMap[aAction]->isChecked())
1325  {
1326  mActionMap[aAction]->setChecked( true );
1327  }
1328  blockSignals( false );
1329 
1330  setEnabled( true );
1331 }
1332 
1333 KMPopFilterAction KMPopFilterActionWidget::action()
1334 {
1335  return mAction;
1336 }
1337 
1338 void KMPopFilterActionWidget::slotActionClicked(int aId)
1339 {
1340  emit actionChanged(mIdMap[aId]);
1341  setAction(mIdMap[aId]);
1342 }
1343 
1344 void KMPopFilterActionWidget::reset()
1345 {
1346  blockSignals( true );
1347  mActionMap[Down]->setChecked( true );
1348  blockSignals( false );
1349 
1350  setEnabled( false );
1351 }
1352 
1354 {
1355  FilterImporterExporter importer( this, bPopFilter );
1356  TQValueList<KMFilter*> filters = importer.importFilters();
1357  // FIXME message box how many were imported?
1358  if (filters.isEmpty()) return;
1359 
1360  TQValueListConstIterator<KMFilter*> it;
1361 
1362  for ( it = filters.constBegin() ; it != filters.constEnd() ; ++it ) {
1363  mFilterList->appendFilter( *it ); // no need to deep copy, ownership passes to the list
1364  }
1365 }
1366 
1368 {
1369  FilterImporterExporter exporter( this, bPopFilter );
1370  TQValueList<KMFilter*> filters = mFilterList->filtersForSaving();
1371  exporter.exportFilters( filters );
1372  TQValueList<KMFilter*>::iterator it;
1373  for ( it = filters.begin(); it != filters.end(); ++it )
1374  delete *it;
1375 }
1376 
1377 #include "kmfilterdlg.moc"