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.
29static 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};
47static 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
74void 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
93void 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
180void KMSearchRuleWidget::slotFunctionChanged()
181{
182 const TQCString ruleField = ruleFieldToEnglish( mRuleField->currentText() );
183 RuleWidgetHandlerManager::instance()->update( ruleField,
184 mFunctionStack,
185 mValueStack );
186}
187
188void 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
198TQCString 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
207int 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
216static 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
227int 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
242void 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
271void 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
284KMSearchRuleWidgetLister::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
292KMSearchRuleWidgetLister::~KMSearchRuleWidgetLister()
293{
294}
295
296void 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
342void 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
350void KMSearchRuleWidgetLister::reset()
351{
352 if ( mRuleList )
353 regenerateRuleListFromWidgets();
354
355 mRuleList = 0;
356 slotClear();
357}
358
359TQWidget* KMSearchRuleWidgetLister::createWidget( TQWidget *parent )
360{
361 return new KMSearchRuleWidget(parent, 0, 0, mHeadersOnly, mAbsoluteDates);
362}
363
364void KMSearchRuleWidgetLister::clearWidget( TQWidget *aWidget )
365{
366 if ( aWidget )
367 ((KMSearchRuleWidget*)aWidget)->reset();
368}
369
370void 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
393KMSearchPatternEdit::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
400KMSearchPatternEdit::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
406KMSearchPatternEdit::~KMSearchPatternEdit()
407{
408}
409
410void 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
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
476void KMSearchPatternEdit::slotRadioClicked(int aIdx)
477{
478 if ( mPattern )
479 mPattern->setOp( (KMSearchPattern::Operator)aIdx );
480}
481
482void 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.