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"