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  TQT_TQOBJECT(this) );
120 
121  // redirect focus to the header field combo box
122  setFocusProxy( mRuleField );
123 
124  connect( mRuleField, TQT_SIGNAL( activated( const TQString & ) ),
125  this, TQT_SLOT( slotRuleFieldChanged( const TQString & ) ) );
126  connect( mRuleField, TQT_SIGNAL( textChanged( const TQString & ) ),
127  this, TQT_SLOT( slotRuleFieldChanged( const TQString & ) ) );
128  connect( mRuleField, TQT_SIGNAL( textChanged( const TQString & ) ),
129  this, TQT_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*/, Qt::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*/, Qt::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, TQT_SIGNAL(clicked(int)),
430  this, TQT_SLOT(slotRadioClicked(int)) );
431 
432  KMSearchRuleWidget *srw = (KMSearchRuleWidget*)mRuleLister->mWidgetList.first();
433  if ( srw ) {
434  connect( srw, TQT_SIGNAL(fieldChanged(const TQString &)),
435  this, TQT_SLOT(slotAutoNameHack()) );
436  connect( srw, TQT_SIGNAL(contentsChanged(const TQString &)),
437  this, TQT_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"