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