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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 21 19:46:47 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003