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