kmail

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