• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

libkdepim

kscoring.cpp

Go to the documentation of this file.
00001 /*
00002   kscoring.cpp
00003 
00004   Copyright (c) 2001 Mathias Waack <mathias@atoll-net.de>
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 "kscoring.h"
00024 #include "kscoringeditor.h"
00025 
00026 #include <KDebug>
00027 #include <KLocale>
00028 #include <KInputDialog>
00029 #include <KStandardDirs>
00030 #include <KTextEdit>
00031 
00032 #include <QCheckBox>
00033 #include <QDomElement>
00034 #include <QDomDocument>
00035 #include <QDomNode>
00036 #include <QDomNodeList>
00037 #include <QFile>
00038 #include <QLayout>
00039 #include <QLabel>
00040 #include <QTextStream>
00041 #include <QVBoxLayout>
00042 #include <Q3PtrList>
00043 
00044 #include <iostream>
00045 
00046 using namespace KPIM;
00047 
00048 //----------------------------------------------------------------------------
00049 // a small function to encode attribute values, code stolen from QDom
00050 static QString toXml( const QString &str )
00051 {
00052   QString tmp(str);
00053   uint len = tmp.length();
00054   uint i = 0;
00055   while ( i < len ) {
00056     if ( tmp[(int)i] == '<' ) {
00057       tmp.replace( i, 1, "&lt;" );
00058       len += 3;
00059       i += 4;
00060     } else if ( tmp[(int)i] == '"' ) {
00061       tmp.replace( i, 1, "&quot;" );
00062       len += 5;
00063       i += 6;
00064     } else if ( tmp[(int)i] == '&' ) {
00065        tmp.replace( i, 1, "&amp;" );
00066        len += 4;
00067        i += 5;
00068     } else if ( tmp[(int)i] == '>' ) {
00069        tmp.replace( i, 1, "&gt;" );
00070        len += 3;
00071        i += 4;
00072     } else {
00073        ++i;
00074     }
00075   }
00076 
00077   return tmp;
00078 }
00079 
00080 // small dialog to display the messages from NotifyAction
00081 NotifyDialog *NotifyDialog::me = 0;
00082 NotifyDialog::NotesMap NotifyDialog::dict;
00083 
00084 NotifyDialog::NotifyDialog( QWidget *parent )
00085   : KDialog( parent )
00086 {
00087   setCaption( i18n( "Notify Message" ) );
00088   setButtons( Close );
00089   setDefaultButton( Close );
00090   setModal( true );
00091 
00092   QFrame *f = new QFrame( this );
00093   setMainWidget ( f );
00094   QVBoxLayout *topL = new QVBoxLayout( f );
00095   note = new QLabel( f );
00096   note->setTextFormat( Qt::RichText );
00097   topL->addWidget( note );
00098   QCheckBox *check = new QCheckBox( i18n( "Do not show this message again" ), f );
00099   check->setChecked( true );
00100   topL->addWidget( check );
00101   connect( check, SIGNAL(toggled(bool)), SLOT(slotShowAgainToggled(bool)) );
00102 }
00103 
00104 void NotifyDialog::slotShowAgainToggled( bool flag )
00105 {
00106   dict.remove( msg );
00107   dict.insert( msg, !flag );
00108   kDebug(5100) <<"note \"" << note <<"\" will popup again:" << flag;
00109 }
00110 
00111 void NotifyDialog::display( ScorableArticle &a, const QString &s )
00112 {
00113   kDebug(5100) <<"displaying message";
00114   if ( !me ) {
00115     me = new NotifyDialog();
00116   }
00117   me->msg = s;
00118 
00119   NotesMap::Iterator i = dict.find( s );
00120   if ( i == dict.end() || i.value() ) {
00121     QString msg =
00122       i18n( "Article\n<b>%1</b><br /><b>%2</b><br />caused the following note to appear:<br />%3",
00123            a.from(),
00124            a.subject(),
00125            s );
00126     me->note->setText(msg);
00127     if ( i == dict.end() ) {
00128       dict.remove( s );
00129       i = dict.insert( s, false );
00130     }
00131     me->adjustSize();
00132     me->exec();
00133   }
00134 }
00135 
00136 //----------------------------------------------------------------------------
00137 ScorableArticle::~ScorableArticle()
00138 {
00139 }
00140 
00141 void ScorableArticle::displayMessage( const QString &note )
00142 {
00143   NotifyDialog::display( *this, note );
00144 }
00145 
00146 //----------------------------------------------------------------------------
00147 ScorableGroup::~ScorableGroup()
00148 {
00149 }
00150 
00151 // the base class for all actions
00152 ActionBase::ActionBase()
00153 {
00154   kDebug(5100) <<"new Action" << this;
00155 }
00156 
00157 ActionBase::~ActionBase()
00158 {
00159   kDebug(5100) <<"delete Action" << this;
00160 }
00161 
00162 QStringList ActionBase::userNames()
00163 {
00164   QStringList l;
00165   l << userName( SETSCORE );
00166   l << userName( NOTIFY );
00167   l << userName( COLOR );
00168   l << userName( MARKASREAD );
00169   return l;
00170 }
00171 
00172 ActionBase *ActionBase::factory( int type, const QString &value )
00173 {
00174   switch( type ) {
00175   case SETSCORE:
00176     return new ActionSetScore( value );
00177   case NOTIFY:
00178     return new ActionNotify( value );
00179   case COLOR:
00180     return new ActionColor( value );
00181   case MARKASREAD:
00182     return new ActionMarkAsRead();
00183   default:
00184     kWarning(5100) <<"unknown type" << type <<" in ActionBase::factory()";
00185     return 0;
00186   }
00187 }
00188 
00189 QString ActionBase::userName( int type )
00190 {
00191   switch( type ) {
00192   case SETSCORE:
00193     return i18n( "Adjust Score" );
00194   case NOTIFY:
00195     return i18n( "Display Message" );
00196   case COLOR:
00197     return i18n( "Colorize Header" );
00198   case MARKASREAD:
00199     return i18n( "Mark As Read" );
00200   default:
00201     kWarning(5100) <<"unknown type" << type <<" in ActionBase::userName()";
00202     return 0;
00203   }
00204 }
00205 
00206 int ActionBase::getTypeForName( const QString &name )
00207 {
00208   if ( name == "SETSCORE" ) {
00209     return SETSCORE;
00210   } else if ( name == "NOTIFY" ) {
00211     return NOTIFY;
00212   } else if ( name == "COLOR" ) {
00213     return COLOR;
00214   } else if ( name == "MARKASREAD" ) {
00215     return MARKASREAD;
00216   } else {
00217     kWarning(5100) <<"unknown type string" << name
00218                    << "in ActionBase::getTypeForName()";
00219     return -1;
00220   }
00221 }
00222 
00223 int ActionBase::getTypeForUserName( const QString &name )
00224 {
00225   if ( name == userName( SETSCORE ) ) {
00226     return SETSCORE;
00227   } else if ( name == userName( NOTIFY ) ) {
00228     return NOTIFY;
00229   } else if ( name == userName( COLOR ) ) {
00230     return COLOR;
00231   } else if ( name == userName( MARKASREAD ) ) {
00232     return MARKASREAD;
00233   } else {
00234     kWarning(5100) <<"unknown type string" << name
00235                    << "in ActionBase::getTypeForUserName()";
00236     return -1;
00237   }
00238 }
00239 
00240 // the set score action
00241 ActionSetScore::ActionSetScore( short v )
00242   : val( v )
00243 {
00244 }
00245 
00246 ActionSetScore::ActionSetScore( const QString &s )
00247 {
00248   val = s.toShort();
00249 }
00250 
00251 ActionSetScore::ActionSetScore( const ActionSetScore &as )
00252   : ActionBase(),
00253     val( as.val )
00254 {
00255 }
00256 
00257 ActionSetScore::~ActionSetScore()
00258 {
00259 }
00260 
00261 QString ActionSetScore::toString() const
00262 {
00263   QString a;
00264   a += "<Action type=\"SETSCORE\" value=\"" + QString::number(val) + "\" />";
00265   return a;
00266 }
00267 
00268 void ActionSetScore::apply( ScorableArticle &a ) const
00269 {
00270   a.addScore( val );
00271 }
00272 
00273 ActionSetScore *ActionSetScore::clone() const
00274 {
00275   return new ActionSetScore( *this );
00276 }
00277 
00278 // the color action
00279 ActionColor::ActionColor( const QColor &c )
00280   : ActionBase(), color( c )
00281 {
00282 }
00283 
00284 ActionColor::ActionColor( const QString &s )
00285   : ActionBase()
00286 {
00287   setValue(s);
00288 }
00289 
00290 ActionColor::ActionColor( const ActionColor &a )
00291   : ActionBase(), color( a.color )
00292 {
00293 }
00294 
00295 ActionColor::~ActionColor()
00296 {}
00297 
00298 QString ActionColor::toString() const
00299 {
00300   QString a;
00301   a += "<Action type=\"COLOR\" value=\"" + toXml(color.name()) + "\" />";
00302   return a;
00303 }
00304 
00305 void ActionColor::apply( ScorableArticle &a ) const
00306 {
00307   a.changeColor( color );
00308 }
00309 
00310 ActionColor *ActionColor::clone() const
00311 {
00312   return new ActionColor( *this );
00313 }
00314 
00315 // the notify action
00316 ActionNotify::ActionNotify( const QString &s )
00317 {
00318   note = s;
00319 }
00320 
00321 ActionNotify::ActionNotify( const ActionNotify &an )
00322   : ActionBase()
00323 {
00324   note = an.note;
00325 }
00326 
00327 QString ActionNotify::toString() const
00328 {
00329   return "<Action type=\"NOTIFY\" value=\"" + toXml(note) + "\" />";
00330 }
00331 
00332 void ActionNotify::apply( ScorableArticle &a ) const
00333 {
00334   a.displayMessage( note );
00335 }
00336 
00337 ActionNotify *ActionNotify::clone() const
00338 {
00339   return new ActionNotify( *this );
00340 }
00341 
00342 // mark as read action
00343 ActionMarkAsRead::ActionMarkAsRead() :
00344   ActionBase()
00345 {
00346 }
00347 
00348 ActionMarkAsRead::ActionMarkAsRead( const ActionMarkAsRead &action ) :
00349   ActionBase()
00350 {
00351   Q_UNUSED( action );
00352 }
00353 
00354 QString ActionMarkAsRead::toString() const
00355 {
00356   return "<Action type=\"MARKASREAD\"/>";
00357 }
00358 
00359 void ActionMarkAsRead::apply( ScorableArticle &article ) const
00360 {
00361   article.markAsRead();
00362 }
00363 
00364 ActionMarkAsRead *ActionMarkAsRead::clone() const
00365 {
00366   return new ActionMarkAsRead( *this );
00367 }
00368 
00369 //----------------------------------------------------------------------------
00370 NotifyCollection::NotifyCollection()
00371 {
00372   notifyList.setAutoDelete( true );
00373 }
00374 
00375 NotifyCollection::~NotifyCollection()
00376 {
00377 }
00378 
00379 void NotifyCollection::addNote( const ScorableArticle &a, const QString &note )
00380 {
00381   article_list *l = notifyList.find( note );
00382   if ( !l ) {
00383     notifyList.insert( note, new article_list );
00384     l = notifyList.find( note );
00385   }
00386   article_info i;
00387   i.from = a.from();
00388   i.subject = a.subject();
00389   l->append(i);
00390 }
00391 
00392 QString NotifyCollection::collection() const
00393 {
00394   QString notifyCollection = i18n( "<h1>List of collected notes</h1>" );
00395   notifyCollection += "<p><ul>";
00396   // first look thru the notes and create one string
00397   Q3DictIterator<article_list> it(notifyList);
00398   for ( ; it.current(); ++it ) {
00399     const QString &note = it.currentKey();
00400     notifyCollection += "<li>" + note + "<ul>";
00401     article_list *alist = it.current();
00402     article_list::Iterator ait;
00403     for ( ait = alist->begin(); ait != alist->end(); ++ait ) {
00404       notifyCollection += "<li><b>From: </b>" + (*ait).from + "<br>";
00405       notifyCollection += "<b>Subject: </b>" + (*ait).subject;
00406     }
00407     notifyCollection += "</ul>";
00408   }
00409   notifyCollection += "</ul>";
00410 
00411   return notifyCollection;
00412 }
00413 
00414 void NotifyCollection::displayCollection( QWidget *p ) const
00415 {
00416   //KMessageBox::information(p,collection(),i18n("Collected Notes"));
00417   KDialog *dlg = new KDialog( p );
00418   dlg->setCaption( i18n( "Collected Notes" ) );
00419   dlg->setButtons( KDialog::Close );
00420   dlg->setDefaultButton( KDialog::Close );
00421   dlg->setModal( false );
00422   KTextEdit *text = new KTextEdit( dlg );
00423   text->setReadOnly( true );
00424   text->setText( collection() );
00425   dlg->setMainWidget( text );
00426   dlg->setMinimumWidth( 300 );
00427   dlg->setMinimumHeight( 300 );
00428   dlg->show();
00429 }
00430 
00431 //----------------------------------------------------------------------------
00432 KScoringExpression::KScoringExpression( const QString &h, const QString &t,
00433                                         const QString &n, const QString &ng )
00434   : header( h ), expr_str( n )
00435 {
00436   if ( t == "MATCH" ) {
00437     cond = MATCH;
00438     expr.setPattern( expr_str );
00439     expr.setCaseSensitivity( Qt::CaseInsensitive );
00440   } else if ( t == "MATCHCS" ) {
00441     cond = MATCHCS;
00442     expr.setPattern( expr_str );
00443     expr.setCaseSensitivity( Qt::CaseSensitive );
00444   } else if ( t == "CONTAINS" ) {
00445     cond = CONTAINS;
00446   } else if ( t == "EQUALS" ) {
00447     cond = EQUALS;
00448   } else if ( t == "GREATER" ) {
00449     cond = GREATER;
00450     expr_int = expr_str.toInt();
00451   } else if ( t == "SMALLER" ) {
00452     cond = SMALLER;
00453     expr_int = expr_str.toInt();
00454   } else {
00455     kDebug(5100) <<"unknown match type in new expression";
00456   }
00457 
00458   neg = ng.toInt();
00459   c_header = header.toLatin1();
00460 
00461   kDebug(5100) <<"new expr:" << c_header << t
00462                << expr_str << neg;
00463 }
00464 
00465 // static
00466 int KScoringExpression::getConditionForName( const QString &s )
00467 {
00468   if ( s == getNameForCondition( CONTAINS ) ) {
00469     return CONTAINS;
00470   } else if ( s == getNameForCondition( MATCH ) ) {
00471     return MATCH;
00472   } else if ( s == getNameForCondition( MATCHCS ) ) {
00473     return MATCHCS;
00474   } else if ( s == getNameForCondition( EQUALS ) ) {
00475     return EQUALS;
00476   } else if ( s == getNameForCondition( SMALLER ) ) {
00477     return SMALLER;
00478   } else if ( s == getNameForCondition( GREATER ) ) {
00479     return GREATER;
00480   } else {
00481     kWarning(5100) <<"unknown condition name" << s
00482                    << "in KScoringExpression::getConditionForName()";
00483     return -1;
00484   }
00485 }
00486 
00487 // static
00488 QString KScoringExpression::getNameForCondition( int cond )
00489 {
00490   switch ( cond ) {
00491   case CONTAINS:
00492     return i18n( "Contains Substring" );
00493   case MATCH:
00494     return i18n( "Matches Regular Expression" );
00495   case MATCHCS:
00496     return i18n( "Matches Regular Expression (Case Sensitive)" );
00497   case EQUALS:
00498     return i18n( "Is Exactly the Same As" );
00499   case SMALLER:
00500     return i18n( "Less Than" );
00501   case GREATER:
00502     return i18n( "Greater Than" );
00503   default:
00504     kWarning(5100) <<"unknown condition" << cond
00505                    << "in KScoringExpression::getNameForCondition()";
00506     return "";
00507   }
00508 }
00509 
00510 // static
00511 QStringList KScoringExpression::conditionNames()
00512 {
00513   QStringList l;
00514   l << getNameForCondition( CONTAINS );
00515   l << getNameForCondition( MATCH );
00516   l << getNameForCondition( MATCHCS );
00517   l << getNameForCondition( EQUALS );
00518   l << getNameForCondition( SMALLER );
00519   l << getNameForCondition( GREATER );
00520   return l;
00521 }
00522 
00523 // static
00524 QStringList KScoringExpression::headerNames()
00525 {
00526   QStringList l;
00527   l.append( "From" );
00528   l.append( "Message-ID" );
00529   l.append( "Subject" );
00530   l.append( "Date" );
00531   l.append( "References" );
00532   l.append( "NNTP-Posting-Host" );
00533   l.append( "Bytes" );
00534   l.append( "Lines" );
00535   l.append( "Xref" );
00536   return l;
00537 }
00538 
00539 KScoringExpression::~KScoringExpression()
00540 {
00541 }
00542 
00543 bool KScoringExpression::match( ScorableArticle &a ) const
00544 {
00545   bool res = true;
00546   QString head;
00547 
00548   if ( header == "From" ) {
00549     head = a.from();
00550   } else if ( header == "Subject" ) {
00551     head = a.subject();
00552   } else {
00553     head = a.getHeaderByType(c_header);
00554   }
00555 
00556   if ( !head.isEmpty() ) {
00557     switch( cond ) {
00558     case EQUALS:
00559       res = ( head.toLower() == expr_str.toLower() );
00560       break;
00561     case CONTAINS:
00562       res = ( head.toLower().indexOf( expr_str.toLower() ) >= 0 );
00563       break;
00564     case MATCH:
00565     case MATCHCS:
00566       res = ( expr.indexIn( head ) != -1 );
00567       break;
00568     case GREATER:
00569       res = ( head.toInt() > expr_int );
00570       break;
00571     case SMALLER:
00572       res = ( head.toInt() < expr_int );
00573       break;
00574     default:
00575       kDebug(5100) <<"unknown match";
00576       res = false;
00577     }
00578   } else {
00579     res = false;
00580   }
00581 
00582   return neg ? !res : res;
00583 }
00584 
00585 void KScoringExpression::write( QTextStream &st ) const
00586 {
00587   st << toString();
00588 }
00589 
00590 QString KScoringExpression::toString() const
00591 {
00592   QString e;
00593   e += "<Expression neg=\"" + QString::number( neg ? 1 : 0 ) +
00594        "\" header=\"" + header +
00595        "\" type=\"" + getTypeString() +
00596        "\" expr=\"" + toXml(expr_str) +
00597        "\" />";
00598 
00599   return e;
00600 }
00601 
00602 QString KScoringExpression::getTypeString() const
00603 {
00604   return KScoringExpression::getTypeString(cond);
00605 }
00606 
00607 QString KScoringExpression::getTypeString( int cond )
00608 {
00609   switch( cond ) {
00610   case CONTAINS:
00611     return "CONTAINS";
00612   case MATCH:
00613     return "MATCH";
00614   case MATCHCS:
00615     return "MATCHCS";
00616   case EQUALS:
00617     return "EQUALS";
00618   case SMALLER:
00619     return "SMALLER";
00620   case GREATER:
00621     return "GREATER";
00622   default:
00623     kWarning(5100) <<"unknown cond" << cond
00624                    <<" in KScoringExpression::getTypeString()";
00625     return "";
00626   }
00627 }
00628 
00629 int  KScoringExpression::getType() const
00630 {
00631   return cond;
00632 }
00633 
00634 //----------------------------------------------------------------------------
00635 KScoringRule::KScoringRule( const QString &n )
00636   : name( n ), link( AND )
00637 {
00638   expressions.setAutoDelete( true );
00639   actions.setAutoDelete( true );
00640 }
00641 
00642 KScoringRule::KScoringRule( const KScoringRule &r )
00643 {
00644   kDebug(5100) <<"copying rule" << r.getName();
00645   name = r.getName();
00646   expressions.setAutoDelete( true );
00647   actions.setAutoDelete( true );
00648   // copy expressions
00649   expressions.clear();
00650   const ScoreExprList &rexpr = r.expressions;
00651   Q3PtrListIterator<KScoringExpression> it( rexpr );
00652   for ( ; it.current(); ++it ) {
00653     KScoringExpression *t = new KScoringExpression( **it );
00654     expressions.append( t );
00655   }
00656   // copy actions
00657   actions.clear();
00658   const ActionList &ract = r.actions;
00659   Q3PtrListIterator<ActionBase> ait( ract );
00660   for ( ; ait.current(); ++ait ) {
00661     ActionBase *t = *ait;
00662     actions.append( t->clone() );
00663   }
00664   // copy groups, servers, linkmode and expires
00665   groups = r.groups;
00666   expires = r.expires;
00667   link = r.link;
00668 }
00669 
00670 KScoringRule::~KScoringRule()
00671 {
00672   cleanExpressions();
00673   cleanActions();
00674 }
00675 
00676 void KScoringRule::cleanExpressions()
00677 {
00678   // the expressions is setAutoDelete(true)
00679   expressions.clear();
00680 }
00681 
00682 void KScoringRule::cleanActions()
00683 {
00684   // the actions is setAutoDelete(true)
00685   actions.clear();
00686 }
00687 
00688 void KScoringRule::addExpression( KScoringExpression *expr )
00689 {
00690   kDebug(5100) <<"KScoringRule::addExpression";
00691   expressions.append(expr);
00692 }
00693 
00694 void KScoringRule::addAction( int type, const QString &val )
00695 {
00696   ActionBase *action = ActionBase::factory( type, val );
00697   addAction( action );
00698 }
00699 
00700 void KScoringRule::addAction( ActionBase *a )
00701 {
00702   kDebug(5100) <<"KScoringRule::addAction()" << a->toString();
00703   actions.append(a);
00704 }
00705 
00706 void KScoringRule::setLinkMode( const QString &l )
00707 {
00708   if ( l == "OR" ) {
00709     link = OR;
00710   } else {
00711     link = AND;
00712   }
00713 }
00714 
00715 void KScoringRule::setExpire( const QString &e )
00716 {
00717   if ( e != "never" ) {
00718     QStringList l = e.split( '-', QString::SkipEmptyParts );
00719     Q_ASSERT( l.count() == 3 );
00720     expires.setYMD( l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt() );
00721   }
00722   kDebug(5100) <<"Rule" << getName() <<" expires at" << getExpireDateString();
00723 }
00724 
00725 bool KScoringRule::matchGroup( const QString &group ) const
00726 {
00727   for ( GroupList::ConstIterator i = groups.begin(); i != groups.end(); ++i ) {
00728     QRegExp e( *i );
00729     if ( e.indexIn( group, 0 ) != -1 && e.matchedLength() == group.length() ) {
00730       return true;
00731     }
00732   }
00733   return false;
00734 }
00735 
00736 void KScoringRule::applyAction( ScorableArticle &a ) const
00737 {
00738   Q3PtrListIterator<ActionBase> it( actions );
00739   for ( ; it.current(); ++it ) {
00740     it.current()->apply( a );
00741   }
00742 }
00743 
00744 void KScoringRule::applyRule( ScorableArticle &a ) const
00745 {
00746   bool oper_and = ( link == AND );
00747   bool res = true;
00748   Q3PtrListIterator<KScoringExpression> it( expressions );
00749 
00750   for ( ; it.current(); ++it ) {
00751     Q_ASSERT( it.current() );
00752     res = it.current()->match( a );
00753     if ( !res && oper_and ) {
00754       return;
00755     } else if ( res && !oper_and ) {
00756       break;
00757     }
00758   }
00759   if ( res ) {
00760     applyAction( a );
00761   }
00762 }
00763 
00764 void KScoringRule::applyRule( ScorableArticle &a, const QString &g ) const
00765 {
00766   // check if one of the groups match
00767   for ( QStringList::ConstIterator i = groups.begin(); i != groups.end(); ++i ) {
00768     if ( QRegExp( *i ).indexIn( g ) != -1 ) {
00769       applyRule( a );
00770       return;
00771     }
00772   }
00773 }
00774 
00775 void KScoringRule::write( QTextStream &s ) const
00776 {
00777   s << toString();
00778 }
00779 
00780 QString KScoringRule::toString() const
00781 {
00782   QString r;
00783   r += "<Rule name=\"" + toXml(name) + "\" linkmode=\"" + getLinkModeName();
00784   r += "\" expires=\"" + getExpireDateString() + "\">";
00785 
00786   for ( GroupList::ConstIterator i = groups.begin(); i != groups.end(); ++i ) {
00787     r += "<Group name=\"" + toXml(*i) + "\" />";
00788   }
00789 
00790   Q3PtrListIterator<KScoringExpression> eit(expressions);
00791   for ( ; eit.current(); ++eit ) {
00792     r += eit.current()->toString();
00793   }
00794 
00795   Q3PtrListIterator<ActionBase> ait(actions);
00796   for ( ; ait.current(); ++ait ) {
00797     r += ait.current()->toString();
00798   }
00799   r += "</Rule>";
00800   return r;
00801 }
00802 
00803 QString KScoringRule::getLinkModeName() const
00804 {
00805   switch( link ) {
00806   case AND:
00807     return "AND";
00808   case OR:
00809     return "OR";
00810   default:
00811     return "AND";
00812   }
00813 }
00814 
00815 QString KScoringRule::getExpireDateString() const
00816 {
00817   if ( expires.isNull() ) {
00818     return "never";
00819   } else {
00820     return
00821       QString::number( expires.year() ) + QString( '-' ) +
00822       QString::number( expires.month() ) + QString( '-' ) +
00823       QString::number( expires.day() );
00824   }
00825 }
00826 
00827 bool KScoringRule::isExpired() const
00828 {
00829   return
00830     expires.isValid() &&
00831     ( expires < QDate::currentDate() );
00832 }
00833 
00834 //----------------------------------------------------------------------------
00835 KScoringManager::KScoringManager( const QString &appName )
00836   :  cacheValid( false )
00837 {
00838   allRules.setAutoDelete( true );
00839   // determine filename of the scorefile
00840   if ( appName.isEmpty() ) {
00841     mFilename = KGlobal::dirs()->saveLocation( "appdata" ) + "/scorefile";
00842   } else {
00843     mFilename = KGlobal::dirs()->saveLocation( "data" ) + '/' + appName + "/scorefile";
00844   }
00845   // open the score file
00846   load();
00847 }
00848 
00849 KScoringManager::~KScoringManager()
00850 {
00851 }
00852 
00853 void KScoringManager::load()
00854 {
00855   QDomDocument sdoc( "Scorefile" );
00856   QFile f( mFilename );
00857   if ( !f.open( QIODevice::ReadOnly ) ) {
00858     return;
00859   }
00860   if ( !sdoc.setContent( &f ) ) {
00861     f.close();
00862     kDebug(5100) <<"loading the scorefile failed";
00863     return;
00864   }
00865   f.close();
00866   kDebug(5100) <<"loaded the scorefile, creating internal representation";
00867   allRules.clear();
00868   createInternalFromXML( sdoc );
00869   expireRules();
00870   kDebug(5100) <<"ready, got" << allRules.count() <<" rules";
00871 }
00872 
00873 void KScoringManager::save()
00874 {
00875   kDebug(5100) <<"KScoringManager::save() starts";
00876   QFile f( mFilename );
00877   if ( !f.open( QIODevice::WriteOnly ) ) {
00878     return;
00879   }
00880   QTextStream stream( &f );
00881   stream.setCodec( "UTF-8" );
00882   kDebug(5100) <<"KScoringManager::save() creating xml";
00883   createXMLfromInternal().save( stream, 2 );
00884   kDebug(5100) <<"KScoringManager::save() finished";
00885 }
00886 
00887 QDomDocument KScoringManager::createXMLfromInternal()
00888 {
00889   // I was'nt able to create a QDomDocument in memory:(
00890   // so I write the content into a string, which is really stupid
00891   QDomDocument sdoc( "Scorefile" );
00892   QString ss; // scorestring
00893   ss += "<?xml version = '1.0'?><!DOCTYPE Scorefile >";
00894   ss += toString();
00895   ss += "</Scorefile>\n";
00896   kDebug(5100) <<"KScoringManager::createXMLfromInternal():" << endl << ss;
00897   sdoc.setContent(ss);
00898   return sdoc;
00899 }
00900 
00901 QString KScoringManager::toString() const
00902 {
00903   QString s;
00904   s += "<Scorefile>\n";
00905   Q3PtrListIterator<KScoringRule> it( allRules );
00906   for ( ; it.current(); ++it ) {
00907     s += it.current()->toString();
00908   }
00909   return s;
00910 }
00911 
00912 void KScoringManager::expireRules()
00913 {
00914   for ( KScoringRule *cR = allRules.first(); cR; cR=allRules.next() ) {
00915     if ( cR->isExpired() ) {
00916       kDebug(5100) <<"Rule" << cR->getName() <<" is expired, deleting it";
00917       allRules.remove();
00918     }
00919   }
00920 }
00921 
00922 void KScoringManager::createInternalFromXML( QDomNode n )
00923 {
00924   static KScoringRule *cR = 0; // the currentRule
00925   // the XML file was parsed and now we simply traverse the resulting tree
00926   if ( !n.isNull() ) {
00927     kDebug(5100) <<"inspecting node of type" << n.nodeType()
00928                   << "named" << n.toElement().tagName();
00929 
00930     switch ( n.nodeType() ) {
00931     case QDomNode::DocumentNode:
00932     {
00933       // the document itself
00934       break;
00935     }
00936     case QDomNode::ElementNode:
00937     {
00938       // Server, Newsgroup, Rule, Expression, Action
00939       QDomElement e = n.toElement();
00940       QString s = e.tagName();
00941       if ( s == "Rule" ) {
00942         cR = new KScoringRule( e.attribute( "name" ) );
00943         cR->setLinkMode( e.attribute( "linkmode" ) );
00944         cR->setExpire( e.attribute( "expires" ) );
00945         addRuleInternal( cR );
00946       } else if ( s == "Group" ) {
00947         Q_CHECK_PTR( cR );
00948         cR->addGroup( e.attribute( "name" ) );
00949       } else if ( s == "Expression" ) {
00950         cR->addExpression( new KScoringExpression( e.attribute( "header" ),
00951                                                    e.attribute( "type" ),
00952                                                    e.attribute( "expr" ),
00953                                                    e.attribute( "neg" ) ) );
00954       } else if ( s == "Action" ) {