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