kscoring.cpp
00001 /* 00002 kscoring.cpp 00003 00004 Copyright (c) 2001 Mathias Waack 00005 Copyright (C) 2005 by Volker Krause <volker.krause@rwth-aachen.de> 00006 00007 Author: Mathias Waack <mathias@atoll-net.de> 00008 00009 This program is free software; you can redistribute it and/or modify 00010 it under the terms of the GNU General Public License as published by 00011 the Free Software Foundation; either version 2 of the License, or 00012 (at your option) any later version. 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software Foundation, 00015 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US 00016 */ 00017 #ifdef KDE_USE_FINAL 00018 #undef TQT_NO_ASCII_CAST 00019 #endif 00020 00021 #undef TQT_NO_COMPAT 00022 00023 #include <iostream> 00024 00025 #include <tqfile.h> 00026 #include <tqdom.h> 00027 #include <tqlayout.h> 00028 #include <tqlabel.h> 00029 #include <tqcheckbox.h> 00030 #include <tqtextview.h> 00031 00032 #include <klocale.h> 00033 #include <kstandarddirs.h> 00034 #include <kdebug.h> 00035 #include <kinputdialog.h> 00036 00037 #include "kscoring.h" 00038 #include "kscoringeditor.h" 00039 00040 00041 //---------------------------------------------------------------------------- 00042 // a small function to encode attribute values, code stolen from TQDom 00043 static TQString toXml(const TQString& str) 00044 { 00045 TQString tmp(str); 00046 uint len = tmp.length(); 00047 uint i = 0; 00048 while ( i < len ) { 00049 if (tmp[(int)i] == '<') { 00050 tmp.replace(i, 1, "<"); 00051 len += 3; 00052 i += 4; 00053 } else if (tmp[(int)i] == '"') { 00054 tmp.replace(i, 1, """); 00055 len += 5; 00056 i += 6; 00057 } else if (tmp[(int)i] == '&') { 00058 tmp.replace(i, 1, "&"); 00059 len += 4; 00060 i += 5; 00061 } else if (tmp[(int)i] == '>') { 00062 tmp.replace(i, 1, ">"); 00063 len += 3; 00064 i += 4; 00065 } else { 00066 ++i; 00067 } 00068 } 00069 00070 return tmp; 00071 } 00072 00073 00074 // small dialog to display the messages from NotifyAction 00075 NotifyDialog* NotifyDialog::me = 0; 00076 NotifyDialog::NotesMap NotifyDialog::dict; 00077 00078 NotifyDialog::NotifyDialog(TQWidget* p) 00079 : KDialogBase(p,"notify action dialog",true,"Notify Message",Close,Close,true) 00080 { 00081 TQFrame *f = makeMainWidget(); 00082 TQVBoxLayout *topL = new TQVBoxLayout(f); 00083 note = new TQLabel(f); 00084 note->setTextFormat(RichText); 00085 topL->addWidget(note); 00086 TQCheckBox *check = new TQCheckBox(i18n("Do not show this message again"),f); 00087 check->setChecked(true); 00088 topL->addWidget(check); 00089 connect(check,TQT_SIGNAL(toggled(bool)),TQT_SLOT(slotShowAgainToggled(bool))); 00090 } 00091 00092 void NotifyDialog::slotShowAgainToggled(bool flag) 00093 { 00094 dict.replace(msg,!flag); 00095 kdDebug(5100) << "note \"" << note << "\" will popup again: " << flag << endl; 00096 } 00097 00098 void NotifyDialog::display(ScorableArticle& a, const TQString& s) 00099 { 00100 kdDebug(5100) << "displaying message" << endl; 00101 if (!me) me = new NotifyDialog(); 00102 me->msg = s; 00103 00104 NotesMap::Iterator i = dict.find(s); 00105 if (i == dict.end() || i.data()) { 00106 TQString msg = i18n("Article\n<b>%1</b><br><b>%2</b><br>caused the following" 00107 " note to appear:<br>%3"). 00108 arg(a.from()). 00109 arg(a.subject()). 00110 arg(s); 00111 me->note->setText(msg); 00112 if ( i == dict.end() ) i = dict.replace(s,false); 00113 me->adjustSize(); 00114 me->exec(); 00115 } 00116 } 00117 00118 00119 //---------------------------------------------------------------------------- 00120 ScorableArticle::~ScorableArticle() 00121 { 00122 } 00123 00124 void ScorableArticle::displayMessage(const TQString& note) 00125 { 00126 NotifyDialog::display(*this,note); 00127 } 00128 00129 //---------------------------------------------------------------------------- 00130 ScorableGroup::~ScorableGroup() 00131 { 00132 } 00133 00134 // the base class for all actions 00135 ActionBase::ActionBase() 00136 { 00137 kdDebug(5100) << "new Action " << this << endl; 00138 } 00139 00140 ActionBase::~ActionBase() 00141 { 00142 kdDebug(5100) << "delete Action " << this << endl; 00143 } 00144 00145 00146 TQStringList ActionBase::userNames() 00147 { 00148 TQStringList l; 00149 l << userName(SETSCORE); 00150 l << userName(NOTIFY); 00151 l << userName(COLOR); 00152 l << userName(MARKASREAD); 00153 return l; 00154 } 00155 00156 ActionBase* ActionBase::factory(int type, const TQString &value) 00157 { 00158 switch (type) { 00159 case SETSCORE: return new ActionSetScore(value); 00160 case NOTIFY: return new ActionNotify(value); 00161 case COLOR: return new ActionColor(value); 00162 case MARKASREAD: return new ActionMarkAsRead(); 00163 default: 00164 kdWarning(5100) << "unknown type " << type << " in ActionBase::factory()" << endl; 00165 return 0; 00166 } 00167 } 00168 00169 TQString ActionBase::userName(int type) 00170 { 00171 switch (type) { 00172 case SETSCORE: return i18n("Adjust Score"); 00173 case NOTIFY: return i18n("Display Message"); 00174 case COLOR: return i18n("Colorize Header"); 00175 case MARKASREAD: return i18n("Mark As Read"); 00176 default: 00177 kdWarning(5100) << "unknown type " << type << " in ActionBase::userName()" << endl; 00178 return 0; 00179 } 00180 } 00181 00182 int ActionBase::getTypeForName(const TQString& name) 00183 { 00184 if (name == "SETSCORE") return SETSCORE; 00185 else if (name == "NOTIFY") return NOTIFY; 00186 else if (name == "COLOR") return COLOR; 00187 else if (name == "MARKASREAD") return MARKASREAD; 00188 else { 00189 kdWarning(5100) << "unknown type string " << name 00190 << " in ActionBase::getTypeForName()" << endl; 00191 return -1; 00192 } 00193 } 00194 00195 int ActionBase::getTypeForUserName(const TQString& name) 00196 { 00197 if (name == userName(SETSCORE)) return SETSCORE; 00198 else if (name == userName(NOTIFY)) return NOTIFY; 00199 else if (name == userName(COLOR)) return COLOR; 00200 else if ( name == userName(MARKASREAD) ) return MARKASREAD; 00201 else { 00202 kdWarning(5100) << "unknown type string " << name 00203 << " in ActionBase::getTypeForUserName()" << endl; 00204 return -1; 00205 } 00206 } 00207 00208 // the set score action 00209 ActionSetScore::ActionSetScore(short v) 00210 : val(v) 00211 { 00212 } 00213 00214 ActionSetScore::ActionSetScore(const TQString& s) 00215 { 00216 val = s.toShort(); 00217 } 00218 00219 ActionSetScore::ActionSetScore(const ActionSetScore& as) 00220 : ActionBase(), 00221 val(as.val) 00222 { 00223 } 00224 00225 ActionSetScore::~ActionSetScore() 00226 { 00227 } 00228 00229 TQString ActionSetScore::toString() const 00230 { 00231 TQString a; 00232 a += "<Action type=\"SETSCORE\" value=\"" + TQString::number(val) + "\" />"; 00233 return a; 00234 } 00235 00236 void ActionSetScore::apply(ScorableArticle& a) const 00237 { 00238 a.addScore(val); 00239 } 00240 00241 ActionSetScore* ActionSetScore::clone() const 00242 { 00243 return new ActionSetScore(*this); 00244 } 00245 00246 // the color action 00247 ActionColor::ActionColor(const TQColor& c) 00248 : ActionBase(), color(c) 00249 { 00250 } 00251 00252 ActionColor::ActionColor(const TQString& s) 00253 : ActionBase() 00254 { 00255 setValue(s); 00256 } 00257 00258 ActionColor::ActionColor(const ActionColor& a) 00259 : ActionBase(), color(a.color) 00260 { 00261 } 00262 00263 ActionColor::~ActionColor() 00264 {} 00265 00266 TQString ActionColor::toString() const 00267 { 00268 TQString a; 00269 a += "<Action type=\"COLOR\" value=\"" + toXml(color.name()) + "\" />"; 00270 return a; 00271 } 00272 00273 void ActionColor::apply(ScorableArticle& a) const 00274 { 00275 a.changeColor(color); 00276 } 00277 00278 ActionColor* ActionColor::clone() const 00279 { 00280 return new ActionColor(*this); 00281 } 00282 00283 00284 // the notify action 00285 ActionNotify::ActionNotify(const TQString& s) 00286 { 00287 note = s; 00288 } 00289 00290 ActionNotify::ActionNotify(const ActionNotify& an) 00291 : ActionBase() 00292 { 00293 note = an.note; 00294 } 00295 00296 TQString ActionNotify::toString() const 00297 { 00298 return "<Action type=\"NOTIFY\" value=\"" + toXml(note) + "\" />"; 00299 } 00300 00301 void ActionNotify::apply(ScorableArticle& a) const 00302 { 00303 a.displayMessage(note); 00304 } 00305 00306 ActionNotify* ActionNotify::clone() const 00307 { 00308 return new ActionNotify(*this); 00309 } 00310 00311 00312 // mark as read action 00313 ActionMarkAsRead::ActionMarkAsRead() : 00314 ActionBase() 00315 { 00316 } 00317 00318 ActionMarkAsRead::ActionMarkAsRead( const ActionMarkAsRead &action ) : 00319 ActionBase() 00320 { 00321 Q_UNUSED( action ); 00322 } 00323 00324 TQString ActionMarkAsRead::toString() const 00325 { 00326 return "<Action type=\"MARKASREAD\"/>"; 00327 } 00328 00329 void ActionMarkAsRead::apply( ScorableArticle &article ) const 00330 { 00331 article.markAsRead(); 00332 } 00333 00334 ActionMarkAsRead* ActionMarkAsRead::clone() const 00335 { 00336 return new ActionMarkAsRead(*this); 00337 } 00338 00339 //---------------------------------------------------------------------------- 00340 NotifyCollection::NotifyCollection() 00341 { 00342 notifyList.setAutoDelete(true); 00343 } 00344 00345 NotifyCollection::~NotifyCollection() 00346 { 00347 } 00348 00349 void NotifyCollection::addNote(const ScorableArticle& a, const TQString& note) 00350 { 00351 article_list *l = notifyList.find(note); 00352 if (!l) { 00353 notifyList.insert(note,new article_list); 00354 l = notifyList.find(note); 00355 } 00356 article_info i; 00357 i.from = a.from(); 00358 i.subject = a.subject(); 00359 l->append(i); 00360 } 00361 00362 TQString NotifyCollection::collection() const 00363 { 00364 TQString notifyCollection = i18n("<h1>List of collected notes</h1>"); 00365 notifyCollection += "<p><ul>"; 00366 // first look thru the notes and create one string 00367 TQDictIterator<article_list> it(notifyList); 00368 for(;it.current();++it) { 00369 const TQString& note = it.currentKey(); 00370 notifyCollection += "<li>" + note + "<ul>"; 00371 article_list* alist = it.current(); 00372 article_list::Iterator ait; 00373 for(ait = alist->begin(); ait != alist->end(); ++ait) { 00374 notifyCollection += "<li><b>From: </b>" + (*ait).from + "<br>"; 00375 notifyCollection += "<b>Subject: </b>" + (*ait).subject; 00376 } 00377 notifyCollection += "</ul>"; 00378 } 00379 notifyCollection += "</ul>"; 00380 00381 return notifyCollection; 00382 } 00383 00384 void NotifyCollection::displayCollection(TQWidget *p) const 00385 { 00386 //KMessageBox::information(p,collection(),i18n("Collected Notes")); 00387 KDialogBase *dlg = new KDialogBase( p, 0, false, i18n("Collected Notes"), 00388 KDialogBase::Close, KDialogBase::Close ); 00389 TQTextView *text = new TQTextView(dlg); 00390 text->setText(collection()); 00391 dlg->setMainWidget(text); 00392 dlg->setMinimumWidth(300); 00393 dlg->setMinimumHeight(300); 00394 dlg->show(); 00395 } 00396 00397 //---------------------------------------------------------------------------- 00398 KScoringExpression::KScoringExpression(const TQString& h, const TQString& t, const TQString& n, const TQString& ng) 00399 : header(h), expr_str(n) 00400 { 00401 if (t == "MATCH" ) { 00402 cond = MATCH; 00403 expr.setPattern(expr_str); 00404 expr.setCaseSensitive(false); 00405 } 00406 else if ( t == "MATCHCS" ) { 00407 cond = MATCHCS; 00408 expr.setPattern( expr_str ); 00409 expr.setCaseSensitive( true ); 00410 } 00411 else if (t == "CONTAINS" ) cond = CONTAINS; 00412 else if (t == "EQUALS" ) cond = EQUALS; 00413 else if (t == "GREATER") { 00414 cond = GREATER; 00415 expr_int = expr_str.toInt(); 00416 } 00417 else if (t == "SMALLER") { 00418 cond = SMALLER; 00419 expr_int = expr_str.toInt(); 00420 } 00421 else { 00422 kdDebug(5100) << "unknown match type in new expression" << endl; 00423 } 00424 00425 neg = ng.toInt(); 00426 c_header = header.latin1(); 00427 00428 kdDebug(5100) << "new expr: " << c_header << " " << t << " " 00429 << expr_str << " " << neg << endl; 00430 } 00431 00432 // static 00433 int KScoringExpression::getConditionForName(const TQString& s) 00434 { 00435 if (s == getNameForCondition(CONTAINS)) return CONTAINS; 00436 else if (s == getNameForCondition(MATCH)) return MATCH; 00437 else if (s == getNameForCondition(MATCHCS)) return MATCHCS; 00438 else if (s == getNameForCondition(EQUALS)) return EQUALS; 00439 else if (s == getNameForCondition(SMALLER)) return SMALLER; 00440 else if (s == getNameForCondition(GREATER)) return GREATER; 00441 else { 00442 kdWarning(5100) << "unknown condition name " << s 00443 << " in KScoringExpression::getConditionForName()" << endl; 00444 return -1; 00445 } 00446 } 00447 00448 // static 00449 TQString KScoringExpression::getNameForCondition(int cond) 00450 { 00451 switch (cond) { 00452 case CONTAINS: return i18n("Contains Substring"); 00453 case MATCH: return i18n("Matches Regular Expression"); 00454 case MATCHCS: return i18n("Matches Regular Expression (Case Sensitive)"); 00455 case EQUALS: return i18n("Is Exactly the Same As"); 00456 case SMALLER: return i18n("Less Than"); 00457 case GREATER: return i18n("Greater Than"); 00458 default: 00459 kdWarning(5100) << "unknown condition " << cond 00460 << " in KScoringExpression::getNameForCondition()" << endl; 00461 return ""; 00462 } 00463 } 00464 00465 // static 00466 TQStringList KScoringExpression::conditionNames() 00467 { 00468 TQStringList l; 00469 l << getNameForCondition(CONTAINS); 00470 l << getNameForCondition(MATCH); 00471 l << getNameForCondition(MATCHCS); 00472 l << getNameForCondition(EQUALS); 00473 l << getNameForCondition(SMALLER); 00474 l << getNameForCondition(GREATER); 00475 return l; 00476 } 00477 00478 // static 00479 TQStringList KScoringExpression::headerNames() 00480 { 00481 TQStringList l; 00482 l.append("From"); 00483 l.append("Message-ID"); 00484 l.append("Subject"); 00485 l.append("Date"); 00486 l.append("References"); 00487 l.append("NNTP-Posting-Host"); 00488 l.append("Bytes"); 00489 l.append("Lines"); 00490 l.append("Xref"); 00491 return l; 00492 } 00493 00494 KScoringExpression::~KScoringExpression() 00495 { 00496 } 00497 00498 bool KScoringExpression::match(ScorableArticle& a) const 00499 { 00500 //kdDebug(5100) << "matching against header " << c_header << endl; 00501 bool res = true; 00502 TQString head; 00503 00504 if (header == "From") 00505 head = a.from(); 00506 else if (header == "Subject") 00507 head = a.subject(); 00508 else 00509 head = a.getHeaderByType(c_header); 00510 00511 if (!head.isEmpty()) { 00512 switch (cond) { 00513 case EQUALS: 00514 res = (head.lower() == expr_str.lower()); 00515 break; 00516 case CONTAINS: 00517 res = (head.lower().find(expr_str.lower()) >= 0); 00518 break; 00519 case MATCH: 00520 case MATCHCS: 00521 res = (expr.search(head)!=-1); 00522 break; 00523 case GREATER: 00524 res = (head.toInt() > expr_int); 00525 break; 00526 case SMALLER: 00527 res = (head.toInt() < expr_int); 00528 break; 00529 default: 00530 kdDebug(5100) << "unknown match" << endl; 00531 res = false; 00532 } 00533 } 00534 else res = false; 00535 // kdDebug(5100) << "matching returns " << res << endl; 00536 return (neg)?!res:res; 00537 } 00538 00539 void KScoringExpression::write(TQTextStream& st) const 00540 { 00541 st << toString(); 00542 } 00543 00544 TQString KScoringExpression::toString() const 00545 { 00546 // kdDebug(5100) << "KScoringExpression::toString() starts" << endl; 00547 // kdDebug(5100) << "header is " << header << endl; 00548 // kdDebug(5100) << "expr is " << expr_str << endl; 00549 // kdDebug(5100) << "neg is " << neg << endl; 00550 // kdDebug(5100) << "type is " << getType() << endl; 00551 TQString e; 00552 e += "<Expression neg=\"" + TQString::number(neg?1:0) 00553 + "\" header=\"" + header 00554 + "\" type=\"" + getTypeString() 00555 + "\" expr=\"" + toXml(expr_str) 00556 + "\" />"; 00557 // kdDebug(5100) << "KScoringExpression::toString() finished" << endl; 00558 return e; 00559 } 00560 00561 TQString KScoringExpression::getTypeString() const 00562 { 00563 return KScoringExpression::getTypeString(cond); 00564 } 00565 00566 TQString KScoringExpression::getTypeString(int cond) 00567 { 00568 switch (cond) { 00569 case CONTAINS: return "CONTAINS"; 00570 case MATCH: return "MATCH"; 00571 case MATCHCS: return "MATCHCS"; 00572 case EQUALS: return "EQUALS"; 00573 case SMALLER: return "SMALLER"; 00574 case GREATER: return "GREATER"; 00575 default: 00576 kdWarning(5100) << "unknown cond " << cond << " in KScoringExpression::getTypeString()" << endl; 00577 return ""; 00578 } 00579 } 00580 00581 int KScoringExpression::getType() const 00582 { 00583 return cond; 00584 } 00585 00586 //---------------------------------------------------------------------------- 00587 KScoringRule::KScoringRule(const TQString& n ) 00588 : name(n), link(AND) 00589 { 00590 expressions.setAutoDelete(true); 00591 actions.setAutoDelete(true); 00592 } 00593 00594 KScoringRule::KScoringRule(const KScoringRule& r) 00595 { 00596 kdDebug(5100) << "copying rule " << r.getName() << endl; 00597 name = r.getName(); 00598 expressions.setAutoDelete(true); 00599 actions.setAutoDelete(true); 00600 // copy expressions 00601 expressions.clear(); 00602 const ScoreExprList& rexpr = r.expressions; 00603 TQPtrListIterator<KScoringExpression> it(rexpr); 00604 for ( ; it.current(); ++it ) { 00605 KScoringExpression *t = new KScoringExpression(**it); 00606 expressions.append(t); 00607 } 00608 // copy actions 00609 actions.clear(); 00610 const ActionList& ract = r.actions; 00611 TQPtrListIterator<ActionBase> ait(ract); 00612 for ( ; ait.current(); ++ait ) { 00613 ActionBase *t = *ait; 00614 actions.append(t->clone()); 00615 } 00616 // copy groups, servers, linkmode and expires 00617 groups = r.groups; 00618 expires = r.expires; 00619 link = r.link; 00620 } 00621 00622 KScoringRule::~KScoringRule() 00623 { 00624 cleanExpressions(); 00625 cleanActions(); 00626 } 00627 00628 void KScoringRule::cleanExpressions() 00629 { 00630 // the expressions is setAutoDelete(true) 00631 expressions.clear(); 00632 } 00633 00634 void KScoringRule::cleanActions() 00635 { 00636 // the actions is setAutoDelete(true) 00637 actions.clear(); 00638 } 00639 00640 void KScoringRule::addExpression( KScoringExpression* expr) 00641 { 00642 kdDebug(5100) << "KScoringRule::addExpression" << endl; 00643 expressions.append(expr); 00644 } 00645 00646 void KScoringRule::addAction(int type, const TQString& val) 00647 { 00648 ActionBase *action = ActionBase::factory(type,val); 00649 addAction(action); 00650 } 00651 00652 void KScoringRule::addAction(ActionBase* a) 00653 { 00654 kdDebug(5100) << "KScoringRule::addAction() " << a->toString() << endl; 00655 actions.append(a); 00656 } 00657 00658 void KScoringRule::setLinkMode(const TQString& l) 00659 { 00660 if (l == "OR") link = OR; 00661 else link = AND; 00662 } 00663 00664 void KScoringRule::setExpire(const TQString& e) 00665 { 00666 if (e != "never") { 00667 TQStringList l = TQStringList::split("-",e); 00668 Q_ASSERT( l.count() == 3 ); 00669 expires.setYMD( (*(l.at(0))).toInt(), 00670 (*(l.at(1))).toInt(), 00671 (*(l.at(2))).toInt()); 00672 } 00673 kdDebug(5100) << "Rule " << getName() << " expires at " << getExpireDateString() << endl; 00674 } 00675 00676 bool KScoringRule::matchGroup(const TQString& group) const 00677 { 00678 for(GroupList::ConstIterator i=groups.begin(); i!=groups.end();++i) { 00679 TQRegExp e(*i); 00680 if (e.search(group, 0) != -1 && 00681 (uint)e.matchedLength() == group.length()) 00682 return true; 00683 } 00684 return false; 00685 } 00686 00687 void KScoringRule::applyAction(ScorableArticle& a) const 00688 { 00689 TQPtrListIterator<ActionBase> it(actions); 00690 for(; it.current(); ++it) { 00691 it.current()->apply(a); 00692 } 00693 } 00694 00695 void KScoringRule::applyRule(ScorableArticle& a) const 00696 { 00697 // kdDebug(5100) << "checking rule " << name << endl; 00698 // kdDebug(5100) << " for article from " 00699 // << a->from()->asUnicodeString() 00700 // << endl; 00701 bool oper_and = (link == AND); 00702 bool res = true; 00703 TQPtrListIterator<KScoringExpression> it(expressions); 00704 //kdDebug(5100) << "checking " << expressions.count() << " expressions" << endl; 00705 for (; it.current(); ++it) { 00706 Q_ASSERT( it.current() ); 00707 res = it.current()->match(a); 00708 if (!res && oper_and) return; 00709 else if (res && !oper_and) break; 00710 } 00711 if (res) applyAction(a); 00712 } 00713 00714 void KScoringRule::applyRule(ScorableArticle& a /*, const TQString& s*/, const TQString& g) const 00715 { 00716 // check if one of the groups match 00717 for (TQStringList::ConstIterator i = groups.begin(); i != groups.end(); ++i) { 00718 if (TQRegExp(*i).search(g) != -1) { 00719 applyRule(a); 00720 return; 00721 } 00722 } 00723 } 00724 00725 void KScoringRule::write(TQTextStream& s) const 00726 { 00727 s << toString(); 00728 } 00729 00730 TQString KScoringRule::toString() const 00731 { 00732 //kdDebug(5100) << "KScoringRule::toString() starts" << endl; 00733 TQString r; 00734 r += "<Rule name=\"" + toXml(name) + "\" linkmode=\"" + getLinkModeName(); 00735 r += "\" expires=\"" + getExpireDateString() + "\">"; 00736 //kdDebug(5100) << "building grouplist..." << endl; 00737 for(GroupList::ConstIterator i=groups.begin();i!=groups.end();++i) { 00738 r += "<Group name=\"" + toXml(*i) + "\" />"; 00739 } 00740 //kdDebug(5100) << "building expressionlist..." << endl; 00741 TQPtrListIterator<KScoringExpression> eit(expressions); 00742 for (; eit.current(); ++eit) { 00743 r += eit.current()->toString(); 00744 } 00745 //kdDebug(5100) << "building actionlist..." << endl; 00746 TQPtrListIterator<ActionBase> ait(actions); 00747 for (; ait.current(); ++ait) { 00748 r += ait.current()->toString(); 00749 } 00750 r += "</Rule>"; 00751 //kdDebug(5100) << "KScoringRule::toString() finished" << endl; 00752 return r; 00753 } 00754 00755 TQString KScoringRule::getLinkModeName() const 00756 { 00757 switch (link) { 00758 case AND: return "AND"; 00759 case OR: return "OR"; 00760 default: return "AND"; 00761 } 00762 } 00763 00764 TQString KScoringRule::getExpireDateString() const 00765 { 00766 if (expires.isNull()) return "never"; 00767 else { 00768 return TQString::number(expires.year()) + TQString("-") 00769 + TQString::number(expires.month()) + TQString("-") 00770 + TQString::number(expires.day()); 00771 } 00772 } 00773 00774 bool KScoringRule::isExpired() const 00775 { 00776 return (expires.isValid() && (expires < TQDate::currentDate())); 00777 } 00778 00779 00780 00781 //---------------------------------------------------------------------------- 00782 KScoringManager::KScoringManager(const TQString& appName) 00783 : cacheValid(false)//, _s(0) 00784 { 00785 allRules.setAutoDelete(true); 00786 // determine filename of the scorefile 00787 if(appName.isEmpty()) 00788 mFilename = KGlobal::dirs()->saveLocation("appdata") + "/scorefile"; 00789 else 00790 mFilename = KGlobal::dirs()->saveLocation("data") + "/" + appName + "/scorefile"; 00791 // open the score file 00792 load(); 00793 } 00794 00795 00796 KScoringManager::~KScoringManager() 00797 { 00798 } 00799 00800 void KScoringManager::load() 00801 { 00802 TQDomDocument sdoc("Scorefile"); 00803 TQFile f( mFilename ); 00804 if ( !f.open( IO_ReadOnly ) ) 00805 return; 00806 if ( !sdoc.setContent( &f ) ) { 00807 f.close(); 00808 kdDebug(5100) << "loading the scorefile failed" << endl; 00809 return; 00810 } 00811 f.close(); 00812 kdDebug(5100) << "loaded the scorefile, creating internal representation" << endl; 00813 allRules.clear(); 00814 createInternalFromXML(sdoc); 00815 expireRules(); 00816 kdDebug(5100) << "ready, got " << allRules.count() << " rules" << endl; 00817 } 00818 00819 void KScoringManager::save() 00820 { 00821 kdDebug(5100) << "KScoringManager::save() starts" << endl; 00822 TQFile f( mFilename ); 00823 if ( !f.open( IO_WriteOnly ) ) 00824 return; 00825 TQTextStream stream(&f); 00826 stream.setEncoding(TQTextStream::Unicode); 00827 kdDebug(5100) << "KScoringManager::save() creating xml" << endl; 00828 createXMLfromInternal().save(stream,2); 00829 kdDebug(5100) << "KScoringManager::save() finished" << endl; 00830 } 00831 00832 TQDomDocument KScoringManager::createXMLfromInternal() 00833 { 00834 // I was'nt able to create a TQDomDocument in memory:( 00835 // so I write the content into a string, which is really stupid 00836 TQDomDocument sdoc("Scorefile"); 00837 TQString ss; // scorestring 00838 ss += "<?xml version = '1.0'?><!DOCTYPE Scorefile >"; 00839 ss += toString(); 00840 ss += "</Scorefile>\n"; 00841 kdDebug(5100) << "KScoringManager::createXMLfromInternal():" << endl << ss << endl; 00842 sdoc.setContent(ss); 00843 return sdoc; 00844 } 00845 00846 TQString KScoringManager::toString() const 00847 { 00848 TQString s; 00849 s += "<Scorefile>\n"; 00850 TQPtrListIterator<KScoringRule> it(allRules); 00851 for( ; it.current(); ++it) { 00852 s += it.current()->toString(); 00853 } 00854 return s; 00855 } 00856 00857 void KScoringManager::expireRules() 00858 { 00859 for ( KScoringRule *cR = allRules.first(); cR; cR=allRules.next()) { 00860 if (cR->isExpired()) { 00861 kdDebug(5100) << "Rule " << cR->getName() << " is expired, deleting it" << endl; 00862 allRules.remove(); 00863 } 00864 } 00865 } 00866 00867 void KScoringManager::createInternalFromXML(TQDomNode n) 00868 { 00869 static KScoringRule *cR = 0; // the currentRule 00870 // the XML file was parsed and now we simply traverse the resulting tree 00871 if ( !n.isNull() ) { 00872 kdDebug(5100) << "inspecting node of type " << n.nodeType() 00873 << " named " << n.toElement().tagName() << endl; 00874 00875 switch (n.nodeType()) { 00876 case TQDomNode::DocumentNode: { 00877 // the document itself 00878 break; 00879 } 00880 case TQDomNode::ElementNode: { 00881 // Server, Newsgroup, Rule, Expression, Action 00882 TQDomElement e = n.toElement(); 00883 //kdDebug(5100) << "The name of the element is " 00884 //<< e.tagName().latin1() << endl; 00885 TQString s = e.tagName(); 00886 if (s == "Rule") { 00887 cR = new KScoringRule(e.attribute("name")); 00888 cR->setLinkMode(e.attribute("linkmode")); 00889 cR->setExpire(e.attribute("expires")); 00890 addRuleInternal(cR); 00891 } 00892 else if (s == "Group") { 00893 Q_CHECK_PTR(cR); 00894 cR->addGroup( e.attribute("name") ); 00895 } 00896 else if (s == "Expression") { 00897 cR->addExpression(new KScoringExpression(e.attribute("header"), 00898 e.attribute("type"), 00899 e.attribute("expr"), 00900 e.attribute("neg"))); 00901 } 00902 else if (s == "Action") { 00903 Q_CHECK_PTR(cR); 00904 cR->addAction(ActionBase::getTypeForName(e.attribute("type")), 00905 e.attribute("value")); 00906 } 00907 break; 00908 } 00909 default: // kdDebug(5100) << "unknown DomNode::type" << endl; 00910 ; 00911 } 00912 TQDomNodeList nodelist = n.childNodes(); 00913 unsigned cnt = nodelist.count(); 00914 //kdDebug(5100) << "recursive checking " << cnt << " nodes" << endl; 00915 for (unsigned i=0;i<cnt;++i) 00916 createInternalFromXML(nodelist.item(i)); 00917 } 00918 } 00919 00920 KScoringRule* KScoringManager::addRule(const ScorableArticle& a, TQString group, short score) 00921 { 00922 KScoringRule *rule = new KScoringRule(findUniqueName()); 00923 rule->addGroup( group ); 00924 rule->addExpression( 00925 new KScoringExpression("From","CONTAINS", 00926 a.from(),"0")); 00927 if (score) rule->addAction(new ActionSetScore(score)); 00928 rule->setExpireDate(TQDate::currentDate().addDays(30)); 00929 addRule(rule); 00930 KScoringEditor *edit = KScoringEditor::createEditor(this); 00931 edit->setRule(rule); 00932 edit->show(); 00933 setCacheValid(false); 00934 return rule; 00935 } 00936 00937 KScoringRule* KScoringManager::addRule(KScoringRule* expr) 00938 { 00939 int i = allRules.findRef(expr); 00940 if (i == -1) { 00941 // only add a rule we don't know 00942 addRuleInternal(expr); 00943 } 00944 else { 00945 emit changedRules(); 00946 } 00947 return expr; 00948 } 00949 00950 KScoringRule* KScoringManager::addRule() 00951 { 00952 KScoringRule *rule = new KScoringRule(findUniqueName()); 00953 addRule(rule); 00954 return rule; 00955 } 00956 00957 void KScoringManager::addRuleInternal(KScoringRule *e) 00958 { 00959 allRules.append(e); 00960 setCacheValid(false); 00961 emit changedRules(); 00962 kdDebug(5100) << "KScoringManager::addRuleInternal " << e->getName() << endl; 00963 } 00964 00965 void KScoringManager::cancelNewRule(KScoringRule *r) 00966 { 00967 // if e was'nt previously added to the list of rules, we delete it 00968 int i = allRules.findRef(r); 00969 if (i == -1) { 00970 kdDebug(5100) << "deleting rule " << r->getName() << endl; 00971 deleteRule(r); 00972 } 00973 else { 00974 kdDebug(5100) << "rule " << r->getName() << " not deleted" << endl; 00975 } 00976 } 00977 00978 void KScoringManager::setRuleName(KScoringRule *r, const TQString& s) 00979 { 00980 bool cont = true; 00981 TQString text = s; 00982 TQString oldName = r->getName(); 00983 while (cont) { 00984 cont = false; 00985 TQPtrListIterator<KScoringRule> it(allRules); 00986 for (; it.current(); ++it) { 00987 if ( it.current() != r && it.current()->getName() == text ) { 00988 kdDebug(5100) << "rule name " << text << " is not unique" << endl; 00989 text = KInputDialog::getText(i18n("Choose Another Rule Name"), 00990 i18n("The rule name is already assigned, please choose another name:"), 00991 text); 00992 cont = true; 00993 break; 00994 } 00995 } 00996 } 00997 if (text != oldName) { 00998 r->setName(text); 00999 emit changedRuleName(oldName,text); 01000 } 01001 } 01002 01003 void KScoringManager::deleteRule(KScoringRule *r) 01004 { 01005 int i = allRules.findRef(r); 01006 if (i != -1) { 01007 allRules.remove(); 01008 emit changedRules(); 01009 } 01010 } 01011 01012 void KScoringManager::editRule(KScoringRule *e, TQWidget *w) 01013 { 01014 KScoringEditor *edit = KScoringEditor::createEditor(this, w); 01015 edit->setRule(e); 01016 edit->show(); 01017 delete edit; 01018 } 01019 01020 void KScoringManager::moveRuleAbove( KScoringRule *above, KScoringRule *below ) 01021 { 01022 int aindex = allRules.findRef( above ); 01023 int bindex = allRules.findRef( below ); 01024 if ( aindex <= 0 || bindex < 0 ) 01025 return; 01026 if ( aindex < bindex ) 01027 --bindex; 01028 allRules.take( aindex ); 01029 allRules.insert( bindex, above ); 01030 } 01031 01032 void KScoringManager::moveRuleBelow( KScoringRule *below, KScoringRule *above ) 01033 { 01034 int bindex = allRules.findRef( below ); 01035 int aindex = allRules.findRef( above ); 01036 if ( bindex < 0 || bindex >= (int)allRules.count() - 1 || aindex < 0 ) 01037 return; 01038 if ( bindex < aindex ) 01039 --aindex; 01040 allRules.take( bindex ); 01041 allRules.insert( aindex + 1, below ); 01042 } 01043 01044 void KScoringManager::editorReady() 01045 { 01046 kdDebug(5100) << "emitting signal finishedEditing" << endl; 01047 save(); 01048 emit finishedEditing(); 01049 } 01050 01051 KScoringRule* KScoringManager::copyRule(KScoringRule *r) 01052 { 01053 KScoringRule *rule = new KScoringRule(*r); 01054 rule->setName(findUniqueName()); 01055 addRuleInternal(rule); 01056 return rule; 01057 } 01058 01059 void KScoringManager::applyRules(ScorableGroup* ) 01060 { 01061 kdWarning(5100) << "KScoringManager::applyRules(ScorableGroup* ) isn't implemented" << endl; 01062 } 01063 01064 void KScoringManager::applyRules(ScorableArticle& article, const TQString& group) 01065 { 01066 setGroup(group); 01067 applyRules(article); 01068 } 01069 01070 void KScoringManager::applyRules(ScorableArticle& a) 01071 { 01072 TQPtrListIterator<KScoringRule> it(isCacheValid()? ruleList : allRules); 01073 for( ; it.current(); ++it) { 01074 it.current()->applyRule(a); 01075 } 01076 } 01077 01078 void KScoringManager::initCache(const TQString& g) 01079 { 01080 group = g; 01081 ruleList.clear(); 01082 TQPtrListIterator<KScoringRule> it(allRules); 01083 for (; it.current(); ++it) { 01084 if ( it.current()->matchGroup(group) ) { 01085 ruleList.append(it.current()); 01086 } 01087 } 01088 kdDebug(5100) << "created cache for group " << group 01089 << " with " << ruleList.count() << " rules" << endl; 01090 setCacheValid(true); 01091 } 01092 01093 void KScoringManager::setGroup(const TQString& g) 01094 { 01095 if (group != g) initCache(g); 01096 } 01097 01098 bool KScoringManager::hasRulesForCurrentGroup() 01099 { 01100 return ruleList.count() != 0; 01101 } 01102 01103 01104 TQStringList KScoringManager::getRuleNames() 01105 { 01106 TQStringList l; 01107 TQPtrListIterator<KScoringRule> it(allRules); 01108 for( ; it.current(); ++it) { 01109 l << it.current()->getName(); 01110 } 01111 return l; 01112 } 01113 01114 KScoringRule* KScoringManager::findRule(const TQString& ruleName) 01115 { 01116 TQPtrListIterator<KScoringRule> it(allRules); 01117 for (; it.current(); ++it) { 01118 if ( it.current()->getName() == ruleName ) { 01119 return it; 01120 } 01121 } 01122 return 0; 01123 } 01124 01125 bool KScoringManager::setCacheValid(bool v) 01126 { 01127 bool res = cacheValid; 01128 cacheValid = v; 01129 return res; 01130 } 01131 01132 TQString KScoringManager::findUniqueName() const 01133 { 01134 int nr = 0; 01135 TQString ret; 01136 bool duplicated=false; 01137 01138 while (nr < 99999999) { 01139 nr++; 01140 ret = i18n("rule %1").arg(nr); 01141 01142 duplicated=false; 01143 TQPtrListIterator<KScoringRule> it(allRules); 01144 for( ; it.current(); ++it) { 01145 if (it.current()->getName() == ret) { 01146 duplicated = true; 01147 break; 01148 } 01149 } 01150 01151 if (!duplicated) 01152 return ret; 01153 } 01154 01155 return ret; 01156 } 01157 01158 bool KScoringManager::hasFeature(int p) 01159 { 01160 switch (p) { 01161 case ActionBase::SETSCORE: return canScores(); 01162 case ActionBase::NOTIFY: return canNotes(); 01163 case ActionBase::COLOR: return canColors(); 01164 case ActionBase::MARKASREAD: return canMarkAsRead(); 01165 default: return false; 01166 } 01167 } 01168 01169 TQStringList KScoringManager::getDefaultHeaders() const 01170 { 01171 TQStringList l; 01172 l.append("Subject"); 01173 l.append("From"); 01174 l.append("Date"); 01175 l.append("Message-ID"); 01176 return l; 01177 } 01178 01179 void KScoringManager::pushRuleList() 01180 { 01181 stack.push(allRules); 01182 } 01183 01184 void KScoringManager::popRuleList() 01185 { 01186 stack.pop(allRules); 01187 } 01188 01189 void KScoringManager::removeTOS() 01190 { 01191 stack.drop(); 01192 } 01193 01194 RuleStack::RuleStack() 01195 { 01196 } 01197 01198 RuleStack::~RuleStack() 01199 {} 01200 01201 void RuleStack::push(TQPtrList<KScoringRule>& l) 01202 { 01203 kdDebug(5100) << "RuleStack::push pushing list with " << l.count() << " rules" << endl; 01204 KScoringManager::ScoringRuleList *l1 = new KScoringManager::ScoringRuleList; 01205 for ( KScoringRule *r=l.first(); r != 0; r=l.next() ) { 01206 l1->append(new KScoringRule(*r)); 01207 } 01208 stack.push(l1); 01209 kdDebug(5100) << "now there are " << stack.count() << " lists on the stack" << endl; 01210 } 01211 01212 void RuleStack::pop(TQPtrList<KScoringRule>& l) 01213 { 01214 top(l); 01215 drop(); 01216 kdDebug(5100) << "RuleStack::pop pops list with " << l.count() << " rules" << endl; 01217 kdDebug(5100) << "now there are " << stack.count() << " lists on the stack" << endl; 01218 } 01219 01220 void RuleStack::top(TQPtrList<KScoringRule>& l) 01221 { 01222 l.clear(); 01223 KScoringManager::ScoringRuleList *l1 = stack.top(); 01224 l = *l1; 01225 } 01226 01227 void RuleStack::drop() 01228 { 01229 kdDebug(5100) << "drop: now there are " << stack.count() << " lists on the stack" << endl; 01230 stack.remove(); 01231 } 01232 01233 01234 #include "kscoring.moc"