00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #ifdef KDE_USE_FINAL
00018 #undef QT_NO_ASCII_CAST
00019 #endif
00020
00021 #undef QT_NO_COMPAT
00022
00023 #include <iostream>
00024
00025 #include <qfile.h>
00026 #include <qdom.h>
00027 #include <qlayout.h>
00028 #include <qlabel.h>
00029 #include <qcheckbox.h>
00030 #include <qtextview.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
00043 static QString toXml(const QString& str)
00044 {
00045 QString 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
00075 NotifyDialog* NotifyDialog::me = 0;
00076 NotifyDialog::NotesMap NotifyDialog::dict;
00077
00078 NotifyDialog::NotifyDialog(QWidget* p)
00079 : KDialogBase(p,"notify action dialog",true,"Notify Message",Close,Close,true)
00080 {
00081 QFrame *f = makeMainWidget();
00082 QVBoxLayout *topL = new QVBoxLayout(f);
00083 note = new QLabel(f);
00084 note->setTextFormat(RichText);
00085 topL->addWidget(note);
00086 QCheckBox *check = new QCheckBox(i18n("Do not show this message again"),f);
00087 check->setChecked(true);
00088 topL->addWidget(check);
00089 connect(check,SIGNAL(toggled(bool)),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 QString& 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 QString 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 QString& note)
00125 {
00126 NotifyDialog::display(*this,note);
00127 }
00128
00129
00130 ScorableGroup::~ScorableGroup()
00131 {
00132 }
00133
00134
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 QStringList ActionBase::userNames()
00147 {
00148 QStringList 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 QString &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 QString 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 QString& 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 QString& 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
00209 ActionSetScore::ActionSetScore(short v)
00210 : val(v)
00211 {
00212 }
00213
00214 ActionSetScore::ActionSetScore(const QString& 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 QString ActionSetScore::toString() const
00230 {
00231 QString a;
00232 a += "<Action type=\"SETSCORE\" value=\"" + QString::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
00247 ActionColor::ActionColor(const QColor& c)
00248 : ActionBase(), color(c)
00249 {
00250 }
00251
00252 ActionColor::ActionColor(const QString& 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 QString ActionColor::toString() const
00267 {
00268 QString 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
00285 ActionNotify::ActionNotify(const QString& s)
00286 {
00287 note = s;
00288 }
00289
00290 ActionNotify::ActionNotify(const ActionNotify& an)
00291 : ActionBase()
00292 {
00293 note = an.note;
00294 }
00295
00296 QString 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
00313 ActionMarkAsRead::ActionMarkAsRead() :
00314 ActionBase()
00315 {
00316 }
00317
00318 ActionMarkAsRead::ActionMarkAsRead( const ActionMarkAsRead &action ) :
00319 ActionBase()
00320 {
00321 Q_UNUSED( action );
00322 }
00323
00324 QString 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 QString& 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 QString NotifyCollection::collection() const
00363 {
00364 QString notifyCollection = i18n("<h1>List of collected notes</h1>");
00365 notifyCollection += "<p><ul>";
00366
00367 QDictIterator<article_list> it(notifyList);
00368 for(;it.current();++it) {
00369 const QString& 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(QWidget *p) const
00385 {
00386
00387 KDialogBase *dlg = new KDialogBase( p, 0, false, i18n("Collected Notes"),
00388 KDialogBase::Close, KDialogBase::Close );
00389 QTextView *text = new QTextView(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 QString& h, const QString& t, const QString& n, const QString& 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
00433 int KScoringExpression::getConditionForName(const QString& 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
00449 QString 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
00466 QStringList KScoringExpression::conditionNames()
00467 {
00468 QStringList 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
00479 QStringList KScoringExpression::headerNames()
00480 {
00481 QStringList 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
00501 bool res = true;
00502 QString 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
00536 return (neg)?!res:res;
00537 }
00538
00539 void KScoringExpression::write(QTextStream& st) const
00540 {
00541 st << toString();
00542 }
00543
00544 QString KScoringExpression::toString() const
00545 {
00546
00547
00548
00549
00550
00551 QString e;
00552 e += "<Expression neg=\"" + QString::number(neg?1:0)
00553 + "\" header=\"" + header
00554 + "\" type=\"" + getTypeString()
00555 + "\" expr=\"" + toXml(expr_str)
00556 + "\" />";
00557
00558 return e;
00559 }
00560
00561 QString KScoringExpression::getTypeString() const
00562 {
00563 return KScoringExpression::getTypeString(cond);
00564 }
00565
00566 QString 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 QString& 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
00601 expressions.clear();
00602 const ScoreExprList& rexpr = r.expressions;
00603 QPtrListIterator<KScoringExpression> it(rexpr);
00604 for ( ; it.current(); ++it ) {
00605 KScoringExpression *t = new KScoringExpression(**it);
00606 expressions.append(t);
00607 }
00608
00609 actions.clear();
00610 const ActionList& ract = r.actions;
00611 QPtrListIterator<ActionBase> ait(ract);
00612 for ( ; ait.current(); ++ait ) {
00613 ActionBase *t = *ait;
00614 actions.append(t->clone());
00615 }
00616
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
00631 expressions.clear();
00632 }
00633
00634 void KScoringRule::cleanActions()
00635 {
00636
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 QString& 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 QString& l)
00659 {
00660 if (l == "OR") link = OR;
00661 else link = AND;
00662 }
00663
00664 void KScoringRule::setExpire(const QString& e)
00665 {
00666 if (e != "never") {
00667 QStringList l = QStringList::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 QString& group) const
00677 {
00678 for(GroupList::ConstIterator i=groups.begin(); i!=groups.end();++i) {
00679 QRegExp 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 QPtrListIterator<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
00698
00699
00700
00701 bool oper_and = (link == AND);
00702 bool res = true;
00703 QPtrListIterator<KScoringExpression> it(expressions);
00704
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 QString& g) const
00715 {
00716
00717 for (QStringList::ConstIterator i = groups.begin(); i != groups.end(); ++i) {
00718 if (QRegExp(*i).search(g) != -1) {
00719 applyRule(a);
00720 return;
00721 }
00722 }
00723 }
00724
00725 void KScoringRule::write(QTextStream& s) const
00726 {
00727 s << toString();
00728 }
00729
00730 QString KScoringRule::toString() const
00731 {
00732
00733 QString r;
00734 r += "<Rule name=\"" + toXml(name) + "\" linkmode=\"" + getLinkModeName();
00735 r += "\" expires=\"" + getExpireDateString() + "\">";
00736
00737 for(GroupList::ConstIterator i=groups.begin();i!=groups.end();++i) {
00738 r += "<Group name=\"" + toXml(*i) + "\" />";
00739 }
00740
00741 QPtrListIterator<KScoringExpression> eit(expressions);
00742 for (; eit.current(); ++eit) {
00743 r += eit.current()->toString();
00744 }
00745
00746 QPtrListIterator<ActionBase> ait(actions);
00747 for (; ait.current(); ++ait) {
00748 r += ait.current()->toString();
00749 }
00750 r += "</Rule>";
00751
00752 return r;
00753 }
00754
00755 QString 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 QString KScoringRule::getExpireDateString() const
00765 {
00766 if (expires.isNull()) return "never";
00767 else {
00768 return QString::number(expires.year()) + QString("-")
00769 + QString::number(expires.month()) + QString("-")
00770 + QString::number(expires.day());
00771 }
00772 }
00773
00774 bool KScoringRule::isExpired() const
00775 {
00776 return (expires.isValid() && (expires < QDate::currentDate()));
00777 }
00778
00779
00780
00781
00782 KScoringManager::KScoringManager(const QString& appName)
00783 : cacheValid(false)
00784 {
00785 allRules.setAutoDelete(true);
00786
00787 if(appName.isEmpty())
00788 mFilename = KGlobal::dirs()->saveLocation("appdata") + "/scorefile";
00789 else
00790 mFilename = KGlobal::dirs()->saveLocation("data") + "/" + appName + "/scorefile";
00791
00792 load();
00793 }
00794
00795
00796 KScoringManager::~KScoringManager()
00797 {
00798 }
00799
00800 void KScoringManager::load()
00801 {
00802 QDomDocument sdoc("Scorefile");
00803 QFile 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 QFile f( mFilename );
00823 if ( !f.open( IO_WriteOnly ) )
00824 return;
00825 QTextStream stream(&f);
00826 stream.setEncoding(QTextStream::Unicode);
00827 kdDebug(5100) << "KScoringManager::save() creating xml" << endl;
00828 createXMLfromInternal().save(stream,2);
00829 kdDebug(5100) << "KScoringManager::save() finished" << endl;
00830 }
00831
00832 QDomDocument KScoringManager::createXMLfromInternal()
00833 {
00834
00835
00836 QDomDocument sdoc("Scorefile");
00837 QString ss;
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 QString KScoringManager::toString() const
00847 {
00848 QString s;
00849 s += "<Scorefile>\n";
00850 QPtrListIterator<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(QDomNode n)
00868 {
00869 static KScoringRule *cR = 0;
00870
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 QDomNode::DocumentNode: {
00877
00878 break;
00879 }
00880 case QDomNode::ElementNode: {
00881
00882 QDomElement e = n.toElement();
00883
00884
00885 QString 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:
00910 ;
00911 }
00912 QDomNodeList nodelist = n.childNodes();
00913 unsigned cnt = nodelist.count();
00914
00915 for (unsigned i=0;i<cnt;++i)
00916 createInternalFromXML(nodelist.item(i));
00917 }
00918 }
00919
00920 KScoringRule* KScoringManager::addRule(const ScorableArticle& a, QString 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(QDate::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
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
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 QString& s)
00979 {
00980 bool cont = true;
00981 QString text = s;
00982 QString oldName = r->getName();
00983 while (cont) {
00984 cont = false;
00985 QPtrListIterator<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, QWidget *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 QString& group)
01065 {
01066 setGroup(group);
01067 applyRules(article);
01068 }
01069
01070 void KScoringManager::applyRules(ScorableArticle& a)
01071 {
01072 QPtrListIterator<KScoringRule> it(isCacheValid()? ruleList : allRules);
01073 for( ; it.current(); ++it) {
01074 it.current()->applyRule(a);
01075 }
01076 }
01077
01078 void KScoringManager::initCache(const QString& g)
01079 {
01080 group = g;
01081 ruleList.clear();
01082 QPtrListIterator<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 QString& g)
01094 {
01095 if (group != g) initCache(g);
01096 }
01097
01098 bool KScoringManager::hasRulesForCurrentGroup()
01099 {
01100 return ruleList.count() != 0;
01101 }
01102
01103
01104 QStringList KScoringManager::getRuleNames()
01105 {
01106 QStringList l;
01107 QPtrListIterator<KScoringRule> it(allRules);
01108 for( ; it.current(); ++it) {
01109 l << it.current()->getName();
01110 }
01111 return l;
01112 }
01113
01114 KScoringRule* KScoringManager::findRule(const QString& ruleName)
01115 {
01116 QPtrListIterator<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 QString KScoringManager::findUniqueName() const
01133 {
01134 int nr = 0;
01135 QString ret;
01136 bool duplicated=false;
01137
01138 while (nr < 99999999) {
01139 nr++;
01140 ret = i18n("rule %1").arg(nr);
01141
01142 duplicated=false;
01143 QPtrListIterator<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 QStringList KScoringManager::getDefaultHeaders() const
01170 {
01171 QStringList 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(QPtrList<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(QPtrList<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(QPtrList<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"