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