kmail

kmsearchpatternedit.cpp
00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmsearchpatternedit.cpp
00003 // Author: Marc Mutz <Marc@Mutz.com>
00004 // This code is under GPL
00005 
00006 #include <config.h>
00007 #include "kmsearchpatternedit.h"
00008 
00009 #include "kmsearchpattern.h"
00010 #include "rulewidgethandlermanager.h"
00011 using KMail::RuleWidgetHandlerManager;
00012 
00013 #include <tdelocale.h>
00014 #include <kdialog.h>
00015 #include <kdebug.h>
00016 
00017 #include <tqradiobutton.h>
00018 #include <tqcombobox.h>
00019 #include <tqbuttongroup.h>
00020 #include <tqwidgetstack.h>
00021 #include <tqlayout.h>
00022 
00023 #include <assert.h>
00024 
00025 // Definition of special rule field strings
00026 // Note: Also see KMSearchRule::matches() and ruleFieldToEnglish() if
00027 //       you change the following i18n-ized strings!
00028 // Note: The index of the values in the following array has to correspond to
00029 //       the value of the entries in the enum in KMSearchRuleWidget.
00030 static const struct {
00031   const char *internalName;
00032   const char *displayName;
00033 } SpecialRuleFields[] = {
00034   { "<message>",     I18N_NOOP( "Complete Message" )       },
00035   { "<body>",        I18N_NOOP( "Body of Message" )          },
00036   { "<any header>",  I18N_NOOP( "Anywhere in Headers" )    },
00037   { "<recipients>",  I18N_NOOP( "All Recipients" )    },
00038   { "<size>",        I18N_NOOP( "Size in Bytes" ) },
00039   { "<age in days>", I18N_NOOP( "Age in Days" )   },
00040   { "<status>",      I18N_NOOP( "Message Status" )        },
00041   { "Subject",       I18N_NOOP( "Subject" )  },
00042   { "From",          I18N_NOOP( "From" )  },
00043   { "To",            I18N_NOOP( "To" )  },
00044   { "CC",            I18N_NOOP( "CC" )  },
00045   { "Reply-To",      I18N_NOOP( "Reply To" )  },
00046   { "Organization",  I18N_NOOP( "Organization" )  }
00047 };
00048 static const int SpecialRuleFieldsCount =
00049   sizeof( SpecialRuleFields ) / sizeof( *SpecialRuleFields );
00050 
00051 //=============================================================================
00052 //
00053 // class KMSearchRuleWidget
00054 //
00055 //=============================================================================
00056 
00057 KMSearchRuleWidget::KMSearchRuleWidget( TQWidget *parent, KMSearchRule *aRule,
00058                                         const char *name, bool headersOnly,
00059                                         bool absoluteDates )
00060   : TQWidget( parent, name ),
00061     mRuleField( 0 ),
00062     mFunctionStack( 0 ),
00063     mValueStack( 0 ),
00064     mAbsoluteDates( absoluteDates )
00065 {
00066   initFieldList( headersOnly, absoluteDates );
00067   initWidget();
00068 
00069   if ( aRule )
00070     setRule( aRule );
00071   else
00072     reset();
00073 }
00074 
00075 void KMSearchRuleWidget::setHeadersOnly( bool headersOnly )
00076 {
00077   KMSearchRule* srule = rule();
00078   TQCString currentText = srule->field();
00079   delete srule;
00080   initFieldList( headersOnly, mAbsoluteDates );
00081 
00082   mRuleField->clear();
00083   mRuleField->insertStringList( mFilterFieldList );
00084   mRuleField->setSizeLimit( mRuleField->count() );
00085   mRuleField->adjustSize();
00086 
00087   if (( currentText != "<message>") &&
00088       ( currentText != "<body>"))
00089     mRuleField->changeItem( TQString(TQString::fromAscii( currentText )), 0 );
00090   else
00091     mRuleField->changeItem( TQString(), 0 );
00092 }
00093 
00094 void KMSearchRuleWidget::initWidget()
00095 {
00096   TQHBoxLayout * hlay = new TQHBoxLayout( this, 0, KDialog::spacingHint() );
00097 
00098   // initialize the header field combo box
00099   mRuleField = new TQComboBox( true, this, "mRuleField" );
00100   mRuleField->insertStringList( mFilterFieldList );
00101   // don't show sliders when popping up this menu
00102   mRuleField->setSizeLimit( mRuleField->count() );
00103   mRuleField->adjustSize();
00104   hlay->addWidget( mRuleField );
00105 
00106   // initialize the function/value widget stack
00107   mFunctionStack = new TQWidgetStack( this, "mFunctionStack" );
00108   //Don't expand the widget in vertical direction
00109   mFunctionStack->setSizePolicy( TQSizePolicy::Preferred,TQSizePolicy::Fixed );
00110 
00111   hlay->addWidget( mFunctionStack );
00112 
00113   mValueStack = new TQWidgetStack( this, "mValueStack" );
00114   mValueStack->setSizePolicy( TQSizePolicy::Preferred,TQSizePolicy::Fixed );
00115   hlay->addWidget( mValueStack );
00116   hlay->setStretchFactor( mValueStack, 10 );
00117 
00118   RuleWidgetHandlerManager::instance()->createWidgets( mFunctionStack,
00119                                                        mValueStack,
00120                                                        TQT_TQOBJECT(this) );
00121 
00122   // redirect focus to the header field combo box
00123   setFocusProxy( mRuleField );
00124 
00125   connect( mRuleField, TQT_SIGNAL( activated( const TQString & ) ),
00126        this, TQT_SLOT( slotRuleFieldChanged( const TQString & ) ) );
00127   connect( mRuleField, TQT_SIGNAL( textChanged( const TQString & ) ),
00128        this, TQT_SLOT( slotRuleFieldChanged( const TQString & ) ) );
00129   connect( mRuleField, TQT_SIGNAL( textChanged( const TQString & ) ),
00130            this, TQT_SIGNAL( fieldChanged( const TQString & ) ) );
00131 }
00132 
00133 void KMSearchRuleWidget::setRule( KMSearchRule *aRule )
00134 {
00135   assert ( aRule );
00136 
00137 //  kdDebug(5006) << "KMSearchRuleWidget::setRule( "
00138 //                << aRule->asString() << " )" << endl;
00139 
00140   //--------------set the field
00141   int i = indexOfRuleField( aRule->field() );
00142 
00143   mRuleField->blockSignals( true );
00144 
00145   if ( i < 0 ) { // not found -> user defined field
00146     mRuleField->changeItem( TQString::fromLatin1( aRule->field() ), 0 );
00147     i = 0;
00148   } else { // found in the list of predefined fields
00149     mRuleField->changeItem( TQString(), 0 );
00150   }
00151 
00152   mRuleField->setCurrentItem( i );
00153   mRuleField->blockSignals( false );
00154 
00155   RuleWidgetHandlerManager::instance()->setRule( mFunctionStack, mValueStack,
00156                                                  aRule );
00157 }
00158 
00159 KMSearchRule* KMSearchRuleWidget::rule() const {
00160   const TQCString ruleField = ruleFieldToEnglish( mRuleField->currentText() );
00161   const KMSearchRule::Function function =
00162     RuleWidgetHandlerManager::instance()->function( ruleField,
00163                                                     mFunctionStack );
00164   const TQString value =
00165     RuleWidgetHandlerManager::instance()->value( ruleField, mFunctionStack,
00166                                                  mValueStack );
00167 
00168   return KMSearchRule::createInstance( ruleField, function, value );
00169 }
00170 
00171 void KMSearchRuleWidget::reset()
00172 {
00173   mRuleField->blockSignals( true );
00174   mRuleField->changeItem( "", 0 );
00175   mRuleField->setCurrentItem( 0 );
00176   mRuleField->blockSignals( false );
00177 
00178   RuleWidgetHandlerManager::instance()->reset( mFunctionStack, mValueStack );
00179 }
00180 
00181 void KMSearchRuleWidget::slotFunctionChanged()
00182 {
00183   const TQCString ruleField = ruleFieldToEnglish( mRuleField->currentText() );
00184   RuleWidgetHandlerManager::instance()->update( ruleField,
00185                                                 mFunctionStack,
00186                                                 mValueStack );
00187 }
00188 
00189 void KMSearchRuleWidget::slotValueChanged()
00190 {
00191   const TQCString ruleField = ruleFieldToEnglish( mRuleField->currentText() );
00192   const TQString prettyValue =
00193     RuleWidgetHandlerManager::instance()->prettyValue( ruleField,
00194                                                        mFunctionStack,
00195                                                        mValueStack );
00196   emit contentsChanged( prettyValue );
00197 }
00198 
00199 TQCString KMSearchRuleWidget::ruleFieldToEnglish( const TQString & i18nVal )
00200 {
00201   for ( int i = 0; i < SpecialRuleFieldsCount; ++i ) {
00202     if ( i18nVal == i18n( SpecialRuleFields[i].displayName ) )
00203       return SpecialRuleFields[i].internalName;
00204   }
00205   return i18nVal.latin1();
00206 }
00207 
00208 int KMSearchRuleWidget::ruleFieldToId( const TQString & i18nVal )
00209 {
00210   for ( int i = 0; i < SpecialRuleFieldsCount; ++i ) {
00211     if ( i18nVal == i18n( SpecialRuleFields[i].displayName ) )
00212       return i;
00213   }
00214   return -1; // no pseudo header
00215 }
00216 
00217 static TQString displayNameFromInternalName( const TQString & internal )
00218 {
00219   for ( int i = 0; i < SpecialRuleFieldsCount; ++i ) {
00220     if ( internal == SpecialRuleFields[i].internalName )
00221       return i18n(SpecialRuleFields[i].displayName);
00222   }
00223   return internal.latin1();
00224 }
00225 
00226 
00227 
00228 int KMSearchRuleWidget::indexOfRuleField( const TQCString & aName ) const
00229 {
00230   if ( aName.isEmpty() )
00231     return -1;
00232 
00233   TQString i18n_aName = displayNameFromInternalName( aName );
00234 
00235   for ( int i = 1; i < mRuleField->count(); ++i ) {
00236     if ( mRuleField->text( i ) == i18n_aName )
00237       return i;
00238   }
00239 
00240   return -1;
00241 }
00242 
00243 void KMSearchRuleWidget::initFieldList( bool headersOnly, bool absoluteDates )
00244 {
00245   mFilterFieldList.clear();
00246   mFilterFieldList.append(""); // empty entry for user input
00247   if( !headersOnly ) {
00248     mFilterFieldList.append( i18n( SpecialRuleFields[Message].displayName ) );
00249     mFilterFieldList.append( i18n( SpecialRuleFields[Body].displayName ) );
00250   }
00251   mFilterFieldList.append( i18n( SpecialRuleFields[AnyHeader].displayName ) );
00252   mFilterFieldList.append( i18n( SpecialRuleFields[Recipients].displayName ) );
00253   mFilterFieldList.append( i18n( SpecialRuleFields[Size].displayName ) );
00254   if ( !absoluteDates )
00255     mFilterFieldList.append( i18n( SpecialRuleFields[AgeInDays].displayName ) );
00256   mFilterFieldList.append( i18n( SpecialRuleFields[Subject].displayName ) );
00257   mFilterFieldList.append( i18n( SpecialRuleFields[From].displayName ) );
00258   mFilterFieldList.append( i18n( SpecialRuleFields[To].displayName ) );
00259   mFilterFieldList.append( i18n( SpecialRuleFields[CC].displayName ) );
00260   mFilterFieldList.append( i18n( SpecialRuleFields[ReplyTo].displayName ) );
00261   mFilterFieldList.append( i18n( SpecialRuleFields[Organization].displayName ) );
00262 
00263   // these others only represent message headers and you can add to
00264   // them as you like
00265   mFilterFieldList.append("List-Id");
00266   mFilterFieldList.append("Resent-From");
00267   mFilterFieldList.append("X-Loop");
00268   mFilterFieldList.append("X-Mailing-List");
00269   mFilterFieldList.append("X-Spam-Flag");
00270 }
00271 
00272 void KMSearchRuleWidget::slotRuleFieldChanged( const TQString & field )
00273 {
00274   RuleWidgetHandlerManager::instance()->update( ruleFieldToEnglish( field ),
00275                                                 mFunctionStack,
00276                                                 mValueStack );
00277 }
00278 
00279 //=============================================================================
00280 //
00281 // class KMFilterActionWidgetLister (the filter action editor)
00282 //
00283 //=============================================================================
00284 
00285 KMSearchRuleWidgetLister::KMSearchRuleWidgetLister( TQWidget *parent, const char* name, bool headersOnly, bool absoluteDates )
00286   : KWidgetLister( 2, FILTER_MAX_RULES, parent, name )
00287 {
00288   mRuleList = 0;
00289   mHeadersOnly = headersOnly;
00290   mAbsoluteDates = absoluteDates;
00291 }
00292 
00293 KMSearchRuleWidgetLister::~KMSearchRuleWidgetLister()
00294 {
00295 }
00296 
00297 void KMSearchRuleWidgetLister::setRuleList( TQPtrList<KMSearchRule> *aList )
00298 {
00299   assert ( aList );
00300 
00301   if ( mRuleList && mRuleList != aList )
00302     regenerateRuleListFromWidgets();
00303 
00304   mRuleList = aList;
00305 
00306   if ( mWidgetList.first() ) // move this below next 'if'?
00307     mWidgetList.first()->blockSignals(true);
00308 
00309   if ( aList->count() == 0 ) {
00310     slotClear();
00311     mWidgetList.first()->blockSignals(false);
00312     return;
00313   }
00314 
00315   int superfluousItems = (int)mRuleList->count() - mMaxWidgets ;
00316   if ( superfluousItems > 0 ) {
00317     kdDebug(5006) << "KMSearchRuleWidgetLister: Clipping rule list to "
00318           << mMaxWidgets << " items!" << endl;
00319 
00320     for ( ; superfluousItems ; superfluousItems-- )
00321       mRuleList->removeLast();
00322   }
00323 
00324   // HACK to workaround regression in TQt 3.1.3 and TQt 3.2.0 (fixes bug #63537)
00325   setNumberOfShownWidgetsTo( TQMAX((int)mRuleList->count(),mMinWidgets)+1 );
00326   // set the right number of widgets
00327   setNumberOfShownWidgetsTo( TQMAX((int)mRuleList->count(),mMinWidgets) );
00328 
00329   // load the actions into the widgets
00330   TQPtrListIterator<KMSearchRule> rIt( *mRuleList );
00331   TQPtrListIterator<TQWidget> wIt( mWidgetList );
00332   for ( rIt.toFirst(), wIt.toFirst() ;
00333     rIt.current() && wIt.current() ; ++rIt, ++wIt ) {
00334     static_cast<KMSearchRuleWidget*>(*wIt)->setRule( (*rIt) );
00335   }
00336   for ( ; wIt.current() ; ++wIt )
00337     ((KMSearchRuleWidget*)(*wIt))->reset();
00338 
00339   assert( mWidgetList.first() );
00340   mWidgetList.first()->blockSignals(false);
00341 }
00342 
00343 void KMSearchRuleWidgetLister::setHeadersOnly( bool headersOnly )
00344 {
00345   TQPtrListIterator<TQWidget> wIt( mWidgetList );
00346   for ( wIt.toFirst() ; wIt.current() ; ++wIt ) {
00347     (static_cast<KMSearchRuleWidget*>(*wIt))->setHeadersOnly( headersOnly );
00348   }
00349 }
00350 
00351 void KMSearchRuleWidgetLister::reset()
00352 {
00353   if ( mRuleList )
00354     regenerateRuleListFromWidgets();
00355 
00356   mRuleList = 0;
00357   slotClear();
00358 }
00359 
00360 TQWidget* KMSearchRuleWidgetLister::createWidget( TQWidget *parent )
00361 {
00362   return new KMSearchRuleWidget(parent, 0, 0, mHeadersOnly, mAbsoluteDates);
00363 }
00364 
00365 void KMSearchRuleWidgetLister::clearWidget( TQWidget *aWidget )
00366 {
00367   if ( aWidget )
00368     ((KMSearchRuleWidget*)aWidget)->reset();
00369 }
00370 
00371 void KMSearchRuleWidgetLister::regenerateRuleListFromWidgets()
00372 {
00373   if ( !mRuleList ) return;
00374 
00375   mRuleList->clear();
00376 
00377   TQPtrListIterator<TQWidget> it( mWidgetList );
00378   for ( it.toFirst() ; it.current() ; ++it ) {
00379     KMSearchRule *r = ((KMSearchRuleWidget*)(*it))->rule();
00380     if ( r )
00381       mRuleList->append( r );
00382   }
00383 }
00384 
00385 
00386 
00387 
00388 //=============================================================================
00389 //
00390 // class KMSearchPatternEdit
00391 //
00392 //=============================================================================
00393 
00394 KMSearchPatternEdit::KMSearchPatternEdit(TQWidget *parent, const char *name, bool headersOnly, bool absoluteDates )
00395   : TQGroupBox( 1/*columns*/, Qt::Horizontal, parent, name )
00396 {
00397   setTitle( i18n("Search Criteria") );
00398   initLayout( headersOnly, absoluteDates );
00399 }
00400 
00401 KMSearchPatternEdit::KMSearchPatternEdit(const TQString & title, TQWidget *parent, const char *name, bool headersOnly, bool absoluteDates)
00402   : TQGroupBox( 1/*column*/, Qt::Horizontal, title, parent, name )
00403 {
00404   initLayout( headersOnly, absoluteDates );
00405 }
00406 
00407 KMSearchPatternEdit::~KMSearchPatternEdit()
00408 {
00409 }
00410 
00411 void KMSearchPatternEdit::initLayout(bool headersOnly, bool absoluteDates)
00412 {
00413   //------------the radio buttons
00414   mAllRBtn = new TQRadioButton( i18n("Match a&ll of the following"), this, "mAllRBtn" );
00415   mAnyRBtn = new TQRadioButton( i18n("Match an&y of the following"), this, "mAnyRBtn" );
00416 
00417   mAllRBtn->setChecked(true);
00418   mAnyRBtn->setChecked(false);
00419 
00420   TQButtonGroup *bg = new TQButtonGroup( this );
00421   bg->hide();
00422   bg->insert( mAllRBtn, (int)KMSearchPattern::OpAnd );
00423   bg->insert( mAnyRBtn, (int)KMSearchPattern::OpOr );
00424 
00425   //------------the list of KMSearchRuleWidget's
00426   mRuleLister = new KMSearchRuleWidgetLister( this, "swl", headersOnly, absoluteDates );
00427   mRuleLister->slotClear();
00428 
00429   //------------connect a few signals
00430   connect( bg, TQT_SIGNAL(clicked(int)),
00431        this, TQT_SLOT(slotRadioClicked(int)) );
00432 
00433   KMSearchRuleWidget *srw = (KMSearchRuleWidget*)mRuleLister->mWidgetList.first();
00434   if ( srw ) {
00435     connect( srw, TQT_SIGNAL(fieldChanged(const TQString &)),
00436          this, TQT_SLOT(slotAutoNameHack()) );
00437     connect( srw, TQT_SIGNAL(contentsChanged(const TQString &)),
00438          this, TQT_SLOT(slotAutoNameHack()) );
00439   } else
00440     kdDebug(5006) << "KMSearchPatternEdit: no first KMSearchRuleWidget, though slotClear() has been called!" << endl;
00441 }
00442 
00443 void KMSearchPatternEdit::setSearchPattern( KMSearchPattern *aPattern )
00444 {
00445   assert( aPattern );
00446 
00447   mRuleLister->setRuleList( aPattern );
00448 
00449   mPattern = aPattern;
00450 
00451   blockSignals(true);
00452   if ( mPattern->op() == KMSearchPattern::OpOr )
00453     mAnyRBtn->setChecked(true);
00454   else
00455     mAllRBtn->setChecked(true);
00456   blockSignals(false);
00457 
00458   setEnabled( true );
00459 }
00460 
00461 void KMSearchPatternEdit::setHeadersOnly( bool headersOnly )
00462 {
00463   mRuleLister->setHeadersOnly( headersOnly );
00464 }
00465 
00466 void KMSearchPatternEdit::reset()
00467 {
00468   mRuleLister->reset();
00469 
00470   blockSignals(true);
00471   mAllRBtn->setChecked( true );
00472   blockSignals(false);
00473 
00474   setEnabled( false );
00475 }
00476 
00477 void KMSearchPatternEdit::slotRadioClicked(int aIdx)
00478 {
00479   if ( mPattern )
00480     mPattern->setOp( (KMSearchPattern::Operator)aIdx );
00481 }
00482 
00483 void KMSearchPatternEdit::slotAutoNameHack()
00484 {
00485   mRuleLister->regenerateRuleListFromWidgets();
00486   emit maybeNameChanged();
00487 }
00488 
00489 #include "kmsearchpatternedit.moc"