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 <klocale.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"