libkdepim

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 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 // a small function to encode attribute values, code stolen from QDom
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, "&lt;");
00051       len += 3;
00052       i += 4;
00053     } else if (tmp[(int)i] == '"') {
00054       tmp.replace(i, 1, "&quot;");
00055       len += 5;
00056       i += 6;
00057     } else if (tmp[(int)i] == '&') {
00058        tmp.replace(i, 1, "&amp;");
00059        len += 4;
00060        i += 5;
00061     } else if (tmp[(int)i] == '>') {
00062        tmp.replace(i, 1, "&gt;");
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(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 // 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 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 // the set score action
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 // the color action
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 // the notify action
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 // 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 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   // first look thru the notes and create one string
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   //KMessageBox::information(p,collection(),i18n("Collected Notes"));
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 // static
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 // static
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 // static
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 // static
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   //kdDebug(5100) << "matching against header " << c_header << endl;
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 //  kdDebug(5100) << "matching returns " << res << endl;
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 //   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   QString e;
00552   e += "<Expression neg=\"" + QString::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 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   // copy expressions
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   // copy actions
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   // 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 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   // 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   QPtrListIterator<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 QString& s*/, const QString& g) const
00715 {
00716   // check if one of the groups match
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   //kdDebug(5100) << "KScoringRule::toString() starts" << endl;
00733   QString 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   QPtrListIterator<KScoringExpression> eit(expressions);
00742   for (; eit.current(); ++eit) {
00743     r += eit.current()->toString();
00744   }
00745   //kdDebug(5100) << "building actionlist..." << endl;
00746   QPtrListIterator<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 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)//, _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   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   // I was'nt able to create a QDomDocument in memory:(
00835   // so I write the content into a string, which is really stupid
00836   QDomDocument sdoc("Scorefile");
00837   QString 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 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; // 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 QDomNode::DocumentNode: {
00877       // the document itself
00878       break;
00879     }
00880     case QDomNode::ElementNode: {
00881       // Server, Newsgroup, Rule, Expression, Action
00882       QDomElement e = n.toElement();
00883       //kdDebug(5100) << "The name of the element is "
00884       //<< e.tagName().latin1() << endl;
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: // kdDebug(5100) << "unknown DomNode::type" << endl;
00910       ;
00911     }
00912     QDomNodeList 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, 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     // 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 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"
KDE Home | KDE Accessibility Home | Description of Access Keys