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 <kstandarddirs.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
41 static 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
73 NotifyDialog* NotifyDialog::me = 0;
74 NotifyDialog::NotesMap NotifyDialog::dict;
75 
76 NotifyDialog::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 
90 void NotifyDialog::slotShowAgainToggled(bool flag)
91 {
92  dict.replace(msg,!flag);
93  kdDebug(5100) << "note \"" << note << "\" will popup again: " << flag << endl;
94 }
95 
96 void 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 //----------------------------------------------------------------------------
118 ScorableArticle::~ScorableArticle()
119 {
120 }
121 
122 void ScorableArticle::displayMessage(const TQString& note)
123 {
124  NotifyDialog::display(*this,note);
125 }
126 
127 //----------------------------------------------------------------------------
128 ScorableGroup::~ScorableGroup()
129 {
130 }
131 
132 // the base class for all actions
133 ActionBase::ActionBase()
134 {
135  kdDebug(5100) << "new Action " << this << endl;
136 }
137 
138 ActionBase::~ActionBase()
139 {
140  kdDebug(5100) << "delete Action " << this << endl;
141 }
142 
143 
144 TQStringList 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 
154 ActionBase* 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 
167 TQString 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 
180 int 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 
193 int 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
207 ActionSetScore::ActionSetScore(short v)
208  : val(v)
209 {
210 }
211 
212 ActionSetScore::ActionSetScore(const TQString& s)
213 {
214  val = s.toShort();
215 }
216 
217 ActionSetScore::ActionSetScore(const ActionSetScore& as)
218  : ActionBase(),
219  val(as.val)
220 {
221 }
222 
223 ActionSetScore::~ActionSetScore()
224 {
225 }
226 
227 TQString ActionSetScore::toString() const
228 {
229  TQString a;
230  a += "<Action type=\"SETSCORE\" value=\"" + TQString::number(val) + "\" />";
231  return a;
232 }
233 
234 void ActionSetScore::apply(ScorableArticle& a) const
235 {
236  a.addScore(val);
237 }
238 
239 ActionSetScore* ActionSetScore::clone() const
240 {
241  return new ActionSetScore(*this);
242 }
243 
244 // the color action
245 ActionColor::ActionColor(const TQColor& c)
246  : ActionBase(), color(c)
247 {
248 }
249 
250 ActionColor::ActionColor(const TQString& s)
251  : ActionBase()
252 {
253  setValue(s);
254 }
255 
256 ActionColor::ActionColor(const ActionColor& a)
257  : ActionBase(), color(a.color)
258 {
259 }
260 
261 ActionColor::~ActionColor()
262 {}
263 
264 TQString ActionColor::toString() const
265 {
266  TQString a;
267  a += "<Action type=\"COLOR\" value=\"" + toXml(color.name()) + "\" />";
268  return a;
269 }
270 
271 void ActionColor::apply(ScorableArticle& a) const
272 {
273  a.changeColor(color);
274 }
275 
276 ActionColor* ActionColor::clone() const
277 {
278  return new ActionColor(*this);
279 }
280 
281 
282 // the notify action
283 ActionNotify::ActionNotify(const TQString& s)
284 {
285  note = s;
286 }
287 
288 ActionNotify::ActionNotify(const ActionNotify& an)
289  : ActionBase()
290 {
291  note = an.note;
292 }
293 
294 TQString ActionNotify::toString() const
295 {
296  return "<Action type=\"NOTIFY\" value=\"" + toXml(note) + "\" />";
297 }
298 
299 void ActionNotify::apply(ScorableArticle& a) const
300 {
301  a.displayMessage(note);
302 }
303 
304 ActionNotify* ActionNotify::clone() const
305 {
306  return new ActionNotify(*this);
307 }
308 
309 
310 // mark as read action
311 ActionMarkAsRead::ActionMarkAsRead() :
312  ActionBase()
313 {
314 }
315 
316 ActionMarkAsRead::ActionMarkAsRead( const ActionMarkAsRead &action ) :
317  ActionBase()
318 {
319  Q_UNUSED( action );
320 }
321 
322 TQString ActionMarkAsRead::toString() const
323 {
324  return "<Action type=\"MARKASREAD\"/>";
325 }
326 
327 void ActionMarkAsRead::apply( ScorableArticle &article ) const
328 {
329  article.markAsRead();
330 }
331 
332 ActionMarkAsRead* ActionMarkAsRead::clone() const
333 {
334  return new ActionMarkAsRead(*this);
335 }
336 
337 //----------------------------------------------------------------------------
338 NotifyCollection::NotifyCollection()
339 {
340  notifyList.setAutoDelete(true);
341 }
342 
343 NotifyCollection::~NotifyCollection()
344 {
345 }
346 
347 void 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 
360 TQString 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 
382 void 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 //----------------------------------------------------------------------------
396 KScoringExpression::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
431 int 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
447 TQString 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
464 TQStringList 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
477 TQStringList 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 
492 KScoringExpression::~KScoringExpression()
493 {
494 }
495 
496 bool 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 
537 void KScoringExpression::write(TQTextStream& st) const
538 {
539  st << toString();
540 }
541 
542 TQString 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 
559 TQString KScoringExpression::getTypeString() const
560 {
561  return KScoringExpression::getTypeString(cond);
562 }
563 
564 TQString 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 
579 int KScoringExpression::getType() const
580 {
581  return cond;
582 }
583 
584 //----------------------------------------------------------------------------
585 KScoringRule::KScoringRule(const TQString& n )
586  : name(n), link(AND)
587 {
588  expressions.setAutoDelete(true);
589  actions.setAutoDelete(true);
590 }
591 
592 KScoringRule::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 
620 KScoringRule::~KScoringRule()
621 {
622  cleanExpressions();
623  cleanActions();
624 }
625 
626 void KScoringRule::cleanExpressions()
627 {
628  // the expressions is setAutoDelete(true)
629  expressions.clear();
630 }
631 
632 void KScoringRule::cleanActions()
633 {
634  // the actions is setAutoDelete(true)
635  actions.clear();
636 }
637 
638 void KScoringRule::addExpression( KScoringExpression* expr)
639 {
640  kdDebug(5100) << "KScoringRule::addExpression" << endl;
641  expressions.append(expr);
642 }
643 
644 void KScoringRule::addAction(int type, const TQString& val)
645 {
646  ActionBase *action = ActionBase::factory(type,val);
647  addAction(action);
648 }
649 
650 void KScoringRule::addAction(ActionBase* a)
651 {
652  kdDebug(5100) << "KScoringRule::addAction() " << a->toString() << endl;
653  actions.append(a);
654 }
655 
656 void KScoringRule::setLinkMode(const TQString& l)
657 {
658  if (l == "OR") link = OR;
659  else link = AND;
660 }
661 
662 void 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 
674 bool 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 
685 void KScoringRule::applyAction(ScorableArticle& a) const
686 {
687  TQPtrListIterator<ActionBase> it(actions);
688  for(; it.current(); ++it) {
689  it.current()->apply(a);
690  }
691 }
692 
693 void 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 
712 void 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 
723 void KScoringRule::write(TQTextStream& s) const
724 {
725  s << toString();
726 }
727 
728 TQString 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 
753 TQString KScoringRule::getLinkModeName() const
754 {
755  switch (link) {
756  case AND: return "AND";
757  case OR: return "OR";
758  default: return "AND";
759  }
760 }
761 
762 TQString 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 
772 bool KScoringRule::isExpired() const
773 {
774  return (expires.isValid() && (expires < TQDate::currentDate()));
775 }
776 
777 
778 
779 //----------------------------------------------------------------------------
780 KScoringManager::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 
794 KScoringManager::~KScoringManager()
795 {
796 }
797 
798 void 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 
817 void 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 
830 TQDomDocument 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 
844 TQString 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 
855 void 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 
865 void 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 
918 KScoringRule* 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 
935 KScoringRule* 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 
948 KScoringRule* KScoringManager::addRule()
949 {
950  KScoringRule *rule = new KScoringRule(findUniqueName());
951  addRule(rule);
952  return rule;
953 }
954 
955 void 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 
963 void 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 
976 void 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 
1001 void KScoringManager::deleteRule(KScoringRule *r)
1002 {
1003  int i = allRules.findRef(r);
1004  if (i != -1) {
1005  allRules.remove();
1006  emit changedRules();
1007  }
1008 }
1009 
1010 void 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 
1018 void 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 
1030 void 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 
1042 void KScoringManager::editorReady()
1043 {
1044  kdDebug(5100) << "emitting signal finishedEditing" << endl;
1045  save();
1046  emit finishedEditing();
1047 }
1048 
1049 KScoringRule* KScoringManager::copyRule(KScoringRule *r)
1050 {
1051  KScoringRule *rule = new KScoringRule(*r);
1052  rule->setName(findUniqueName());
1053  addRuleInternal(rule);
1054  return rule;
1055 }
1056 
1057 void KScoringManager::applyRules(ScorableGroup* )
1058 {
1059  kdWarning(5100) << "KScoringManager::applyRules(ScorableGroup* ) isn't implemented" << endl;
1060 }
1061 
1062 void KScoringManager::applyRules(ScorableArticle& article, const TQString& group)
1063 {
1064  setGroup(group);
1065  applyRules(article);
1066 }
1067 
1068 void 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 
1076 void 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 
1091 void KScoringManager::setGroup(const TQString& g)
1092 {
1093  if (group != g) initCache(g);
1094 }
1095 
1096 bool KScoringManager::hasRulesForCurrentGroup()
1097 {
1098  return ruleList.count() != 0;
1099 }
1100 
1101 
1102 TQStringList 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 
1112 KScoringRule* 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 
1123 bool KScoringManager::setCacheValid(bool v)
1124 {
1125  bool res = cacheValid;
1126  cacheValid = v;
1127  return res;
1128 }
1129 
1130 TQString 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 
1156 bool 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 
1167 TQStringList 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 
1177 void KScoringManager::pushRuleList()
1178 {
1179  stack.push(allRules);
1180 }
1181 
1182 void KScoringManager::popRuleList()
1183 {
1184  stack.pop(allRules);
1185 }
1186 
1187 void KScoringManager::removeTOS()
1188 {
1189  stack.drop();
1190 }
1191 
1192 RuleStack::RuleStack()
1193 {
1194 }
1195 
1196 RuleStack::~RuleStack()
1197 {}
1198 
1199 void 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 
1210 void 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 
1218 void 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