kmail

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