libtdepim

kscoring.cpp
1/*
2 kscoring.cpp
3
4 Copyright (c) 2001 Mathias Waack
5 Copyright (C) 2005 by Volker Krause <volker.krause@rwth-aachen.de>
6
7 Author: Mathias Waack <mathias@atoll-net.de>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software Foundation,
15 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
16*/
17#ifdef KDE_USE_FINAL
18#undef TQT_NO_ASCII_CAST
19#endif
20
21#include <iostream>
22
23#include <tqfile.h>
24#include <tqdom.h>
25#include <tqlayout.h>
26#include <tqlabel.h>
27#include <tqcheckbox.h>
28#include <tqtextview.h>
29
30#include <tdelocale.h>
31#include <tdestandarddirs.h>
32#include <kdebug.h>
33#include <kinputdialog.h>
34
35#include "kscoring.h"
36#include "kscoringeditor.h"
37
38
39//----------------------------------------------------------------------------
40// a small function to encode attribute values, code stolen from TQDom
41static TQString toXml(const TQString& str)
42{
43 TQString tmp(str);
44 uint len = tmp.length();
45 uint i = 0;
46 while ( i < len ) {
47 if (tmp[(int)i] == '<') {
48 tmp.replace(i, 1, "&lt;");
49 len += 3;
50 i += 4;
51 } else if (tmp[(int)i] == '"') {
52 tmp.replace(i, 1, "&quot;");
53 len += 5;
54 i += 6;
55 } else if (tmp[(int)i] == '&') {
56 tmp.replace(i, 1, "&amp;");
57 len += 4;
58 i += 5;
59 } else if (tmp[(int)i] == '>') {
60 tmp.replace(i, 1, "&gt;");
61 len += 3;
62 i += 4;
63 } else {
64 ++i;
65 }
66 }
67
68 return tmp;
69}
70
71
72// small dialog to display the messages from NotifyAction
73NotifyDialog* NotifyDialog::me = 0;
74NotifyDialog::NotesMap NotifyDialog::dict;
75
76NotifyDialog::NotifyDialog(TQWidget* p)
77 : KDialogBase(p,"notify action dialog",true,"Notify Message",Close,Close,true)
78{
79 TQFrame *f = makeMainWidget();
80 TQVBoxLayout *topL = new TQVBoxLayout(f);
81 note = new TQLabel(f);
82 note->setTextFormat(RichText);
83 topL->addWidget(note);
84 TQCheckBox *check = new TQCheckBox(i18n("Do not show this message again"),f);
85 check->setChecked(true);
86 topL->addWidget(check);
87 connect(check,TQ_SIGNAL(toggled(bool)),TQ_SLOT(slotShowAgainToggled(bool)));
88}
89
90void NotifyDialog::slotShowAgainToggled(bool flag)
91{
92 dict.replace(msg,!flag);
93 kdDebug(5100) << "note \"" << note << "\" will popup again: " << flag << endl;
94}
95
96void NotifyDialog::display(ScorableArticle& a, const TQString& s)
97{
98 kdDebug(5100) << "displaying message" << endl;
99 if (!me) me = new NotifyDialog();
100 me->msg = s;
101
102 NotesMap::Iterator i = dict.find(s);
103 if (i == dict.end() || i.data()) {
104 TQString msg = i18n("Article\n<b>%1</b><br><b>%2</b><br>caused the following"
105 " note to appear:<br>%3").
106 arg(a.from()).
107 arg(a.subject()).
108 arg(s);
109 me->note->setText(msg);
110 if ( i == dict.end() ) i = dict.replace(s,false);
111 me->adjustSize();
112 me->exec();
113 }
114}
115
116
117//----------------------------------------------------------------------------
118ScorableArticle::~ScorableArticle()
119{
120}
121
122void ScorableArticle::displayMessage(const TQString& note)
123{
124 NotifyDialog::display(*this,note);
125}
126
127//----------------------------------------------------------------------------
128ScorableGroup::~ScorableGroup()
129{
130}
131
132// the base class for all actions
133ActionBase::ActionBase()
134{
135 kdDebug(5100) << "new Action " << this << endl;
136}
137
138ActionBase::~ActionBase()
139{
140 kdDebug(5100) << "delete Action " << this << endl;
141}
142
143
144TQStringList ActionBase::userNames()
145{
146 TQStringList l;
147 l << userName(SETSCORE);
148 l << userName(NOTIFY);
149 l << userName(COLOR);
150 l << userName(MARKASREAD);
151 return l;
152}
153
154ActionBase* ActionBase::factory(int type, const TQString &value)
155{
156 switch (type) {
157 case SETSCORE: return new ActionSetScore(value);
158 case NOTIFY: return new ActionNotify(value);
159 case COLOR: return new ActionColor(value);
160 case MARKASREAD: return new ActionMarkAsRead();
161 default:
162 kdWarning(5100) << "unknown type " << type << " in ActionBase::factory()" << endl;
163 return 0;
164 }
165}
166
167TQString ActionBase::userName(int type)
168{
169 switch (type) {
170 case SETSCORE: return i18n("Adjust Score");
171 case NOTIFY: return i18n("Display Message");
172 case COLOR: return i18n("Colorize Header");
173 case MARKASREAD: return i18n("Mark As Read");
174 default:
175 kdWarning(5100) << "unknown type " << type << " in ActionBase::userName()" << endl;
176 return 0;
177 }
178}
179
180int ActionBase::getTypeForName(const TQString& name)
181{
182 if (name == "SETSCORE") return SETSCORE;
183 else if (name == "NOTIFY") return NOTIFY;
184 else if (name == "COLOR") return COLOR;
185 else if (name == "MARKASREAD") return MARKASREAD;
186 else {
187 kdWarning(5100) << "unknown type string " << name
188 << " in ActionBase::getTypeForName()" << endl;
189 return -1;
190 }
191}
192
193int ActionBase::getTypeForUserName(const TQString& name)
194{
195 if (name == userName(SETSCORE)) return SETSCORE;
196 else if (name == userName(NOTIFY)) return NOTIFY;
197 else if (name == userName(COLOR)) return COLOR;
198 else if ( name == userName(MARKASREAD) ) return MARKASREAD;
199 else {
200 kdWarning(5100) << "unknown type string " << name
201 << " in ActionBase::getTypeForUserName()" << endl;
202 return -1;
203 }
204}
205
206// the set score action
207ActionSetScore::ActionSetScore(short v)
208 : val(v)
209{
210}
211
212ActionSetScore::ActionSetScore(const TQString& s)
213{
214 val = s.toShort();
215}
216
217ActionSetScore::ActionSetScore(const ActionSetScore& as)
218 : ActionBase(),
219 val(as.val)
220{
221}
222
223ActionSetScore::~ActionSetScore()
224{
225}
226
227TQString ActionSetScore::toString() const
228{
229 TQString a;
230 a += "<Action type=\"SETSCORE\" value=\"" + TQString::number(val) + "\" />";
231 return a;
232}
233
234void ActionSetScore::apply(ScorableArticle& a) const
235{
236 a.addScore(val);
237}
238
239ActionSetScore* ActionSetScore::clone() const
240{
241 return new ActionSetScore(*this);
242}
243
244// the color action
245ActionColor::ActionColor(const TQColor& c)
246 : ActionBase(), color(c)
247{
248}
249
250ActionColor::ActionColor(const TQString& s)
251 : ActionBase()
252{
253 setValue(s);
254}
255
256ActionColor::ActionColor(const ActionColor& a)
257 : ActionBase(), color(a.color)
258{
259}
260
261ActionColor::~ActionColor()
262{}
263
264TQString ActionColor::toString() const
265{
266 TQString a;
267 a += "<Action type=\"COLOR\" value=\"" + toXml(color.name()) + "\" />";
268 return a;
269}
270
271void ActionColor::apply(ScorableArticle& a) const
272{
273 a.changeColor(color);
274}
275
276ActionColor* ActionColor::clone() const
277{
278 return new ActionColor(*this);
279}
280
281
282// the notify action
283ActionNotify::ActionNotify(const TQString& s)
284{
285 note = s;
286}
287
288ActionNotify::ActionNotify(const ActionNotify& an)
289 : ActionBase()
290{
291 note = an.note;
292}
293
294TQString ActionNotify::toString() const
295{
296 return "<Action type=\"NOTIFY\" value=\"" + toXml(note) + "\" />";
297}
298
299void ActionNotify::apply(ScorableArticle& a) const
300{
301 a.displayMessage(note);
302}
303
304ActionNotify* ActionNotify::clone() const
305{
306 return new ActionNotify(*this);
307}
308
309
310// mark as read action
311ActionMarkAsRead::ActionMarkAsRead() :
312 ActionBase()
313{
314}
315
316ActionMarkAsRead::ActionMarkAsRead( const ActionMarkAsRead &action ) :
317 ActionBase()
318{
319 Q_UNUSED( action );
320}
321
322TQString ActionMarkAsRead::toString() const
323{
324 return "<Action type=\"MARKASREAD\"/>";
325}
326
327void ActionMarkAsRead::apply( ScorableArticle &article ) const
328{
329 article.markAsRead();
330}
331
332ActionMarkAsRead* ActionMarkAsRead::clone() const
333{
334 return new ActionMarkAsRead(*this);
335}
336
337//----------------------------------------------------------------------------
338NotifyCollection::NotifyCollection()
339{
340 notifyList.setAutoDelete(true);
341}
342
343NotifyCollection::~NotifyCollection()
344{
345}
346
347void NotifyCollection::addNote(const ScorableArticle& a, const TQString& note)
348{
349 article_list *l = notifyList.find(note);
350 if (!l) {
351 notifyList.insert(note,new article_list);
352 l = notifyList.find(note);
353 }
354 article_info i;
355 i.from = a.from();
356 i.subject = a.subject();
357 l->append(i);
358}
359
360TQString NotifyCollection::collection() const
361{
362 TQString notifyCollection = i18n("<h1>List of collected notes</h1>");
363 notifyCollection += "<p><ul>";
364 // first look thru the notes and create one string
365 TQDictIterator<article_list> it(notifyList);
366 for(;it.current();++it) {
367 const TQString& note = it.currentKey();
368 notifyCollection += "<li>" + note + "<ul>";
369 article_list* alist = it.current();
370 article_list::Iterator ait;
371 for(ait = alist->begin(); ait != alist->end(); ++ait) {
372 notifyCollection += "<li><b>From: </b>" + (*ait).from + "<br>";
373 notifyCollection += "<b>Subject: </b>" + (*ait).subject;
374 }
375 notifyCollection += "</ul>";
376 }
377 notifyCollection += "</ul>";
378
379 return notifyCollection;
380}
381
382void NotifyCollection::displayCollection(TQWidget *p) const
383{
384 //KMessageBox::information(p,collection(),i18n("Collected Notes"));
385 KDialogBase *dlg = new KDialogBase( p, 0, false, i18n("Collected Notes"),
386 KDialogBase::Close, KDialogBase::Close );
387 TQTextView *text = new TQTextView(dlg);
388 text->setText(collection());
389 dlg->setMainWidget(text);
390 dlg->setMinimumWidth(300);
391 dlg->setMinimumHeight(300);
392 dlg->show();
393}
394
395//----------------------------------------------------------------------------
396KScoringExpression::KScoringExpression(const TQString& h, const TQString& t, const TQString& n, const TQString& ng)
397 : header(h), expr_str(n)
398{
399 if (t == "MATCH" ) {
400 cond = MATCH;
401 expr.setPattern(expr_str);
402 expr.setCaseSensitive(false);
403 }
404 else if ( t == "MATCHCS" ) {
405 cond = MATCHCS;
406 expr.setPattern( expr_str );
407 expr.setCaseSensitive( true );
408 }
409 else if (t == "CONTAINS" ) cond = CONTAINS;
410 else if (t == "EQUALS" ) cond = EQUALS;
411 else if (t == "GREATER") {
412 cond = GREATER;
413 expr_int = expr_str.toInt();
414 }
415 else if (t == "SMALLER") {
416 cond = SMALLER;
417 expr_int = expr_str.toInt();
418 }
419 else {
420 kdDebug(5100) << "unknown match type in new expression" << endl;
421 }
422
423 neg = ng.toInt();
424 c_header = header.latin1();
425
426 kdDebug(5100) << "new expr: " << c_header << " " << t << " "
427 << expr_str << " " << neg << endl;
428}
429
430// static
431int KScoringExpression::getConditionForName(const TQString& s)
432{
433 if (s == getNameForCondition(CONTAINS)) return CONTAINS;
434 else if (s == getNameForCondition(MATCH)) return MATCH;
435 else if (s == getNameForCondition(MATCHCS)) return MATCHCS;
436 else if (s == getNameForCondition(EQUALS)) return EQUALS;
437 else if (s == getNameForCondition(SMALLER)) return SMALLER;
438 else if (s == getNameForCondition(GREATER)) return GREATER;
439 else {
440 kdWarning(5100) << "unknown condition name " << s
441 << " in KScoringExpression::getConditionForName()" << endl;
442 return -1;
443 }
444}
445
446// static
447TQString KScoringExpression::getNameForCondition(int cond)
448{
449 switch (cond) {
450 case CONTAINS: return i18n("Contains Substring");
451 case MATCH: return i18n("Matches Regular Expression");
452 case MATCHCS: return i18n("Matches Regular Expression (Case Sensitive)");
453 case EQUALS: return i18n("Is Exactly the Same As");
454 case SMALLER: return i18n("Less Than");
455 case GREATER: return i18n("Greater Than");
456 default:
457 kdWarning(5100) << "unknown condition " << cond
458 << " in KScoringExpression::getNameForCondition()" << endl;
459 return "";
460 }
461}
462
463// static
464TQStringList KScoringExpression::conditionNames()
465{
466 TQStringList l;
467 l << getNameForCondition(CONTAINS);
468 l << getNameForCondition(MATCH);
469 l << getNameForCondition(MATCHCS);
470 l << getNameForCondition(EQUALS);
471 l << getNameForCondition(SMALLER);
472 l << getNameForCondition(GREATER);
473 return l;
474}
475
476// static
477TQStringList KScoringExpression::headerNames()
478{
479 TQStringList l;
480 l.append("From");
481 l.append("Message-ID");
482 l.append("Subject");
483 l.append("Date");
484 l.append("References");
485 l.append("NNTP-Posting-Host");
486 l.append("Bytes");
487 l.append("Lines");
488 l.append("Xref");
489 return l;
490}
491
492KScoringExpression::~KScoringExpression()
493{
494}
495
496bool KScoringExpression::match(ScorableArticle& a) const
497{
498 //kdDebug(5100) << "matching against header " << c_header << endl;
499 bool res = true;
500 TQString head;
501
502 if (header == "From")
503 head = a.from();
504 else if (header == "Subject")
505 head = a.subject();
506 else
507 head = a.getHeaderByType(c_header);
508
509 if (!head.isEmpty()) {
510 switch (cond) {
511 case EQUALS:
512 res = (head.lower() == expr_str.lower());
513 break;
514 case CONTAINS:
515 res = (head.lower().find(expr_str.lower()) >= 0);
516 break;
517 case MATCH:
518 case MATCHCS:
519 res = (expr.search(head)!=-1);
520 break;
521 case GREATER:
522 res = (head.toInt() > expr_int);
523 break;
524 case SMALLER:
525 res = (head.toInt() < expr_int);
526 break;
527 default:
528 kdDebug(5100) << "unknown match" << endl;
529 res = false;
530 }
531 }
532 else res = false;
533// kdDebug(5100) << "matching returns " << res << endl;
534 return (neg)?!res:res;
535}
536
537void KScoringExpression::write(TQTextStream& st) const
538{
539 st << toString();
540}
541
542TQString KScoringExpression::toString() const
543{
544// kdDebug(5100) << "KScoringExpression::toString() starts" << endl;
545// kdDebug(5100) << "header is " << header << endl;
546// kdDebug(5100) << "expr is " << expr_str << endl;
547// kdDebug(5100) << "neg is " << neg << endl;
548// kdDebug(5100) << "type is " << getType() << endl;
549 TQString e;
550 e += "<Expression neg=\"" + TQString::number(neg?1:0)
551 + "\" header=\"" + header
552 + "\" type=\"" + getTypeString()
553 + "\" expr=\"" + toXml(expr_str)
554 + "\" />";
555// kdDebug(5100) << "KScoringExpression::toString() finished" << endl;
556 return e;
557}
558
559TQString KScoringExpression::getTypeString() const
560{
561 return KScoringExpression::getTypeString(cond);
562}
563
564TQString KScoringExpression::getTypeString(int cond)
565{
566 switch (cond) {
567 case CONTAINS: return "CONTAINS";
568 case MATCH: return "MATCH";
569 case MATCHCS: return "MATCHCS";
570 case EQUALS: return "EQUALS";
571 case SMALLER: return "SMALLER";
572 case GREATER: return "GREATER";
573 default:
574 kdWarning(5100) << "unknown cond " << cond << " in KScoringExpression::getTypeString()" << endl;
575 return "";
576 }
577}
578
579int KScoringExpression::getType() const
580{
581 return cond;
582}
583
584//----------------------------------------------------------------------------
585KScoringRule::KScoringRule(const TQString& n )
586 : name(n), link(AND)
587{
588 expressions.setAutoDelete(true);
589 actions.setAutoDelete(true);
590}
591
592KScoringRule::KScoringRule(const KScoringRule& r)
593{
594 kdDebug(5100) << "copying rule " << r.getName() << endl;
595 name = r.getName();
596 expressions.setAutoDelete(true);
597 actions.setAutoDelete(true);
598 // copy expressions
599 expressions.clear();
600 const ScoreExprList& rexpr = r.expressions;
601 TQPtrListIterator<KScoringExpression> it(rexpr);
602 for ( ; it.current(); ++it ) {
603 KScoringExpression *t = new KScoringExpression(**it);
604 expressions.append(t);
605 }
606 // copy actions
607 actions.clear();
608 const ActionList& ract = r.actions;
609 TQPtrListIterator<ActionBase> ait(ract);
610 for ( ; ait.current(); ++ait ) {
611 ActionBase *t = *ait;
612 actions.append(t->clone());
613 }
614 // copy groups, servers, linkmode and expires
615 groups = r.groups;
616 expires = r.expires;
617 link = r.link;
618}
619
620KScoringRule::~KScoringRule()
621{
622 cleanExpressions();
623 cleanActions();
624}
625
626void KScoringRule::cleanExpressions()
627{
628 // the expressions is setAutoDelete(true)
629 expressions.clear();
630}
631
632void KScoringRule::cleanActions()
633{
634 // the actions is setAutoDelete(true)
635 actions.clear();
636}
637
638void KScoringRule::addExpression( KScoringExpression* expr)
639{
640 kdDebug(5100) << "KScoringRule::addExpression" << endl;
641 expressions.append(expr);
642}
643
644void KScoringRule::addAction(int type, const TQString& val)
645{
646 ActionBase *action = ActionBase::factory(type,val);
647 addAction(action);
648}
649
650void KScoringRule::addAction(ActionBase* a)
651{
652 kdDebug(5100) << "KScoringRule::addAction() " << a->toString() << endl;
653 actions.append(a);
654}
655
656void KScoringRule::setLinkMode(const TQString& l)
657{
658 if (l == "OR") link = OR;
659 else link = AND;
660}
661
662void KScoringRule::setExpire(const TQString& e)
663{
664 if (e != "never") {
665 TQStringList l = TQStringList::split("-",e);
666 Q_ASSERT( l.count() == 3 );
667 expires.setYMD( (*(l.at(0))).toInt(),
668 (*(l.at(1))).toInt(),
669 (*(l.at(2))).toInt());
670 }
671 kdDebug(5100) << "Rule " << getName() << " expires at " << getExpireDateString() << endl;
672}
673
674bool KScoringRule::matchGroup(const TQString& group) const
675{
676 for(GroupList::ConstIterator i=groups.begin(); i!=groups.end();++i) {
677 TQRegExp e(*i);
678 if (e.search(group, 0) != -1 &&
679 (uint)e.matchedLength() == group.length())
680 return true;
681 }
682 return false;
683}
684
685void KScoringRule::applyAction(ScorableArticle& a) const
686{
687 TQPtrListIterator<ActionBase> it(actions);
688 for(; it.current(); ++it) {
689 it.current()->apply(a);
690 }
691}
692
693void KScoringRule::applyRule(ScorableArticle& a) const
694{
695 // kdDebug(5100) << "checking rule " << name << endl;
696 // kdDebug(5100) << " for article from "
697 // << a->from()->asUnicodeString()
698 // << endl;
699 bool oper_and = (link == AND);
700 bool res = true;
701 TQPtrListIterator<KScoringExpression> it(expressions);
702 //kdDebug(5100) << "checking " << expressions.count() << " expressions" << endl;
703 for (; it.current(); ++it) {
704 Q_ASSERT( it.current() );
705 res = it.current()->match(a);
706 if (!res && oper_and) return;
707 else if (res && !oper_and) break;
708 }
709 if (res) applyAction(a);
710}
711
712void KScoringRule::applyRule(ScorableArticle& a /*, const TQString& s*/, const TQString& g) const
713{
714 // check if one of the groups match
715 for (TQStringList::ConstIterator i = groups.begin(); i != groups.end(); ++i) {
716 if (TQRegExp(*i).search(g) != -1) {
717 applyRule(a);
718 return;
719 }
720 }
721}
722
723void KScoringRule::write(TQTextStream& s) const
724{
725 s << toString();
726}
727
728TQString KScoringRule::toString() const
729{
730 //kdDebug(5100) << "KScoringRule::toString() starts" << endl;
731 TQString r;
732 r += "<Rule name=\"" + toXml(name) + "\" linkmode=\"" + getLinkModeName();
733 r += "\" expires=\"" + getExpireDateString() + "\">";
734 //kdDebug(5100) << "building grouplist..." << endl;
735 for(GroupList::ConstIterator i=groups.begin();i!=groups.end();++i) {
736 r += "<Group name=\"" + toXml(*i) + "\" />";
737 }
738 //kdDebug(5100) << "building expressionlist..." << endl;
739 TQPtrListIterator<KScoringExpression> eit(expressions);
740 for (; eit.current(); ++eit) {
741 r += eit.current()->toString();
742 }
743 //kdDebug(5100) << "building actionlist..." << endl;
744 TQPtrListIterator<ActionBase> ait(actions);
745 for (; ait.current(); ++ait) {
746 r += ait.current()->toString();
747 }
748 r += "</Rule>";
749 //kdDebug(5100) << "KScoringRule::toString() finished" << endl;
750 return r;
751}
752
753TQString KScoringRule::getLinkModeName() const
754{
755 switch (link) {
756 case AND: return "AND";
757 case OR: return "OR";
758 default: return "AND";
759 }
760}
761
762TQString KScoringRule::getExpireDateString() const
763{
764 if (expires.isNull()) return "never";
765 else {
766 return TQString::number(expires.year()) + TQString("-")
767 + TQString::number(expires.month()) + TQString("-")
768 + TQString::number(expires.day());
769 }
770}
771
772bool KScoringRule::isExpired() const
773{
774 return (expires.isValid() && (expires < TQDate::currentDate()));
775}
776
777
778
779//----------------------------------------------------------------------------
780KScoringManager::KScoringManager(const TQString& appName)
781 : cacheValid(false)//, _s(0)
782{
783 allRules.setAutoDelete(true);
784 // determine filename of the scorefile
785 if(appName.isEmpty())
786 mFilename = TDEGlobal::dirs()->saveLocation("appdata") + "/scorefile";
787 else
788 mFilename = TDEGlobal::dirs()->saveLocation("data") + "/" + appName + "/scorefile";
789 // open the score file
790 load();
791}
792
793
794KScoringManager::~KScoringManager()
795{
796}
797
798void KScoringManager::load()
799{
800 TQDomDocument sdoc("Scorefile");
801 TQFile f( mFilename );
802 if ( !f.open( IO_ReadOnly ) )
803 return;
804 if ( !sdoc.setContent( &f ) ) {
805 f.close();
806 kdDebug(5100) << "loading the scorefile failed" << endl;
807 return;
808 }
809 f.close();
810 kdDebug(5100) << "loaded the scorefile, creating internal representation" << endl;
811 allRules.clear();
812 createInternalFromXML(sdoc);
813 expireRules();
814 kdDebug(5100) << "ready, got " << allRules.count() << " rules" << endl;
815}
816
817void KScoringManager::save()
818{
819 kdDebug(5100) << "KScoringManager::save() starts" << endl;
820 TQFile f( mFilename );
821 if ( !f.open( IO_WriteOnly ) )
822 return;
823 TQTextStream stream(&f);
824 stream.setEncoding(TQTextStream::Unicode);
825 kdDebug(5100) << "KScoringManager::save() creating xml" << endl;
826 createXMLfromInternal().save(stream,2);
827 kdDebug(5100) << "KScoringManager::save() finished" << endl;
828}
829
830TQDomDocument KScoringManager::createXMLfromInternal()
831{
832 // I was'nt able to create a TQDomDocument in memory:(
833 // so I write the content into a string, which is really stupid
834 TQDomDocument sdoc("Scorefile");
835 TQString ss; // scorestring
836 ss += "<?xml version = '1.0'?><!DOCTYPE Scorefile >";
837 ss += toString();
838 ss += "</Scorefile>\n";
839 kdDebug(5100) << "KScoringManager::createXMLfromInternal():" << endl << ss << endl;
840 sdoc.setContent(ss);
841 return sdoc;
842}
843
844TQString KScoringManager::toString() const
845{
846 TQString s;
847 s += "<Scorefile>\n";
848 TQPtrListIterator<KScoringRule> it(allRules);
849 for( ; it.current(); ++it) {
850 s += it.current()->toString();
851 }
852 return s;
853}
854
855void KScoringManager::expireRules()
856{
857 for ( KScoringRule *cR = allRules.first(); cR; cR=allRules.next()) {
858 if (cR->isExpired()) {
859 kdDebug(5100) << "Rule " << cR->getName() << " is expired, deleting it" << endl;
860 allRules.remove();
861 }
862 }
863}
864
865void KScoringManager::createInternalFromXML(TQDomNode n)
866{
867 static KScoringRule *cR = 0; // the currentRule
868 // the XML file was parsed and now we simply traverse the resulting tree
869 if ( !n.isNull() ) {
870 kdDebug(5100) << "inspecting node of type " << n.nodeType()
871 << " named " << n.toElement().tagName() << endl;
872
873 switch (n.nodeType()) {
874 case TQDomNode::DocumentNode: {
875 // the document itself
876 break;
877 }
878 case TQDomNode::ElementNode: {
879 // Server, Newsgroup, Rule, Expression, Action
880 TQDomElement e = n.toElement();
881 //kdDebug(5100) << "The name of the element is "
882 //<< e.tagName().latin1() << endl;
883 TQString s = e.tagName();
884 if (s == "Rule") {
885 cR = new KScoringRule(e.attribute("name"));
886 cR->setLinkMode(e.attribute("linkmode"));
887 cR->setExpire(e.attribute("expires"));
888 addRuleInternal(cR);
889 }
890 else if (s == "Group") {
891 TQ_CHECK_PTR(cR);
892 cR->addGroup( e.attribute("name") );
893 }
894 else if (s == "Expression") {
895 cR->addExpression(new KScoringExpression(e.attribute("header"),
896 e.attribute("type"),
897 e.attribute("expr"),
898 e.attribute("neg")));
899 }
900 else if (s == "Action") {
901 TQ_CHECK_PTR(cR);
902 cR->addAction(ActionBase::getTypeForName(e.attribute("type")),
903 e.attribute("value"));
904 }
905 break;
906 }
907 default: // kdDebug(5100) << "unknown DomNode::type" << endl;
908 ;
909 }
910 TQDomNodeList nodelist = n.childNodes();
911 unsigned cnt = nodelist.count();
912 //kdDebug(5100) << "recursive checking " << cnt << " nodes" << endl;
913 for (unsigned i=0;i<cnt;++i)
914 createInternalFromXML(nodelist.item(i));
915 }
916}
917
918KScoringRule* KScoringManager::addRule(const ScorableArticle& a, TQString group, short score)
919{
920 KScoringRule *rule = new KScoringRule(findUniqueName());
921 rule->addGroup( group );
922 rule->addExpression(
923 new KScoringExpression("From","CONTAINS",
924 a.from(),"0"));
925 if (score) rule->addAction(new ActionSetScore(score));
926 rule->setExpireDate(TQDate::currentDate().addDays(30));
927 addRule(rule);
928 KScoringEditor *edit = KScoringEditor::createEditor(this);
929 edit->setRule(rule);
930 edit->show();
931 setCacheValid(false);
932 return rule;
933}
934
935KScoringRule* KScoringManager::addRule(KScoringRule* expr)
936{
937 int i = allRules.findRef(expr);
938 if (i == -1) {
939 // only add a rule we don't know
940 addRuleInternal(expr);
941 }
942 else {
943 emit changedRules();
944 }
945 return expr;
946}
947
948KScoringRule* KScoringManager::addRule()
949{
950 KScoringRule *rule = new KScoringRule(findUniqueName());
951 addRule(rule);
952 return rule;
953}
954
955void KScoringManager::addRuleInternal(KScoringRule *e)
956{
957 allRules.append(e);
958 setCacheValid(false);
959 emit changedRules();
960 kdDebug(5100) << "KScoringManager::addRuleInternal " << e->getName() << endl;
961}
962
963void KScoringManager::cancelNewRule(KScoringRule *r)
964{
965 // if e was'nt previously added to the list of rules, we delete it
966 int i = allRules.findRef(r);
967 if (i == -1) {
968 kdDebug(5100) << "deleting rule " << r->getName() << endl;
969 deleteRule(r);
970 }
971 else {
972 kdDebug(5100) << "rule " << r->getName() << " not deleted" << endl;
973 }
974}
975
976void KScoringManager::setRuleName(KScoringRule *r, const TQString& s)
977{
978 bool cont = true;
979 TQString text = s;
980 TQString oldName = r->getName();
981 while (cont) {
982 cont = false;
983 TQPtrListIterator<KScoringRule> it(allRules);
984 for (; it.current(); ++it) {
985 if ( it.current() != r && it.current()->getName() == text ) {
986 kdDebug(5100) << "rule name " << text << " is not unique" << endl;
987 text = KInputDialog::getText(i18n("Choose Another Rule Name"),
988 i18n("The rule name is already assigned, please choose another name:"),
989 text);
990 cont = true;
991 break;
992 }
993 }
994 }
995 if (text != oldName) {
996 r->setName(text);
997 emit changedRuleName(oldName,text);
998 }
999}
1000
1001void KScoringManager::deleteRule(KScoringRule *r)
1002{
1003 int i = allRules.findRef(r);
1004 if (i != -1) {
1005 allRules.remove();
1006 emit changedRules();
1007 }
1008}
1009
1010void KScoringManager::editRule(KScoringRule *e, TQWidget *w)
1011{
1012 KScoringEditor *edit = KScoringEditor::createEditor(this, w);
1013 edit->setRule(e);
1014 edit->show();
1015 delete edit;
1016}
1017
1018void KScoringManager::moveRuleAbove( KScoringRule *above, KScoringRule *below )
1019{
1020 int aindex = allRules.findRef( above );
1021 int bindex = allRules.findRef( below );
1022 if ( aindex <= 0 || bindex < 0 )
1023 return;
1024 if ( aindex < bindex )
1025 --bindex;
1026 allRules.take( aindex );
1027 allRules.insert( bindex, above );
1028}
1029
1030void KScoringManager::moveRuleBelow( KScoringRule *below, KScoringRule *above )
1031{
1032 int bindex = allRules.findRef( below );
1033 int aindex = allRules.findRef( above );
1034 if ( bindex < 0 || bindex >= (int)allRules.count() - 1 || aindex < 0 )
1035 return;
1036 if ( bindex < aindex )
1037 --aindex;
1038 allRules.take( bindex );
1039 allRules.insert( aindex + 1, below );
1040}
1041
1042void KScoringManager::editorReady()
1043{
1044 kdDebug(5100) << "emitting signal finishedEditing" << endl;
1045 save();
1046 emit finishedEditing();
1047}
1048
1049KScoringRule* KScoringManager::copyRule(KScoringRule *r)
1050{
1051 KScoringRule *rule = new KScoringRule(*r);
1052 rule->setName(findUniqueName());
1053 addRuleInternal(rule);
1054 return rule;
1055}
1056
1057void KScoringManager::applyRules(ScorableGroup* )
1058{
1059 kdWarning(5100) << "KScoringManager::applyRules(ScorableGroup* ) isn't implemented" << endl;
1060}
1061
1062void KScoringManager::applyRules(ScorableArticle& article, const TQString& group)
1063{
1064 setGroup(group);
1065 applyRules(article);
1066}
1067
1068void KScoringManager::applyRules(ScorableArticle& a)
1069{
1070 TQPtrListIterator<KScoringRule> it(isCacheValid()? ruleList : allRules);
1071 for( ; it.current(); ++it) {
1072 it.current()->applyRule(a);
1073 }
1074}
1075
1076void KScoringManager::initCache(const TQString& g)
1077{
1078 group = g;
1079 ruleList.clear();
1080 TQPtrListIterator<KScoringRule> it(allRules);
1081 for (; it.current(); ++it) {
1082 if ( it.current()->matchGroup(group) ) {
1083 ruleList.append(it.current());
1084 }
1085 }
1086 kdDebug(5100) << "created cache for group " << group
1087 << " with " << ruleList.count() << " rules" << endl;
1088 setCacheValid(true);
1089}
1090
1091void KScoringManager::setGroup(const TQString& g)
1092{
1093 if (group != g) initCache(g);
1094}
1095
1096bool KScoringManager::hasRulesForCurrentGroup()
1097{
1098 return ruleList.count() != 0;
1099}
1100
1101
1102TQStringList KScoringManager::getRuleNames()
1103{
1104 TQStringList l;
1105 TQPtrListIterator<KScoringRule> it(allRules);
1106 for( ; it.current(); ++it) {
1107 l << it.current()->getName();
1108 }
1109 return l;
1110}
1111
1112KScoringRule* KScoringManager::findRule(const TQString& ruleName)
1113{
1114 TQPtrListIterator<KScoringRule> it(allRules);
1115 for (; it.current(); ++it) {
1116 if ( it.current()->getName() == ruleName ) {
1117 return it;
1118 }
1119 }
1120 return 0;
1121}
1122
1123bool KScoringManager::setCacheValid(bool v)
1124{
1125 bool res = cacheValid;
1126 cacheValid = v;
1127 return res;
1128}
1129
1130TQString KScoringManager::findUniqueName() const
1131{
1132 int nr = 0;
1133 TQString ret;
1134 bool duplicated=false;
1135
1136 while (nr < 99999999) {
1137 nr++;
1138 ret = i18n("rule %1").arg(nr);
1139
1140 duplicated=false;
1141 TQPtrListIterator<KScoringRule> it(allRules);
1142 for( ; it.current(); ++it) {
1143 if (it.current()->getName() == ret) {
1144 duplicated = true;
1145 break;
1146 }
1147 }
1148
1149 if (!duplicated)
1150 return ret;
1151 }
1152
1153 return ret;
1154}
1155
1156bool KScoringManager::hasFeature(int p)
1157{
1158 switch (p) {
1159 case ActionBase::SETSCORE: return canScores();
1160 case ActionBase::NOTIFY: return canNotes();
1161 case ActionBase::COLOR: return canColors();
1162 case ActionBase::MARKASREAD: return canMarkAsRead();
1163 default: return false;
1164 }
1165}
1166
1167TQStringList KScoringManager::getDefaultHeaders() const
1168{
1169 TQStringList l;
1170 l.append("Subject");
1171 l.append("From");
1172 l.append("Date");
1173 l.append("Message-ID");
1174 return l;
1175}
1176
1177void KScoringManager::pushRuleList()
1178{
1179 stack.push(allRules);
1180}
1181
1182void KScoringManager::popRuleList()
1183{
1184 stack.pop(allRules);
1185}
1186
1187void KScoringManager::removeTOS()
1188{
1189 stack.drop();
1190}
1191
1192RuleStack::RuleStack()
1193{
1194}
1195
1196RuleStack::~RuleStack()
1197{}
1198
1199void RuleStack::push(TQPtrList<KScoringRule>& l)
1200{
1201 kdDebug(5100) << "RuleStack::push pushing list with " << l.count() << " rules" << endl;
1202 KScoringManager::ScoringRuleList *l1 = new KScoringManager::ScoringRuleList;
1203 for ( KScoringRule *r=l.first(); r != 0; r=l.next() ) {
1204 l1->append(new KScoringRule(*r));
1205 }
1206 stack.push(l1);
1207 kdDebug(5100) << "now there are " << stack.count() << " lists on the stack" << endl;
1208}
1209
1210void RuleStack::pop(TQPtrList<KScoringRule>& l)
1211{
1212 top(l);
1213 drop();
1214 kdDebug(5100) << "RuleStack::pop pops list with " << l.count() << " rules" << endl;
1215 kdDebug(5100) << "now there are " << stack.count() << " lists on the stack" << endl;
1216}
1217
1218void RuleStack::top(TQPtrList<KScoringRule>& l)
1219{
1220 l.clear();
1221 KScoringManager::ScoringRuleList *l1 = stack.top();
1222 l = *l1;
1223}
1224
1226{
1227 kdDebug(5100) << "drop: now there are " << stack.count() << " lists on the stack" << endl;
1228 stack.remove();
1229}
1230
1231
1232#include "kscoring.moc"
Base class for other Action classes.
Definition: kscoring.h:84
void drop()
drops the TOS
Definition: kscoring.cpp:1225
void pop(TQPtrList< KScoringRule > &)
clears the argument list and copy the content of the TOS into it after that the TOS gets dropped
Definition: kscoring.cpp:1210
void push(TQPtrList< KScoringRule > &)
puts the list on the stack, doesn't change the list
Definition: kscoring.cpp:1199
void top(TQPtrList< KScoringRule > &)
like pop but without dropping the TOS
Definition: kscoring.cpp:1218
The following classes ScorableArticle, ScorableGroup define the interface for the scoring.
Definition: kscoring.h:59