kmail Library API Documentation

kmfilterdlg.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmfilterdlg.cpp
00003 // Author: Marc Mutz <Marc@Mutz.com>
00004 // based on work by Stefan Taferner <taferner@kde.org>
00005 // This code is under the GPL
00006 
00007 #include <config.h>
00008 #include "kmfilterdlg.h"
00009 
00010 // other KMail headers:
00011 #include "kmsearchpatternedit.h"
00012 #include "kmfiltermgr.h"
00013 #include "foldersetselector.h"
00014 #include "kmmainwidget.h"
00015 #include "globalsettings.h"
00016 
00017 // other KDE headers:
00018 #include <kmessagebox.h>
00019 #include <kdebug.h>
00020 #include <klocale.h>
00021 #include <kinputdialog.h>
00022 #include <kiconloader.h>
00023 #include <kapplication.h>
00024 #include <kwin.h>
00025 #include <kconfig.h>
00026 #include <kicondialog.h>
00027 
00028 // other Qt headers:
00029 #include <qlayout.h>
00030 #include <qlabel.h>
00031 #include <qcombobox.h>
00032 #include <qwidgetstack.h>
00033 #include <qtooltip.h>
00034 #include <qwhatsthis.h>
00035 #include <qcheckbox.h>
00036 #include <qpushbutton.h>
00037 #include <qhbox.h>
00038 
00039 // other headers:
00040 #include <assert.h>
00041 
00042 using namespace KMail;
00043 
00044 
00045 // What's this help texts
00046 const char * _wt_filterlist =
00047 I18N_NOOP( "<qt><p>This is the list of defined filters. "
00048        "They are processed top-to-bottom.</p>"
00049        "<p>Click on any filter to edit it "
00050        "using the controls in the right-hand half "
00051        "of the dialog.</p></qt>" );
00052 const char * _wt_filterlist_new =
00053 I18N_NOOP( "<qt><p>Click this button to create a new filter.</p>"
00054        "<p>The filter will be inserted just before the currently-"
00055        "selected one, but you can always change that "
00056        "later on.</p>"
00057        "<p>If you have clicked this button accidentally, you can undo this "
00058        "by clicking on the <em>Delete</em> button.</p></qt>" );
00059 const char * _wt_filterlist_copy =
00060 I18N_NOOP( "<qt><p>Click this button to copy a filter.</p>"
00061        "<p>If you have clicked this button accidentally, you can undo this "
00062        "by clicking on the <em>Delete</em> button.</p></qt>" );
00063 const char * _wt_filterlist_delete =
00064 I18N_NOOP( "<qt><p>Click this button to <em>delete</em> the currently-"
00065        "selected filter from the list above.</p>"
00066        "<p>There is no way to get the filter back once "
00067        "it is deleted, but you can always leave the "
00068        "dialog by clicking <em>Cancel</em> to discard the "
00069        "changes made.</p></qt>" );
00070 const char * _wt_filterlist_up =
00071 I18N_NOOP( "<qt><p>Click this button to move the currently-"
00072        "selected filter <em>up</em> one in the list above.</p>"
00073        "<p>This is useful since the order of the filters in the list "
00074        "determines the order in which they are tried on messages: "
00075        "The topmost filter gets tried first.</p>"
00076        "<p>If you have clicked this button accidentally, you can undo this "
00077        "by clicking on the <em>Down</em> button.</p></qt>" );
00078 const char * _wt_filterlist_down =
00079 I18N_NOOP( "<qt><p>Click this button to move the currently-"
00080        "selected filter <em>down</em> one in the list above.</p>"
00081        "<p>This is useful since the order of the filters in the list "
00082        "determines the order in which they are tried on messages: "
00083        "The topmost filter gets tried first.</p>"
00084        "<p>If you have clicked this button accidentally, you can undo this "
00085        "by clicking on the <em>Up</em> button.</p></qt>" );
00086 const char * _wt_filterlist_rename =
00087 I18N_NOOP( "<qt><p>Click this button to rename the currently-selected filter.</p>"
00088        "<p>Filters are named automatically, as long as they start with "
00089        "\"<<\".</p>"
00090        "<p>If you have renamed a filter accidentally and want automatic "
00091        "naming back, click this button and select <em>Clear</em> followed "
00092        "by <em>OK</em> in the appearing dialog.</p></qt>" );
00093 const char * _wt_filterdlg_showLater =
00094 I18N_NOOP( "<qt><p>Check this button to force the confirmation dialog to be "
00095        "displayed.</p><p>This is useful if you have defined a ruleset that tags "
00096            "messages to be downloaded later. Without the possibility to force "
00097            "the dialog popup, these messages could never be downloaded if no "
00098            "other large messages were waiting on the server, or if you wanted to "
00099            "change the ruleset to tag the messages differently.</p></qt>" );
00100 
00101 // The anchor of the filter dialog's help.
00102 const char * KMFilterDlgHelpAnchor =  "filters-id" ;
00103 const char * KMPopFilterDlgHelpAnchor =  "popfilters-id" ;
00104 
00105 //=============================================================================
00106 //
00107 // class KMFilterDlg (the filter dialog)
00108 //
00109 //=============================================================================
00110 
00111 KMFilterDlg::KMFilterDlg(QWidget* parent, const char* name, bool popFilter)
00112   : KDialogBase( parent, name, FALSE /* modality */,
00113          (popFilter)? i18n("POP3 Filter Rules"): i18n("Filter Rules") /* caption*/,
00114          Help|Ok|Apply|Cancel /* button mask */,
00115          Ok /* default btn */, FALSE /* separator */),
00116   bPopFilter(popFilter)
00117 {
00118   KWin::setIcons( winId(), kapp->icon(), kapp->miniIcon() );
00119   setHelp( (bPopFilter)? KMPopFilterDlgHelpAnchor: KMFilterDlgHelpAnchor );
00120 
00121   QWidget *w = new QWidget(this);
00122   setMainWidget(w);
00123   QHBoxLayout *hbl = new QHBoxLayout( w, 0, spacingHint(), "kmfd_hbl" );
00124 
00125   mFilterList = new KMFilterListBox( i18n("Available Filters"), w, 0, bPopFilter);
00126   hbl->addWidget( mFilterList, 1 /*stretch*/ );
00127 
00128   QVBoxLayout *vbl = new QVBoxLayout( hbl, spacingHint(), "kmfd_vbl" );
00129   hbl->setStretchFactor( vbl, 2 );
00130 
00131   mPatternEdit = new KMSearchPatternEdit( i18n("Filter Criteria"), w , "spe", bPopFilter);
00132   vbl->addWidget( mPatternEdit, 0, Qt::AlignTop );
00133 
00134   if(bPopFilter){
00135     mActionGroup = new KMPopFilterActionWidget( i18n("Filter Action"), w );
00136     vbl->addWidget( mActionGroup, 0, Qt::AlignTop );
00137 
00138     mGlobalsBox = new QVGroupBox(i18n("Global Options"), w);
00139     mShowLaterBtn = new QCheckBox(i18n("Always &show matched 'Download Later' messages in confirmation dialog"), mGlobalsBox);
00140     QWhatsThis::add( mShowLaterBtn, i18n(_wt_filterdlg_showLater) );
00141     vbl->addWidget( mGlobalsBox, 0, Qt::AlignTop );
00142   }
00143   else {
00144     QGroupBox *agb = new QGroupBox( 1 /*column*/, Vertical, i18n("Filter Actions"), w );
00145     mActionLister = new KMFilterActionWidgetLister( agb );
00146     vbl->addWidget( agb, 0, Qt::AlignTop );
00147 
00148     mAdvOptsGroup = new QGroupBox ( 1 /*columns*/, Vertical,
00149                     i18n("Advanced Options"), w);
00150     {
00151       QWidget *adv_w = new QWidget( mAdvOptsGroup );
00152       QGridLayout *gl = new QGridLayout( adv_w, 4 /*rows*/, 4 /*cols*/,
00153                          0 /*border*/, spacingHint() );
00154       gl->setColStretch( 0, 1 );
00155       QLabel *l = new QLabel( i18n("Apply this filter"), adv_w );
00156       gl->addWidget( l, 0, 0, AlignLeft );
00157       mApplyOnIn = new QCheckBox( i18n("to &incoming messages"), adv_w );
00158       gl->addWidget( mApplyOnIn, 0, 1 );
00159       mApplyOnOut = new QCheckBox( i18n("to &sent messages"), adv_w );
00160       gl->addWidget( mApplyOnOut, 0, 2 );
00161       mApplyOnCtrlJ = new QCheckBox( i18n("on manual &filtering"), adv_w );
00162       gl->addWidget( mApplyOnCtrlJ, 0, 3 );
00163       mStopProcessingHere = new QCheckBox( i18n("If this filter &matches, stop processing here"), adv_w );
00164       gl->addMultiCellWidget( mStopProcessingHere, //1, 0, Qt::AlignLeft );
00165                   1, 1, /*from to row*/
00166                   0, 3 /*from to col*/ );
00167       mConfigureShortcut = new QCheckBox( i18n("Add this filter to the Apply Filter menu"), adv_w );
00168       gl->addMultiCellWidget( mConfigureShortcut, 2, 2, 0, 3 );
00169 
00170       QHBox *hbox = new QHBox( adv_w );
00171       mFilterActionLabel = new QLabel( i18n( "Icon for this filter:" ),
00172                                        hbox );
00173       mFilterActionLabel->setEnabled( false );
00174 
00175       mFilterActionIconButton = new KIconButton( hbox );
00176       mFilterActionLabel->setBuddy( mFilterActionIconButton );
00177       mFilterActionIconButton->setIconType( KIcon::NoGroup, KIcon::Any, true );
00178       mFilterActionIconButton->setIconSize( 16 );
00179       mFilterActionIconButton->setIcon( "gear" );
00180       mFilterActionIconButton->setEnabled( false );
00181 
00182       gl->addMultiCellWidget( hbox, 3, 3, 0, 3 );
00183     }
00184     vbl->addWidget( mAdvOptsGroup, 0, Qt::AlignTop );
00185   }
00186   // spacer:
00187   vbl->addStretch( 1 );
00188 
00189   // load the filter parts into the edit widgets
00190   connect( mFilterList, SIGNAL(filterSelected(KMFilter*)),
00191        this, SLOT(slotFilterSelected(KMFilter*)) );
00192 
00193   if (bPopFilter){
00194     // set the state of the global setting 'show later msgs'
00195     connect( mShowLaterBtn, SIGNAL(toggled(bool)),
00196              mFilterList, SLOT(slotShowLaterToggled(bool)));
00197 
00198     // set the action in the filter when changed
00199     connect( mActionGroup, SIGNAL(actionChanged(const KMPopFilterAction)),
00200          this, SLOT(slotActionChanged(const KMPopFilterAction)) );
00201   } else {
00202     // transfer changes from the 'Apply this filter on...'
00203     // combo box to the filter
00204     connect( mApplyOnIn, SIGNAL(clicked()),
00205          this, SLOT(slotApplicabilityChanged()) );
00206     connect( mApplyOnOut, SIGNAL(clicked()),
00207          this, SLOT(slotApplicabilityChanged()) );
00208     connect( mApplyOnCtrlJ, SIGNAL(clicked()),
00209          this, SLOT(slotApplicabilityChanged()) );
00210 
00211     // transfer changes from the 'stop processing here'
00212     // check box to the filter
00213     connect( mStopProcessingHere, SIGNAL(toggled(bool)),
00214          this, SLOT(slotStopProcessingButtonToggled(bool)) );
00215 
00216     connect( mConfigureShortcut, SIGNAL(toggled(bool)),
00217          this, SLOT(slotConfigureShortcutButtonToggled(bool)) );
00218 
00219     connect( mFilterActionIconButton, SIGNAL( iconChanged( QString ) ),
00220              this, SLOT( slotFilterActionIconChanged( QString ) ) );
00221   }
00222 
00223   // reset all widgets here
00224   connect( mFilterList, SIGNAL(resetWidgets()),
00225        this, SLOT(slotReset()) );
00226 
00227   connect( mFilterList, SIGNAL( applyWidgets() ),
00228            this, SLOT( slotUpdateFilter() ) );
00229 
00230   // support auto-naming the filter
00231   connect( mPatternEdit, SIGNAL(maybeNameChanged()),
00232        mFilterList, SLOT(slotUpdateFilterName()) );
00233 
00234   // apply changes on 'Apply'
00235   connect( this, SIGNAL(applyClicked()),
00236        mFilterList, SLOT(slotApplyFilterChanges()) );
00237 
00238   // apply changes on 'OK'
00239   connect( this, SIGNAL(okClicked()),
00240        mFilterList, SLOT(slotApplyFilterChanges()) );
00241 
00242   // save dialog size on 'OK'
00243   connect( this, SIGNAL(okClicked()),
00244        this, SLOT(slotSaveSize()) );
00245 
00246   // destruct the dialog on OK, close and Cancel
00247   connect( this, SIGNAL(finished()),
00248        this, SLOT(slotFinished()) );
00249 
00250   KConfigGroup geometry( KMKernel::config(), "Geometry");
00251   const char * configKey
00252     = bPopFilter ? "popFilterDialogSize" : "filterDialogSize";
00253   if ( geometry.hasKey( configKey ) )
00254     resize( geometry.readSizeEntry( configKey ) );
00255   else
00256     adjustSize();
00257 
00258   // load the filter list (emits filterSelected())
00259   mFilterList->loadFilterList();
00260 }
00261 
00262 void KMFilterDlg::slotFinished() {
00263     delayedDestruct();
00264 }
00265 
00266 void KMFilterDlg::slotSaveSize() {
00267   KConfigGroup geometry( KMKernel::config(), "Geometry" );
00268   geometry.writeEntry( bPopFilter ? "popFilterDialogSize" : "filterDialogSize", size() );
00269 }
00270 
00272 void KMFilterDlg::slotActionChanged(const KMPopFilterAction aAction)
00273 {
00274   mFilter->setAction(aAction);
00275 }
00276 
00277 void KMFilterDlg::slotFilterSelected( KMFilter* aFilter )
00278 {
00279   assert( aFilter );
00280 
00281   if (bPopFilter){
00282     mActionGroup->setAction( aFilter->action() );
00283     mGlobalsBox->setEnabled(true);
00284     mShowLaterBtn->setChecked(mFilterList->showLaterMsgs());
00285   } else {
00286     mActionLister->setActionList( aFilter->actions() );
00287 
00288     mAdvOptsGroup->setEnabled( true );
00289   }
00290 
00291   mPatternEdit->setSearchPattern( aFilter->pattern() );
00292   mFilter = aFilter;
00293 
00294   if (!bPopFilter) {
00295     kdDebug(5006) << "apply on inbound == "
00296           << aFilter->applyOnInbound() << endl;
00297     kdDebug(5006) << "apply on outbound == "
00298           << aFilter->applyOnOutbound() << endl;
00299     kdDebug(5006) << "apply on explicit == "
00300           << aFilter->applyOnExplicit() << endl;
00301 
00302     // NOTE: setting these values activates the slot that sets them in
00303     // the filter! So make sure we have the correct values _before_ we
00304     // set the first one:
00305     const bool applyOnIn = aFilter->applyOnInbound();
00306     const bool applyOnOut = aFilter->applyOnOutbound();
00307     const bool applyOnExplicit = aFilter->applyOnExplicit();
00308     const bool stopHere = aFilter->stopProcessingHere();
00309     const bool configureShortcut = aFilter->configureShortcut();
00310     const QString icon = aFilter->icon();
00311 
00312     mApplyOnIn->setChecked( applyOnIn );
00313     mApplyOnOut->setChecked( applyOnOut );
00314     mApplyOnCtrlJ->setChecked( applyOnExplicit );
00315     mStopProcessingHere->setChecked( stopHere );
00316     mConfigureShortcut->setChecked( configureShortcut );
00317     mFilterActionIconButton->setIcon( icon );
00318   }
00319 }
00320 
00321 void KMFilterDlg::slotReset()
00322 {
00323   mFilter = 0;
00324   mPatternEdit->reset();
00325 
00326   if(bPopFilter) {
00327     mActionGroup->reset();
00328     mGlobalsBox->setEnabled( false );
00329   } else {
00330     mActionLister->reset();
00331     mAdvOptsGroup->setEnabled( false );
00332   }
00333 }
00334 
00335 void KMFilterDlg::slotUpdateFilter()
00336 {
00337   mPatternEdit->updateSearchPattern();
00338   if ( !bPopFilter ) {
00339     mActionLister->updateActionList();
00340   }
00341 }
00342 
00343 void KMFilterDlg::slotApplicabilityChanged()
00344 {
00345   if ( !mFilter )
00346     return;
00347 
00348   mFilter->setApplyOnInbound( mApplyOnIn->isChecked() );
00349   mFilter->setApplyOnOutbound( mApplyOnOut->isChecked() );
00350   mFilter->setApplyOnExplicit( mApplyOnCtrlJ->isChecked() );
00351   kdDebug(5006) << "KMFilterDlg: setting filter to be applied at "
00352         << ( mFilter->applyOnInbound() ? "incoming " : "" )
00353         << ( mFilter->applyOnOutbound() ? "outgoing " : "" )
00354         << ( mFilter->applyOnExplicit() ? "explicit CTRL-J" : "" )
00355         << endl;
00356 }
00357 
00358 void KMFilterDlg::slotStopProcessingButtonToggled( bool aChecked )
00359 {
00360   if ( !mFilter )
00361     return;
00362 
00363   mFilter->setStopProcessingHere( aChecked );
00364 }
00365 
00366 void KMFilterDlg::slotConfigureShortcutButtonToggled( bool aChecked )
00367 {
00368   if ( !mFilter )
00369     return;
00370 
00371   mFilter->setConfigureShortcut( aChecked );
00372   mFilterActionIconButton->setEnabled( aChecked );
00373   mFilterActionLabel->setEnabled( aChecked );
00374 }
00375 
00376 void KMFilterDlg::slotFilterActionIconChanged( QString icon )
00377 {
00378   if ( !mFilter )
00379     return;
00380 
00381   mFilter->setIcon( icon );
00382 }
00383 
00384 //=============================================================================
00385 //
00386 // class KMFilterListBox (the filter list manipulator)
00387 //
00388 //=============================================================================
00389 
00390 KMFilterListBox::KMFilterListBox( const QString & title, QWidget *parent, const char* name, bool popFilter )
00391   : QGroupBox( 1, Horizontal, title, parent, name ),
00392     bPopFilter(popFilter)
00393 {
00394   mFilterList.setAutoDelete(TRUE);
00395   mIdxSelItem = -1;
00396 
00397   //----------- the list box
00398   mListBox = new QListBox(this);
00399   mListBox->setMinimumWidth(150);
00400   QWhatsThis::add( mListBox, i18n(_wt_filterlist) );
00401 
00402   //----------- the first row of buttons
00403   QHBox *hb = new QHBox(this);
00404   hb->setSpacing(4);
00405   mBtnUp = new QPushButton( QString::null, hb );
00406   mBtnUp->setAutoRepeat( true );
00407   mBtnUp->setPixmap( BarIcon( "up", KIcon::SizeSmall ) );
00408   mBtnUp->setMinimumSize( mBtnUp->sizeHint() * 1.2 );
00409   mBtnDown = new QPushButton( QString::null, hb );
00410   mBtnDown->setAutoRepeat( true );
00411   mBtnDown->setPixmap( BarIcon( "down", KIcon::SizeSmall ) );
00412   mBtnDown->setMinimumSize( mBtnDown->sizeHint() * 1.2 );
00413   QToolTip::add( mBtnUp, i18n("Up") );
00414   QToolTip::add( mBtnDown, i18n("Down") );
00415   QWhatsThis::add( mBtnUp, i18n(_wt_filterlist_up) );
00416   QWhatsThis::add( mBtnDown, i18n(_wt_filterlist_down) );
00417 
00418   //----------- the second row of buttons
00419   hb = new QHBox(this);
00420   hb->setSpacing(4);
00421   mBtnNew = new QPushButton( QString::null, hb );
00422   mBtnNew->setPixmap( BarIcon( "filenew", KIcon::SizeSmall ) );
00423   mBtnNew->setMinimumSize( mBtnNew->sizeHint() * 1.2 );
00424   mBtnCopy = new QPushButton( QString::null, hb );
00425   mBtnCopy->setPixmap( BarIcon( "editcopy", KIcon::SizeSmall ) );
00426   mBtnCopy->setMinimumSize( mBtnCopy->sizeHint() * 1.2 );
00427   mBtnDelete = new QPushButton( QString::null, hb );
00428   mBtnDelete->setPixmap( BarIcon( "editdelete", KIcon::SizeSmall ) );
00429   mBtnDelete->setMinimumSize( mBtnDelete->sizeHint() * 1.2 );
00430   mBtnRename = new QPushButton( i18n("Rename..."), hb );
00431   QToolTip::add( mBtnNew, i18n("New") );
00432   QToolTip::add( mBtnCopy, i18n("Copy") );
00433   QToolTip::add( mBtnDelete, i18n("Delete"));
00434   QWhatsThis::add( mBtnNew, i18n(_wt_filterlist_new) );
00435   QWhatsThis::add( mBtnCopy, i18n(_wt_filterlist_copy) );
00436   QWhatsThis::add( mBtnDelete, i18n(_wt_filterlist_delete) );
00437   QWhatsThis::add( mBtnRename, i18n(_wt_filterlist_rename) );
00438 
00439   // third row
00440   if ( !popFilter ) {
00441     hb = new QHBox( this );
00442     hb->setSpacing( 4 );
00443     QPushButton *btn = new QPushButton( i18n("Select Source Folders"), hb );
00444     connect( btn, SIGNAL(clicked()), SLOT(slotSelectSourceFolders()) );
00445   }
00446 
00447 
00448   //----------- now connect everything
00449   connect( mListBox, SIGNAL(highlighted(int)),
00450        this, SLOT(slotSelected(int)) );
00451   connect( mListBox, SIGNAL( doubleClicked ( QListBoxItem * )),
00452            this, SLOT( slotRename()) );
00453   connect( mBtnUp, SIGNAL(clicked()),
00454        this, SLOT(slotUp()) );
00455   connect( mBtnDown, SIGNAL(clicked()),
00456        this, SLOT(slotDown()) );
00457   connect( mBtnNew, SIGNAL(clicked()),
00458        this, SLOT(slotNew()) );
00459   connect( mBtnCopy, SIGNAL(clicked()),
00460        this, SLOT(slotCopy()) );
00461   connect( mBtnDelete, SIGNAL(clicked()),
00462        this, SLOT(slotDelete()) );
00463   connect( mBtnRename, SIGNAL(clicked()),
00464        this, SLOT(slotRename()) );
00465 
00466   // the dialog should call loadFilterList()
00467   // when all signals are connected.
00468   enableControls();
00469 }
00470 
00471 
00472 void KMFilterListBox::createFilter( const QCString & field,
00473                     const QString & value )
00474 {
00475   KMSearchRule *newRule = KMSearchRule::createInstance( field, KMSearchRule::FuncContains, value );
00476 
00477   KMFilter *newFilter = new KMFilter(0, bPopFilter);
00478   newFilter->pattern()->append( newRule );
00479   newFilter->pattern()->setName( QString("<%1>:%2").arg( field ).arg( value) );
00480 
00481   KMFilterActionDesc *desc = (*kmkernel->filterActionDict())["transfer"];
00482   if ( desc )
00483     newFilter->actions()->append( desc->create() );
00484 
00485   insertFilter( newFilter );
00486   enableControls();
00487 }
00488 
00489 bool KMFilterListBox::showLaterMsgs()
00490 {
00491     return mShowLater;
00492 }
00493 
00494 void KMFilterListBox::slotUpdateFilterName()
00495 {
00496   KMSearchPattern *p = mFilterList.at(mIdxSelItem)->pattern();
00497   if ( !p ) return;
00498 
00499   QString shouldBeName = p->name();
00500   QString displayedName = mListBox->text( mIdxSelItem );
00501 
00502   if ( shouldBeName.stripWhiteSpace().isEmpty() || shouldBeName[0] == '<' ) {
00503     // auto-naming of patterns
00504     if ( p->first() && !p->first()->field().stripWhiteSpace().isEmpty() )
00505       shouldBeName = QString( "<%1>: %2" ).arg( p->first()->field() ).arg( p->first()->contents() );
00506     else
00507       shouldBeName = "<" + i18n("unnamed") + ">";
00508     p->setName( shouldBeName );
00509   }
00510 
00511   if ( displayedName == shouldBeName ) return;
00512 
00513   mListBox->blockSignals(TRUE);
00514   mListBox->changeItem( shouldBeName, mIdxSelItem );
00515   mListBox->blockSignals(FALSE);
00516 }
00517 
00518 void KMFilterListBox::slotShowLaterToggled(bool aOn)
00519 {
00520   mShowLater = aOn;
00521 }
00522 
00523 void KMFilterListBox::slotApplyFilterChanges()
00524 {
00525   int oIdxSelItem = mIdxSelItem;
00526   // unselect all filters:
00527   mListBox->selectAll( FALSE );
00528   // maybe QListBox doesn't emit selected(-1) on unselect,
00529   // so we make sure the edit widgets receive an equivalent:
00530   emit resetWidgets();
00531   mIdxSelItem = -1;
00532   enableControls();
00533 
00534   // by now all edit widgets should have written back
00535   // their widget's data into our filter list.
00536 
00537   KMFilterMgr *fm;
00538   if (bPopFilter)
00539     fm = kmkernel->popFilterMgr();
00540   else
00541     fm = kmkernel->filterMgr();
00542 
00543   // block attemts to use filters (currently a no-op)
00544   fm->beginUpdate();
00545   fm->clear();
00546 
00547   QStringList emptyFilters;
00548   QPtrListIterator<KMFilter> it( mFilterList );
00549   for ( it.toFirst() ; it.current() ; ++it ) {
00550     KMFilter *f = new KMFilter( **it ); // deep copy
00551     f->purify();
00552     if ( !f->isEmpty() )
00553       // the filter is valid:
00554       fm->append( f );
00555     else {
00556       // the filter is invalid:
00557       emptyFilters << f->name();
00558       delete f;
00559     }
00560   }
00561   if (bPopFilter)
00562     fm->setShowLaterMsgs(mShowLater);
00563 
00564   // allow usage of the filters again.
00565   fm->endUpdate();
00566   fm->writeConfig();
00567 
00568   // report on invalid filters:
00569   if ( !emptyFilters.empty() ) {
00570     QString msg = i18n("The following filters have not been saved because they "
00571                "were invalid (e.g. containing no actions or no search "
00572                "rules).");
00573     KMessageBox::informationList( 0, msg, emptyFilters, QString::null,
00574                   "ShowInvalidFilterWarning" );
00575   }
00576 
00577   if ( oIdxSelItem >= 0 ) {
00578     mIdxSelItem = oIdxSelItem;
00579     mListBox->setSelected( oIdxSelItem, TRUE);
00580     slotSelected( mListBox->currentItem() );
00581   }
00582 }
00583 
00584 void KMFilterListBox::slotSelected( int aIdx )
00585 {
00586   mIdxSelItem = aIdx;
00587   // QPtrList::at(i) will return 0 if i is out of range.
00588   KMFilter *f = mFilterList.at(aIdx);
00589   if ( f )
00590     emit filterSelected( f );
00591   else
00592     emit resetWidgets();
00593   enableControls();
00594 }
00595 
00596 void KMFilterListBox::slotNew()
00597 {
00598   // just insert a new filter.
00599   insertFilter( new KMFilter(0, bPopFilter) );
00600   enableControls();
00601 }
00602 
00603 void KMFilterListBox::slotCopy()
00604 {
00605   if ( mIdxSelItem < 0 ) {
00606     kdDebug(5006) << "KMFilterListBox::slotCopy called while no filter is selected, ignoring." << endl;
00607     return;
00608   }
00609 
00610   // make sure that all changes are written to the filter before we copy it
00611   emit applyWidgets();
00612 
00613   KMFilter *filter = mFilterList.at( mIdxSelItem );
00614 
00615   // enableControls should make sure this method is
00616   // never called when no filter is selected.
00617   assert( filter );
00618 
00619   // inserts a copy of the current filter.
00620   insertFilter( new KMFilter( *filter ) );
00621   enableControls();
00622 }
00623 
00624 void KMFilterListBox::slotDelete()
00625 {
00626   if ( mIdxSelItem < 0 ) {
00627     kdDebug(5006) << "KMFilterListBox::slotDelete called while no filter is selected, ignoring." << endl;
00628     return;
00629   }
00630 
00631   int oIdxSelItem = mIdxSelItem;
00632   mIdxSelItem = -1;
00633   // unselect all
00634   mListBox->selectAll(FALSE);
00635   // broadcast that all widgets let go
00636   // of the filter
00637   emit resetWidgets();
00638 
00639   // remove the filter from both the filter list...
00640   mFilterList.remove( oIdxSelItem );
00641   // and the listbox
00642   mListBox->removeItem( oIdxSelItem );
00643 
00644   int count = (int)mListBox->count();
00645   // and set the new current item.
00646   if ( count > oIdxSelItem )
00647     // oIdxItem is still a valid index
00648     mListBox->setSelected( oIdxSelItem, TRUE );
00649   else if ( (int)mListBox->count() )
00650     // oIdxSelIdx is no longer valid, but the
00651     // list box isn't empty
00652     mListBox->setSelected( count - 1, TRUE );
00653   // the list is empty - keep index -1
00654 
00655   enableControls();
00656 }
00657 
00658 void KMFilterListBox::slotUp()
00659 {
00660   if ( mIdxSelItem < 0 ) {
00661     kdDebug(5006) << "KMFilterListBox::slotUp called while no filter is selected, ignoring." << endl;
00662     return;
00663   }
00664   if ( mIdxSelItem == 0 ) {
00665     kdDebug(5006) << "KMFilterListBox::slotUp called while the _topmost_ filter is selected, ignoring." << endl;
00666     return;
00667   }
00668 
00669   swapNeighbouringFilters( mIdxSelItem, mIdxSelItem - 1 );
00670   enableControls();
00671 }
00672 
00673 void KMFilterListBox::slotDown()
00674 {
00675   if ( mIdxSelItem < 0 ) {
00676     kdDebug(5006) << "KMFilterListBox::slotDown called while no filter is selected, ignoring." << endl;
00677     return;
00678   }
00679   if ( mIdxSelItem == (int)mListBox->count() - 1 ) {
00680     kdDebug(5006) << "KMFilterListBox::slotDown called while the _last_ filter is selected, ignoring." << endl;
00681     return;
00682   }
00683 
00684   swapNeighbouringFilters( mIdxSelItem, mIdxSelItem + 1);
00685   enableControls();
00686 }
00687 
00688 void KMFilterListBox::slotRename()
00689 {
00690   if ( mIdxSelItem < 0 ) {
00691     kdDebug(5006) << "KMFilterListBox::slotRename called while no filter is selected, ignoring." << endl;
00692     return;
00693   }
00694 
00695   bool okPressed = FALSE;
00696   KMFilter *filter = mFilterList.at( mIdxSelItem );
00697 
00698   // enableControls should make sure this method is
00699   // never called when no filter is selected.
00700   assert( filter );
00701 
00702   QString newName = KInputDialog::getText
00703     (
00704      i18n("Rename Filter"),
00705      i18n("Rename filter \"%1\" to:").arg( filter->pattern()->name() ) /*label*/,
00706      filter->pattern()->name() /* initial value */,
00707      &okPressed, topLevelWidget()
00708      );
00709 
00710   if ( !okPressed ) return;
00711 
00712   if ( newName.isEmpty() )
00713     // bait for slotUpdateFilterName to
00714     // use automatic naming again.
00715     filter->pattern()->setName( "<>" );
00716   else
00717     filter->pattern()->setName( newName );
00718 
00719   slotUpdateFilterName();
00720 }
00721 
00722 void KMFilterListBox::slotSelectSourceFolders()
00723 {
00724   FolderSetSelector dlg( kmkernel->getKMMainWidget()->folderTree(), this );
00725   dlg.setCaption( i18n( "Select Folders to Filter" ) );
00726   if ( !GlobalSettings::filterSourceFolders().isEmpty() )
00727     dlg.setSelectedFolders( GlobalSettings::filterSourceFolders() );
00728   if ( dlg.exec() == QDialog::Accepted ) {
00729     GlobalSettings::setFilterSourceFolders( dlg.selectedFolders() );
00730   }
00731 }
00732 
00733 void KMFilterListBox::enableControls()
00734 {
00735   bool theFirst = ( mIdxSelItem == 0 );
00736   bool theLast = ( mIdxSelItem >= (int)mFilterList.count() - 1 );
00737   bool aFilterIsSelected = ( mIdxSelItem >= 0 );
00738 
00739   mBtnUp->setEnabled( aFilterIsSelected && !theFirst );
00740   mBtnDown->setEnabled( aFilterIsSelected && !theLast );
00741   mBtnCopy->setEnabled( aFilterIsSelected );
00742   mBtnDelete->setEnabled( aFilterIsSelected );
00743   mBtnRename->setEnabled( aFilterIsSelected );
00744 
00745   if ( aFilterIsSelected )
00746     mListBox->ensureCurrentVisible();
00747 }
00748 
00749 void KMFilterListBox::loadFilterList()
00750 {
00751   assert(mListBox);
00752   setEnabled(FALSE);
00753   // we don't want the insertion to
00754   // cause flicker in the edit widgets.
00755   blockSignals(TRUE);
00756 
00757   // clear both lists
00758   mFilterList.clear();
00759   mListBox->clear();
00760 
00761     QPtrList<KMFilter> *manager;
00762   if(bPopFilter)
00763     {
00764     mShowLater = kmkernel->popFilterMgr()->showLaterMsgs();
00765         manager = kmkernel->popFilterMgr();
00766     }
00767     else
00768     {
00769         manager = kmkernel->filterMgr();
00770     }
00771 
00772   QPtrListIterator<KMFilter> it( *manager );
00773   for ( it.toFirst() ; it.current() ; ++it ) {
00774     mFilterList.append( new KMFilter( **it ) ); // deep copy
00775     mListBox->insertItem( (*it)->pattern()->name() );
00776   }
00777 
00778   blockSignals(FALSE);
00779   setEnabled(TRUE);
00780 
00781   // create an empty filter when there's none, to avoid a completely
00782   // disabled dialog (usability tests indicated that the new-filter
00783   // button is too hard to find that way):
00784   if ( !mListBox->count() )
00785     slotNew();
00786 
00787   assert( mListBox->count() > 0 );
00788   mListBox->setSelected( 0, true );
00789 
00790   enableControls();
00791 }
00792 
00793 void KMFilterListBox::insertFilter( KMFilter* aFilter )
00794 {
00795   // must be really a filter...
00796   assert( aFilter );
00797 
00798   // if mIdxSelItem < 0, QListBox::insertItem will append.
00799   mListBox->insertItem( aFilter->pattern()->name(), mIdxSelItem );
00800   if ( mIdxSelItem < 0 ) {
00801     // none selected -> append
00802     mFilterList.append( aFilter );
00803     mListBox->setSelected( mListBox->count() - 1, TRUE );
00804     //    slotSelected( mListBox->count() - 1 );
00805   } else {
00806     // insert just before selected
00807     mFilterList.insert( mIdxSelItem, aFilter );
00808     mListBox->setSelected( mIdxSelItem, TRUE );
00809     //    slotSelected( mIdxSelItem );
00810   }
00811 
00812 }
00813 
00814 void KMFilterListBox::swapNeighbouringFilters( int untouchedOne, int movedOne )
00815 {
00816   // must be neighbours...
00817   assert( untouchedOne - movedOne == 1 || movedOne - untouchedOne == 1 );
00818 
00819   // untouchedOne is at idx. to move it down(up),
00820   // remove item at idx+(-)1 w/o deleting it.
00821   QListBoxItem *item = mListBox->item( movedOne );
00822   mListBox->takeItem( item );
00823   // now selected item is at idx(idx-1), so
00824   // insert the other item at idx, ie. above(below).
00825   mListBox->insertItem( item, untouchedOne );
00826 
00827   KMFilter* filter = mFilterList.take( movedOne );
00828   mFilterList.insert( untouchedOne, filter );
00829 
00830   mIdxSelItem += movedOne - untouchedOne;
00831 }
00832 
00833 
00834 //=============================================================================
00835 //
00836 // class KMFilterActionWidget
00837 //
00838 //=============================================================================
00839 
00840 KMFilterActionWidget::KMFilterActionWidget( QWidget *parent, const char* name )
00841   : QHBox( parent, name )
00842 {
00843   int i;
00844   mActionList.setAutoDelete(TRUE);
00845 
00846   mComboBox = new QComboBox( FALSE, this );
00847   assert( mComboBox );
00848   mWidgetStack = new QWidgetStack(this);
00849   assert( mWidgetStack );
00850 
00851   setSpacing( 4 );
00852 
00853   QPtrListIterator<KMFilterActionDesc> it ( kmkernel->filterActionDict()->list() );
00854   for ( i=0, it.toFirst() ; it.current() ; ++it, ++i ) {
00855     //create an instance:
00856     KMFilterAction *a = (*it)->create();
00857     // append to the list of actions:
00858     mActionList.append( a );
00859     // add parameter widget to widget stack:
00860     mWidgetStack->addWidget( a->createParamWidget( mWidgetStack ), i );
00861     // add (i18n-ized) name to combo box
00862     mComboBox->insertItem( (*it)->label );
00863   }
00864   // widget for the case where no action is selected.
00865   mWidgetStack->addWidget( new QLabel( i18n("Please select an action."), mWidgetStack ), i );
00866   mWidgetStack->raiseWidget(i);
00867   mComboBox->insertItem( " " );
00868   mComboBox->setCurrentItem(i);
00869 
00870   // don't show scroll bars.
00871   mComboBox->setSizeLimit( mComboBox->count() );
00872   // layout management:
00873   // o the combo box is not to be made larger than it's sizeHint(),
00874   //   the parameter widget should grow instead.
00875   // o the whole widget takes all space horizontally, but is fixed vertically.
00876   mComboBox->adjustSize();
00877   mComboBox->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) );
00878   setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) );
00879   updateGeometry();
00880 
00881   // redirect focus to the filter action combo box
00882   setFocusProxy( mComboBox );
00883 
00884   // now connect the combo box and the widget stack
00885   connect( mComboBox, SIGNAL(activated(int)),
00886        mWidgetStack, SLOT(raiseWidget(int)) );
00887 }
00888 
00889 void KMFilterActionWidget::setAction( const KMFilterAction* aAction )
00890 {
00891   int i=0;
00892   bool found = FALSE;
00893   int count = mComboBox->count() - 1 ; // last entry is the empty one
00894   QString label = ( aAction ) ? aAction->label() : QString::null ;
00895 
00896   // find the index of typeOf(aAction) in mComboBox
00897   // and clear the other widgets on the way.
00898   for ( ; i < count ; i++ )
00899     if ( aAction && mComboBox->text(i) == label ) {
00900       //...set the parameter widget to the settings
00901       // of aAction...
00902       aAction->setParamWidgetValue( mWidgetStack->widget(i) );
00903       //...and show the correct entry of
00904       // the combo box
00905       mComboBox->setCurrentItem(i); // (mm) also raise the widget, but doesn't
00906       mWidgetStack->raiseWidget(i);
00907       found = TRUE;
00908     } else // clear the parameter widget
00909       mActionList.at(i)->clearParamWidget( mWidgetStack->widget(i) );
00910   if ( found ) return;
00911 
00912   // not found, so set the empty widget
00913   mComboBox->setCurrentItem( count ); // last item
00914   mWidgetStack->raiseWidget( count) ;
00915 }
00916 
00917 KMFilterAction * KMFilterActionWidget::action()
00918 {
00919   // look up the action description via the label
00920   // returned by QComboBox::currentText()...
00921   KMFilterActionDesc *desc = (*kmkernel->filterActionDict())[ mComboBox->currentText() ];
00922   if ( desc ) {
00923     // ...create an instance...
00924     KMFilterAction *fa = desc->create();
00925     if ( fa ) {
00926       // ...and apply the setting of the parameter widget.
00927       fa->applyParamWidgetValue( mWidgetStack->visibleWidget() );
00928       return fa;
00929     }
00930   }
00931 
00932   return 0;
00933 }
00934 
00935 //=============================================================================
00936 //
00937 // class KMFilterActionWidgetLister (the filter action editor)
00938 //
00939 //=============================================================================
00940 
00941 KMFilterActionWidgetLister::KMFilterActionWidgetLister( QWidget *parent, const char* name )
00942   : KWidgetLister( 1, FILTER_MAX_ACTIONS, parent, name )
00943 {
00944   mActionList = 0;
00945 }
00946 
00947 KMFilterActionWidgetLister::~KMFilterActionWidgetLister()
00948 {
00949 }
00950 
00951 void KMFilterActionWidgetLister::setActionList( QPtrList<KMFilterAction> *aList )
00952 {
00953   assert ( aList );
00954 
00955   if ( mActionList )
00956     regenerateActionListFromWidgets();
00957 
00958   mActionList = aList;
00959 
00960   ((QWidget*)parent())->setEnabled( TRUE );
00961 
00962   if ( aList->count() == 0 ) {
00963     slotClear();
00964     return;
00965   }
00966 
00967   int superfluousItems = (int)mActionList->count() - mMaxWidgets ;
00968   if ( superfluousItems > 0 ) {
00969     kdDebug(5006) << "KMFilterActionWidgetLister: Clipping action list to "
00970           << mMaxWidgets << " items!" << endl;
00971 
00972     for ( ; superfluousItems ; superfluousItems-- )
00973       mActionList->removeLast();
00974   }
00975 
00976   // set the right number of widgets
00977   setNumberOfShownWidgetsTo( mActionList->count() );
00978 
00979   // load the actions into the widgets
00980   QPtrListIterator<KMFilterAction> aIt( *mActionList );
00981   QPtrListIterator<QWidget> wIt( mWidgetList );
00982   for ( aIt.toFirst(), wIt.toFirst() ;
00983     aIt.current() && wIt.current() ; ++aIt, ++wIt )
00984     ((KMFilterActionWidget*)(*wIt))->setAction( (*aIt) );
00985 }
00986 
00987 void KMFilterActionWidgetLister::reset()
00988 {
00989   if ( mActionList )
00990     regenerateActionListFromWidgets();
00991 
00992   mActionList = 0;
00993   slotClear();
00994   ((QWidget*)parent())->setEnabled( FALSE );
00995 }
00996 
00997 QWidget* KMFilterActionWidgetLister::createWidget( QWidget *parent )
00998 {
00999   return new KMFilterActionWidget(parent);
01000 }
01001 
01002 void KMFilterActionWidgetLister::clearWidget( QWidget *aWidget )
01003 {
01004   if ( aWidget )
01005     ((KMFilterActionWidget*)aWidget)->setAction(0);
01006 }
01007 
01008 void KMFilterActionWidgetLister::regenerateActionListFromWidgets()
01009 {
01010   if ( !mActionList ) return;
01011 
01012   mActionList->clear();
01013 
01014   QPtrListIterator<QWidget> it( mWidgetList );
01015   for ( it.toFirst() ; it.current() ; ++it ) {
01016     KMFilterAction *a = ((KMFilterActionWidget*)(*it))->action();
01017     if ( a )
01018       mActionList->append( a );
01019   }
01020 
01021 }
01022 
01023 //=============================================================================
01024 //
01025 // class KMPopFilterActionWidget
01026 //
01027 //=============================================================================
01028 
01029 KMPopFilterActionWidget::KMPopFilterActionWidget( const QString& title, QWidget *parent, const char* name )
01030   : QVButtonGroup( title, parent, name )
01031 {
01032   mActionMap[Down] = new QRadioButton( i18n("&Download mail"), this );
01033   mActionMap[Later] = new QRadioButton( i18n("Download mail la&ter"), this );
01034   mActionMap[Delete] = new QRadioButton( i18n("D&elete mail from server"), this );
01035   mIdMap[id(mActionMap[Later])] = Later;
01036   mIdMap[id(mActionMap[Down])] = Down;
01037   mIdMap[id(mActionMap[Delete])] = Delete;
01038 
01039   connect( this, SIGNAL(clicked(int)),
01040        this, SLOT( slotActionClicked(int)) );
01041 }
01042 
01043 void KMPopFilterActionWidget::setAction( KMPopFilterAction aAction )
01044 {
01045   if( aAction == NoAction)
01046   {
01047     aAction = Later;
01048   }
01049 
01050   mAction = aAction;
01051 
01052   blockSignals( true );
01053   if(!mActionMap[aAction]->isChecked())
01054   {
01055     mActionMap[aAction]->setChecked(true);
01056   }
01057   blockSignals( false );
01058 
01059   setEnabled(true);
01060 }
01061 
01062 KMPopFilterAction  KMPopFilterActionWidget::action()
01063 {
01064   return mAction;
01065 }
01066 
01067 void KMPopFilterActionWidget::slotActionClicked(int aId)
01068 {
01069   emit actionChanged(mIdMap[aId]);
01070   setAction(mIdMap[aId]);
01071 }
01072 
01073 void KMPopFilterActionWidget::reset()
01074 {
01075   blockSignals(TRUE);
01076   mActionMap[Down]->setChecked( TRUE );
01077   blockSignals(FALSE);
01078 
01079   setEnabled( FALSE );
01080 }
01081 
01082 #include "kmfilterdlg.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Aug 2 09:55:11 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003