kmail

kmsearchpatternedit.cpp
1 // kmsearchpatternedit.cpp
2 // Author: Marc Mutz <Marc@Mutz.com>
3 // This code is under GPL
4 
5 #include <config.h>
6 #include "kmsearchpatternedit.h"
7 
8 #include "kmsearchpattern.h"
9 #include "rulewidgethandlermanager.h"
11 
12 #include <tdelocale.h>
13 #include <kdialog.h>
14 #include <kdebug.h>
15 
16 #include <tqradiobutton.h>
17 #include <tqcombobox.h>
18 #include <tqbuttongroup.h>
19 #include <tqwidgetstack.h>
20 #include <tqlayout.h>
21 
22 #include <assert.h>
23 
24 // Definition of special rule field strings
25 // Note: Also see KMSearchRule::matches() and ruleFieldToEnglish() if
26 // you change the following i18n-ized strings!
27 // Note: The index of the values in the following array has to correspond to
28 // the value of the entries in the enum in KMSearchRuleWidget.
29 static const struct {
30  const char *internalName;
31  const char *displayName;
32 } SpecialRuleFields[] = {
33  { "<message>", I18N_NOOP( "Complete Message" ) },
34  { "<body>", I18N_NOOP( "Body of Message" ) },
35  { "<any header>", I18N_NOOP( "Anywhere in Headers" ) },
36  { "<recipients>", I18N_NOOP( "All Recipients" ) },
37  { "<size>", I18N_NOOP( "Size in Bytes" ) },
38  { "<age in days>", I18N_NOOP( "Age in Days" ) },
39  { "<status>", I18N_NOOP( "Message Status" ) },
40  { "Subject", I18N_NOOP( "Subject" ) },
41  { "From", I18N_NOOP( "From" ) },
42  { "To", I18N_NOOP( "To" ) },
43  { "CC", I18N_NOOP( "CC" ) },
44  { "Reply-To", I18N_NOOP( "Reply To" ) },
45  { "Organization", I18N_NOOP( "Organization" ) }
46 };
47 static const int SpecialRuleFieldsCount =
48  sizeof( SpecialRuleFields ) / sizeof( *SpecialRuleFields );
49 
50 //=============================================================================
51 //
52 // class KMSearchRuleWidget
53 //
54 //=============================================================================
55 
57  const char *name, bool headersOnly,
58  bool absoluteDates )
59  : TQWidget( parent, name ),
60  mRuleField( 0 ),
61  mFunctionStack( 0 ),
62  mValueStack( 0 ),
63  mAbsoluteDates( absoluteDates )
64 {
65  initFieldList( headersOnly, absoluteDates );
66  initWidget();
67 
68  if ( aRule )
69  setRule( aRule );
70  else
71  reset();
72 }
73 
74 void KMSearchRuleWidget::setHeadersOnly( bool headersOnly )
75 {
76  KMSearchRule* srule = rule();
77  TQCString currentText = srule->field();
78  delete srule;
79  initFieldList( headersOnly, mAbsoluteDates );
80 
81  mRuleField->clear();
82  mRuleField->insertStringList( mFilterFieldList );
83  mRuleField->setSizeLimit( mRuleField->count() );
84  mRuleField->adjustSize();
85 
86  if (( currentText != "<message>") &&
87  ( currentText != "<body>"))
88  mRuleField->changeItem( TQString(TQString::fromAscii( currentText )), 0 );
89  else
90  mRuleField->changeItem( TQString(), 0 );
91 }
92 
93 void KMSearchRuleWidget::initWidget()
94 {
95  TQHBoxLayout * hlay = new TQHBoxLayout( this, 0, KDialog::spacingHint() );
96 
97  // initialize the header field combo box
98  mRuleField = new TQComboBox( true, this, "mRuleField" );
99  mRuleField->insertStringList( mFilterFieldList );
100  // don't show sliders when popping up this menu
101  mRuleField->setSizeLimit( mRuleField->count() );
102  mRuleField->adjustSize();
103  hlay->addWidget( mRuleField );
104 
105  // initialize the function/value widget stack
106  mFunctionStack = new TQWidgetStack( this, "mFunctionStack" );
107  //Don't expand the widget in vertical direction
108  mFunctionStack->setSizePolicy( TQSizePolicy::Preferred,TQSizePolicy::Fixed );
109 
110  hlay->addWidget( mFunctionStack );
111 
112  mValueStack = new TQWidgetStack( this, "mValueStack" );
113  mValueStack->setSizePolicy( TQSizePolicy::Preferred,TQSizePolicy::Fixed );
114  hlay->addWidget( mValueStack );
115  hlay->setStretchFactor( mValueStack, 10 );
116 
117  RuleWidgetHandlerManager::instance()->createWidgets( mFunctionStack,
118  mValueStack,
119  this );
120 
121  // redirect focus to the header field combo box
122  setFocusProxy( mRuleField );
123 
124  connect( mRuleField, TQ_SIGNAL( activated( const TQString & ) ),
125  this, TQ_SLOT( slotRuleFieldChanged( const TQString & ) ) );
126  connect( mRuleField, TQ_SIGNAL( textChanged( const TQString & ) ),
127  this, TQ_SLOT( slotRuleFieldChanged( const TQString & ) ) );
128  connect( mRuleField, TQ_SIGNAL( textChanged( const TQString & ) ),
129  this, TQ_SIGNAL( fieldChanged( const TQString & ) ) );
130 }
131 
133 {
134  assert ( aRule );
135 
136 // kdDebug(5006) << "KMSearchRuleWidget::setRule( "
137 // << aRule->asString() << " )" << endl;
138 
139  //--------------set the field
140  int i = indexOfRuleField( aRule->field() );
141 
142  mRuleField->blockSignals( true );
143 
144  if ( i < 0 ) { // not found -> user defined field
145  mRuleField->changeItem( TQString::fromLatin1( aRule->field() ), 0 );
146  i = 0;
147  } else { // found in the list of predefined fields
148  mRuleField->changeItem( TQString(), 0 );
149  }
150 
151  mRuleField->setCurrentItem( i );
152  mRuleField->blockSignals( false );
153 
154  RuleWidgetHandlerManager::instance()->setRule( mFunctionStack, mValueStack,
155  aRule );
156 }
157 
159  const TQCString ruleField = ruleFieldToEnglish( mRuleField->currentText() );
160  const KMSearchRule::Function function =
161  RuleWidgetHandlerManager::instance()->function( ruleField,
162  mFunctionStack );
163  const TQString value =
164  RuleWidgetHandlerManager::instance()->value( ruleField, mFunctionStack,
165  mValueStack );
166 
167  return KMSearchRule::createInstance( ruleField, function, value );
168 }
169 
171 {
172  mRuleField->blockSignals( true );
173  mRuleField->changeItem( "", 0 );
174  mRuleField->setCurrentItem( 0 );
175  mRuleField->blockSignals( false );
176 
177  RuleWidgetHandlerManager::instance()->reset( mFunctionStack, mValueStack );
178 }
179 
180 void KMSearchRuleWidget::slotFunctionChanged()
181 {
182  const TQCString ruleField = ruleFieldToEnglish( mRuleField->currentText() );
183  RuleWidgetHandlerManager::instance()->update( ruleField,
184  mFunctionStack,
185  mValueStack );
186 }
187 
188 void KMSearchRuleWidget::slotValueChanged()
189 {
190  const TQCString ruleField = ruleFieldToEnglish( mRuleField->currentText() );
191  const TQString prettyValue =
192  RuleWidgetHandlerManager::instance()->prettyValue( ruleField,
193  mFunctionStack,
194  mValueStack );
195  emit contentsChanged( prettyValue );
196 }
197 
198 TQCString KMSearchRuleWidget::ruleFieldToEnglish( const TQString & i18nVal )
199 {
200  for ( int i = 0; i < SpecialRuleFieldsCount; ++i ) {
201  if ( i18nVal == i18n( SpecialRuleFields[i].displayName ) )
202  return SpecialRuleFields[i].internalName;
203  }
204  return i18nVal.latin1();
205 }
206 
207 int KMSearchRuleWidget::ruleFieldToId( const TQString & i18nVal )
208 {
209  for ( int i = 0; i < SpecialRuleFieldsCount; ++i ) {
210  if ( i18nVal == i18n( SpecialRuleFields[i].displayName ) )
211  return i;
212  }
213  return -1; // no pseudo header
214 }
215 
216 static TQString displayNameFromInternalName( const TQString & internal )
217 {
218  for ( int i = 0; i < SpecialRuleFieldsCount; ++i ) {
219  if ( internal == SpecialRuleFields[i].internalName )
220  return i18n(SpecialRuleFields[i].displayName);
221  }
222  return internal.latin1();
223 }
224 
225 
226 
227 int KMSearchRuleWidget::indexOfRuleField( const TQCString & aName ) const
228 {
229  if ( aName.isEmpty() )
230  return -1;
231 
232  TQString i18n_aName = displayNameFromInternalName( aName );
233 
234  for ( int i = 1; i < mRuleField->count(); ++i ) {
235  if ( mRuleField->text( i ) == i18n_aName )
236  return i;
237  }
238 
239  return -1;
240 }
241 
242 void KMSearchRuleWidget::initFieldList( bool headersOnly, bool absoluteDates )
243 {
244  mFilterFieldList.clear();
245  mFilterFieldList.append(""); // empty entry for user input
246  if( !headersOnly ) {
247  mFilterFieldList.append( i18n( SpecialRuleFields[Message].displayName ) );
248  mFilterFieldList.append( i18n( SpecialRuleFields[Body].displayName ) );
249  }
250  mFilterFieldList.append( i18n( SpecialRuleFields[AnyHeader].displayName ) );
251  mFilterFieldList.append( i18n( SpecialRuleFields[Recipients].displayName ) );
252  mFilterFieldList.append( i18n( SpecialRuleFields[Size].displayName ) );
253  if ( !absoluteDates )
254  mFilterFieldList.append( i18n( SpecialRuleFields[AgeInDays].displayName ) );
255  mFilterFieldList.append( i18n( SpecialRuleFields[Subject].displayName ) );
256  mFilterFieldList.append( i18n( SpecialRuleFields[From].displayName ) );
257  mFilterFieldList.append( i18n( SpecialRuleFields[To].displayName ) );
258  mFilterFieldList.append( i18n( SpecialRuleFields[CC].displayName ) );
259  mFilterFieldList.append( i18n( SpecialRuleFields[ReplyTo].displayName ) );
260  mFilterFieldList.append( i18n( SpecialRuleFields[Organization].displayName ) );
261 
262  // these others only represent message headers and you can add to
263  // them as you like
264  mFilterFieldList.append("List-Id");
265  mFilterFieldList.append("Resent-From");
266  mFilterFieldList.append("X-Loop");
267  mFilterFieldList.append("X-Mailing-List");
268  mFilterFieldList.append("X-Spam-Flag");
269 }
270 
271 void KMSearchRuleWidget::slotRuleFieldChanged( const TQString & field )
272 {
273  RuleWidgetHandlerManager::instance()->update( ruleFieldToEnglish( field ),
274  mFunctionStack,
275  mValueStack );
276 }
277 
278 //=============================================================================
279 //
280 // class KMFilterActionWidgetLister (the filter action editor)
281 //
282 //=============================================================================
283 
284 KMSearchRuleWidgetLister::KMSearchRuleWidgetLister( TQWidget *parent, const char* name, bool headersOnly, bool absoluteDates )
285  : KWidgetLister( 2, FILTER_MAX_RULES, parent, name )
286 {
287  mRuleList = 0;
288  mHeadersOnly = headersOnly;
289  mAbsoluteDates = absoluteDates;
290 }
291 
292 KMSearchRuleWidgetLister::~KMSearchRuleWidgetLister()
293 {
294 }
295 
296 void KMSearchRuleWidgetLister::setRuleList( TQPtrList<KMSearchRule> *aList )
297 {
298  assert ( aList );
299 
300  if ( mRuleList && mRuleList != aList )
301  regenerateRuleListFromWidgets();
302 
303  mRuleList = aList;
304 
305  if ( mWidgetList.first() ) // move this below next 'if'?
306  mWidgetList.first()->blockSignals(true);
307 
308  if ( aList->count() == 0 ) {
309  slotClear();
310  mWidgetList.first()->blockSignals(false);
311  return;
312  }
313 
314  int superfluousItems = (int)mRuleList->count() - mMaxWidgets ;
315  if ( superfluousItems > 0 ) {
316  kdDebug(5006) << "KMSearchRuleWidgetLister: Clipping rule list to "
317  << mMaxWidgets << " items!" << endl;
318 
319  for ( ; superfluousItems ; superfluousItems-- )
320  mRuleList->removeLast();
321  }
322 
323  // HACK to workaround regression in TQt 3.1.3 and TQt 3.2.0 (fixes bug #63537)
324  setNumberOfShownWidgetsTo( TQMAX((int)mRuleList->count(),mMinWidgets)+1 );
325  // set the right number of widgets
326  setNumberOfShownWidgetsTo( TQMAX((int)mRuleList->count(),mMinWidgets) );
327 
328  // load the actions into the widgets
329  TQPtrListIterator<KMSearchRule> rIt( *mRuleList );
330  TQPtrListIterator<TQWidget> wIt( mWidgetList );
331  for ( rIt.toFirst(), wIt.toFirst() ;
332  rIt.current() && wIt.current() ; ++rIt, ++wIt ) {
333  static_cast<KMSearchRuleWidget*>(*wIt)->setRule( (*rIt) );
334  }
335  for ( ; wIt.current() ; ++wIt )
336  ((KMSearchRuleWidget*)(*wIt))->reset();
337 
338  assert( mWidgetList.first() );
339  mWidgetList.first()->blockSignals(false);
340 }
341 
342 void KMSearchRuleWidgetLister::setHeadersOnly( bool headersOnly )
343 {
344  TQPtrListIterator<TQWidget> wIt( mWidgetList );
345  for ( wIt.toFirst() ; wIt.current() ; ++wIt ) {
346  (static_cast<KMSearchRuleWidget*>(*wIt))->setHeadersOnly( headersOnly );
347  }
348 }
349 
350 void KMSearchRuleWidgetLister::reset()
351 {
352  if ( mRuleList )
353  regenerateRuleListFromWidgets();
354 
355  mRuleList = 0;
356  slotClear();
357 }
358 
359 TQWidget* KMSearchRuleWidgetLister::createWidget( TQWidget *parent )
360 {
361  return new KMSearchRuleWidget(parent, 0, 0, mHeadersOnly, mAbsoluteDates);
362 }
363 
364 void KMSearchRuleWidgetLister::clearWidget( TQWidget *aWidget )
365 {
366  if ( aWidget )
367  ((KMSearchRuleWidget*)aWidget)->reset();
368 }
369 
370 void KMSearchRuleWidgetLister::regenerateRuleListFromWidgets()
371 {
372  if ( !mRuleList ) return;
373 
374  mRuleList->clear();
375 
376  TQPtrListIterator<TQWidget> it( mWidgetList );
377  for ( it.toFirst() ; it.current() ; ++it ) {
378  KMSearchRule *r = ((KMSearchRuleWidget*)(*it))->rule();
379  if ( r )
380  mRuleList->append( r );
381  }
382 }
383 
384 
385 
386 
387 //=============================================================================
388 //
389 // class KMSearchPatternEdit
390 //
391 //=============================================================================
392 
393 KMSearchPatternEdit::KMSearchPatternEdit(TQWidget *parent, const char *name, bool headersOnly, bool absoluteDates )
394  : TQGroupBox( 1/*columns*/, TQt::Horizontal, parent, name )
395 {
396  setTitle( i18n("Search Criteria") );
397  initLayout( headersOnly, absoluteDates );
398 }
399 
400 KMSearchPatternEdit::KMSearchPatternEdit(const TQString & title, TQWidget *parent, const char *name, bool headersOnly, bool absoluteDates)
401  : TQGroupBox( 1/*column*/, TQt::Horizontal, title, parent, name )
402 {
403  initLayout( headersOnly, absoluteDates );
404 }
405 
406 KMSearchPatternEdit::~KMSearchPatternEdit()
407 {
408 }
409 
410 void KMSearchPatternEdit::initLayout(bool headersOnly, bool absoluteDates)
411 {
412  //------------the radio buttons
413  mAllRBtn = new TQRadioButton( i18n("Match a&ll of the following"), this, "mAllRBtn" );
414  mAnyRBtn = new TQRadioButton( i18n("Match an&y of the following"), this, "mAnyRBtn" );
415 
416  mAllRBtn->setChecked(true);
417  mAnyRBtn->setChecked(false);
418 
419  TQButtonGroup *bg = new TQButtonGroup( this );
420  bg->hide();
421  bg->insert( mAllRBtn, (int)KMSearchPattern::OpAnd );
422  bg->insert( mAnyRBtn, (int)KMSearchPattern::OpOr );
423 
424  //------------the list of KMSearchRuleWidget's
425  mRuleLister = new KMSearchRuleWidgetLister( this, "swl", headersOnly, absoluteDates );
426  mRuleLister->slotClear();
427 
428  //------------connect a few signals
429  connect( bg, TQ_SIGNAL(clicked(int)),
430  this, TQ_SLOT(slotRadioClicked(int)) );
431 
432  KMSearchRuleWidget *srw = (KMSearchRuleWidget*)mRuleLister->mWidgetList.first();
433  if ( srw ) {
434  connect( srw, TQ_SIGNAL(fieldChanged(const TQString &)),
435  this, TQ_SLOT(slotAutoNameHack()) );
436  connect( srw, TQ_SIGNAL(contentsChanged(const TQString &)),
437  this, TQ_SLOT(slotAutoNameHack()) );
438  } else
439  kdDebug(5006) << "KMSearchPatternEdit: no first KMSearchRuleWidget, though slotClear() has been called!" << endl;
440 }
441 
443 {
444  assert( aPattern );
445 
446  mRuleLister->setRuleList( aPattern );
447 
448  mPattern = aPattern;
449 
450  blockSignals(true);
451  if ( mPattern->op() == KMSearchPattern::OpOr )
452  mAnyRBtn->setChecked(true);
453  else
454  mAllRBtn->setChecked(true);
455  blockSignals(false);
456 
457  setEnabled( true );
458 }
459 
460 void KMSearchPatternEdit::setHeadersOnly( bool headersOnly )
461 {
462  mRuleLister->setHeadersOnly( headersOnly );
463 }
464 
466 {
467  mRuleLister->reset();
468 
469  blockSignals(true);
470  mAllRBtn->setChecked( true );
471  blockSignals(false);
472 
473  setEnabled( false );
474 }
475 
476 void KMSearchPatternEdit::slotRadioClicked(int aIdx)
477 {
478  if ( mPattern )
479  mPattern->setOp( (KMSearchPattern::Operator)aIdx );
480 }
481 
482 void KMSearchPatternEdit::slotAutoNameHack()
483 {
484  mRuleLister->regenerateRuleListFromWidgets();
485  emit maybeNameChanged();
486 }
487 
488 #include "kmsearchpatternedit.moc"
void maybeNameChanged()
This signal is emitted whenever the name of the processed search pattern may have changed.
KMSearchPatternEdit(TQWidget *parent=0, const char *name=0, bool headersOnly=false, bool absoluteDates=false)
Constructor.
void setHeadersOnly(bool headersOnly)
Set whether only header fields can be searched.
void reset()
Called when the widget should let go of the currently referenced filter and disable itself.
void setSearchPattern(KMSearchPattern *aPattern)
Set the search pattern.
This class is an abstraction of a search over messages.
KMSearchPattern::Operator op() const
Get the filter operator.
void setOp(KMSearchPattern::Operator aOp)
Set the filter operator.
Operator
Boolean operators that connect the return values of the individual rules.
A widget to edit a single KMSearchRule.
void fieldChanged(const TQString &)
This signal is emitted whenever the user alters the field.
KMSearchRuleWidget(TQWidget *parent=0, KMSearchRule *aRule=0, const char *name=0, bool headersOnly=false, bool absoluteDates=false)
Constructor.
void setRule(KMSearchRule *aRule)
Set the rule.
KMSearchRule * rule() const
Return a reference to the currently-worked-on KMSearchRule.
int indexOfRuleField(const TQCString &aName) const
Used internally to find the corresponding index into the field ComboBox.
void setHeadersOnly(bool headersOnly)
Set whether only header fields can be searched.
static TQCString ruleFieldToEnglish(const TQString &i18nVal)
Used internally to translate i18n-ized pseudo-headers back to english.
void contentsChanged(const TQString &)
This signal is emitted whenever the user alters the contents/value of the rule.
void reset()
Resets the rule currently worked on and updates the widget accordingly.
Incoming mail is sent through the list of mail filter rules before it is placed in the associated mai...
static KMSearchRule * createInstance(const TQCString &field=0, Function function=FuncContains, const TQString &contents=TQString())
Create a search rule of a certain type by instantiating the appro- priate subclass depending on the f...
TQCString field() const
Return message header field name (without the trailing ':').
Function
Operators for comparison of field and contents.
Singleton to manage the list of RuleWidgetHandlers.