18 #undef QT_NO_ASCII_CAST
28 #include <KInputDialog>
29 #include <KStandardDirs>
33 #include <QDomElement>
34 #include <QDomDocument>
36 #include <QDomNodeList>
40 #include <QTextStream>
41 #include <QVBoxLayout>
50 static QString
toXml(
const QString &str )
53 uint len = tmp.length();
56 if ( tmp[(
int)i] ==
'<' ) {
57 tmp.replace( i, 1,
"<" );
60 }
else if ( tmp[(
int)i] ==
'"' ) {
61 tmp.replace( i, 1,
""" );
64 }
else if ( tmp[(
int)i] ==
'&' ) {
65 tmp.replace( i, 1,
"&" );
68 }
else if ( tmp[(
int)i] ==
'>' ) {
69 tmp.replace( i, 1,
">" );
82 NotifyDialog::NotesMap NotifyDialog::dict;
84 NotifyDialog::NotifyDialog(
QWidget *parent )
87 setCaption( i18n(
"Notify Message" ) );
89 setDefaultButton( Close );
94 QVBoxLayout *topL =
new QVBoxLayout( f );
96 note->setTextFormat( Qt::RichText );
97 topL->addWidget( note );
98 QCheckBox *check =
new QCheckBox( i18n(
"Do not show this message again" ), f );
99 check->setChecked(
true );
100 topL->addWidget( check );
101 connect( check, SIGNAL(toggled(
bool)), SLOT(slotShowAgainToggled(
bool)) );
104 void NotifyDialog::slotShowAgainToggled(
bool flag )
107 dict.insert( msg, !flag );
108 kDebug(5100) <<
"note \"" << note <<
"\" will popup again:" << flag;
113 kDebug(5100) <<
"displaying message";
119 NotesMap::Iterator i = dict.find( s );
120 if ( i == dict.end() || i.value() ) {
122 i18n(
"Article\n<b>%1</b><br /><b>%2</b><br />caused the following note to appear:<br />%3",
126 me->note->setText(msg);
127 if ( i == dict.end() ) {
129 i = dict.insert( s,
false );
154 kDebug(5100) <<
"new Action" <<
this;
159 kDebug(5100) <<
"delete Action" <<
this;
184 kWarning(5100) <<
"unknown type" << type <<
" in ActionBase::factory()";
193 return i18n(
"Adjust Score" );
195 return i18n(
"Display Message" );
197 return i18n(
"Colorize Header" );
199 return i18n(
"Mark as Read" );
201 kWarning(5100) <<
"unknown type" << type <<
" in ActionBase::userName()";
208 if ( name ==
"SETSCORE" ) {
210 }
else if ( name ==
"NOTIFY" ) {
212 }
else if ( name ==
"COLOR" ) {
214 }
else if ( name ==
"MARKASREAD" ) {
217 kWarning(5100) <<
"unknown type string" << name
218 <<
"in ActionBase::getTypeForName()";
234 kWarning(5100) <<
"unknown type string" << name
235 <<
"in ActionBase::getTypeForUserName()";
264 a +=
"<Action type=\"SETSCORE\" value=\"" + QString::number(val) +
"\" />";
301 a +=
"<Action type=\"COLOR\" value=\"" +
toXml(color.name()) +
"\" />";
329 return "<Action type=\"NOTIFY\" value=\"" +
toXml(note) +
"\" />";
356 return "<Action type=\"MARKASREAD\"/>";
372 notifyList.setAutoDelete(
true );
384 l = notifyList.find( note );
394 QString notifyCollection = i18n(
"<h1>List of collected notes</h1>" );
395 notifyCollection +=
"<p><ul>";
397 Q3DictIterator<article_list> it(notifyList);
398 for ( ; it.current(); ++it ) {
399 const QString ¬e = it.currentKey();
400 notifyCollection +=
"<li>" + note +
"<ul>";
402 article_list::Iterator ait;
403 for ( ait = alist->begin(); ait != alist->end(); ++ait ) {
404 notifyCollection +=
"<li><b>From: </b>" + (*ait).from +
"<br>";
405 notifyCollection +=
"<b>Subject: </b>" + (*ait).subject;
407 notifyCollection +=
"</ul>";
409 notifyCollection +=
"</ul>";
411 return notifyCollection;
418 dlg->setCaption( i18n(
"Collected Notes" ) );
419 dlg->setButtons( KDialog::Close );
420 dlg->setDefaultButton( KDialog::Close );
421 dlg->setModal(
false );
423 text->setReadOnly(
true );
425 dlg->setMainWidget( text );
426 dlg->setMinimumWidth( 300 );
427 dlg->setMinimumHeight( 300 );
433 const QString &n,
const QString &ng )
434 : header( h ), expr_str( n )
436 if ( t ==
"MATCH" ) {
438 expr.setPattern( expr_str );
439 expr.setCaseSensitivity( Qt::CaseInsensitive );
440 }
else if ( t ==
"MATCHCS" ) {
442 expr.setPattern( expr_str );
443 expr.setCaseSensitivity( Qt::CaseSensitive );
444 }
else if ( t ==
"CONTAINS" ) {
446 }
else if ( t ==
"EQUALS" ) {
448 }
else if ( t ==
"GREATER" ) {
450 expr_int = expr_str.toInt();
451 }
else if ( t ==
"SMALLER" ) {
453 expr_int = expr_str.toInt();
455 kDebug(5100) <<
"unknown match type in new expression";
460 kDebug(5100) <<
"new expr:" << header << t
480 kWarning(5100) <<
"unknown condition name" << s
481 <<
"in KScoringExpression::getConditionForName()";
491 return i18n(
"Contains Substring" );
493 return i18n(
"Matches Regular Expression" );
495 return i18n(
"Matches Regular Expression (Case Sensitive)" );
497 return i18n(
"Is Exactly the Same As" );
499 return i18n(
"Less Than" );
501 return i18n(
"Greater Than" );
503 kWarning(5100) <<
"unknown condition" << cond
504 <<
"in KScoringExpression::getNameForCondition()";
527 l.append(
"Message-ID" );
528 l.append(
"Subject" );
530 l.append(
"References" );
531 l.append(
"NNTP-Posting-Host" );
547 if ( header ==
"From" ) {
549 }
else if ( header ==
"Subject" ) {
555 if ( !head.isEmpty() ) {
558 res = ( head.toLower() == expr_str.toLower() );
561 res = ( head.toLower().indexOf( expr_str.toLower() ) >= 0 );
565 res = ( expr.indexIn( head ) != -1 );
568 res = ( head.toInt() > expr_int );
571 res = ( head.toInt() < expr_int );
574 kDebug(5100) <<
"unknown match";
581 return neg ? !res : res;
592 e +=
"<Expression neg=\"" + QString::number( neg ? 1 : 0 ) +
593 "\" header=\"" + header +
595 "\" expr=\"" +
toXml(expr_str) +
622 kWarning(5100) <<
"unknown cond" << cond
623 <<
" in KScoringExpression::getTypeString()";
635 : name( n ), link( AND )
637 expressions.setAutoDelete(
true );
638 actions.setAutoDelete(
true );
643 kDebug(5100) <<
"copying rule" << r.
getName();
645 expressions.setAutoDelete(
true );
646 actions.setAutoDelete(
true );
650 Q3PtrListIterator<KScoringExpression> it( rexpr );
651 for ( ; it.current(); ++it ) {
653 expressions.append( t );
658 Q3PtrListIterator<ActionBase> ait( ract );
659 for ( ; ait.current(); ++ait ) {
661 actions.append( t->
clone() );
689 kDebug(5100) <<
"KScoringRule::addExpression";
690 expressions.append(expr);
701 kDebug(5100) <<
"KScoringRule::addAction()" << a->
toString();
716 if ( e !=
"never" ) {
717 QStringList l = e.split(
'-', QString::SkipEmptyParts );
718 Q_ASSERT( l.count() == 3 );
719 expires.setYMD( l.at(0).toInt(), l.at(1).toInt(), l.at(2).toInt() );
726 for ( GroupList::ConstIterator i = groups.begin(); i != groups.end(); ++i ) {
728 if ( e.indexIn( group, 0 ) != -1 && e.matchedLength() == group.length() ) {
737 Q3PtrListIterator<ActionBase> it( actions );
738 for ( ; it.current(); ++it ) {
739 it.current()->apply( a );
745 bool oper_and = ( link ==
AND );
747 Q3PtrListIterator<KScoringExpression> it( expressions );
749 for ( ; it.current(); ++it ) {
750 Q_ASSERT( it.current() );
751 res = it.current()->match( a );
752 if ( !res && oper_and ) {
754 }
else if ( res && !oper_and ) {
766 for ( QStringList::ConstIterator i = groups.begin(); i != groups.end(); ++i ) {
767 if ( QRegExp( *i ).indexIn( g ) != -1 ) {
785 for ( GroupList::ConstIterator i = groups.begin(); i != groups.end(); ++i ) {
786 r +=
"<Group name=\"" +
toXml(*i) +
"\" />";
789 Q3PtrListIterator<KScoringExpression> eit(expressions);
790 for ( ; eit.current(); ++eit ) {
791 r += eit.current()->toString();
794 Q3PtrListIterator<ActionBase> ait(actions);
795 for ( ; ait.current(); ++ait ) {
796 r += ait.current()->toString();
816 if ( expires.isNull() ) {
820 QString::number( expires.year() ) + QString(
'-' ) +
821 QString::number( expires.month() ) + QString(
'-' ) +
822 QString::number( expires.day() );
830 ( expires < QDate::currentDate() );
835 : cacheValid( false )
837 allRules.setAutoDelete(
true );
839 if ( appName.isEmpty() ) {
840 mFilename = KGlobal::dirs()->saveLocation(
"appdata" ) +
"/scorefile";
842 mFilename = KGlobal::dirs()->saveLocation(
"data" ) +
'/' + appName +
"/scorefile";
854 QDomDocument sdoc(
"Scorefile" );
855 QFile f( mFilename );
856 if ( !f.open( QIODevice::ReadOnly ) ) {
859 if ( !sdoc.setContent( &f ) ) {
861 kDebug(5100) <<
"loading the scorefile failed";
865 kDebug(5100) <<
"loaded the scorefile, creating internal representation";
867 createInternalFromXML( sdoc );
869 kDebug(5100) <<
"ready, got" << allRules.count() <<
" rules";
874 kDebug(5100) <<
"KScoringManager::save() starts";
875 QFile f( mFilename );
876 if ( !f.open( QIODevice::WriteOnly ) ) {
879 QTextStream stream( &f );
880 stream.setCodec(
"UTF-8" );
881 kDebug(5100) <<
"KScoringManager::save() creating xml";
882 createXMLfromInternal().save( stream, 2 );
883 kDebug(5100) <<
"KScoringManager::save() finished";
886 QDomDocument KScoringManager::createXMLfromInternal()
890 QDomDocument sdoc(
"Scorefile" );
892 ss +=
"<?xml version = '1.0'?><!DOCTYPE Scorefile >";
894 ss +=
"</Scorefile>\n";
895 kDebug(5100) <<
"KScoringManager::createXMLfromInternal():" << endl << ss;
903 s +=
"<Scorefile>\n";
904 Q3PtrListIterator<KScoringRule> it( allRules );
905 for ( ; it.current(); ++it ) {
906 s += it.current()->toString();
911 void KScoringManager::expireRules()
913 for (
KScoringRule *cR = allRules.first(); cR; cR=allRules.next() ) {
914 if ( cR->isExpired() ) {
915 kDebug(5100) <<
"Rule" << cR->getName() <<
" is expired, deleting it";
921 void KScoringManager::createInternalFromXML( QDomNode n )
926 kDebug(5100) <<
"inspecting node of type" << n.nodeType()
927 <<
"named" << n.toElement().tagName();
929 switch ( n.nodeType() ) {
930 case QDomNode::DocumentNode:
935 case QDomNode::ElementNode:
938 QDomElement e = n.toElement();
939 QString s = e.tagName();
943 cR->
setExpire( e.attribute(
"expires" ) );
944 addRuleInternal( cR );
945 }
else if ( s ==
"Group" ) {
947 cR->
addGroup( e.attribute(
"name" ) );
948 }
else if ( s ==
"Expression" ) {
950 e.attribute(
"type" ),
951 e.attribute(
"expr" ),
952 e.attribute(
"neg" ) ) );
953 }
else if ( s ==
"Action" ) {
956 e.attribute(
"value" ) );
963 QDomNodeList nodelist = n.childNodes();
964 int cnt = nodelist.count();
965 for (
int i=0; i<cnt; ++i ) {
966 createInternalFromXML( nodelist.item( i ) );
972 const QString &group,
short score )
993 int i = allRules.findRef( expr );
996 addRuleInternal( expr );
1010 void KScoringManager::addRuleInternal(
KScoringRule *e )
1012 allRules.append( e ) ;
1015 kDebug(5100) <<
"KScoringManager::addRuleInternal" << e->
getName();
1021 int i = allRules.findRef( r );
1023 kDebug(5100) <<
"deleting rule" << r->
getName();
1026 kDebug(5100) <<
"rule" << r->
getName() <<
" not deleted";
1034 QString oldName = r->
getName();
1037 Q3PtrListIterator<KScoringRule> it( allRules );
1038 for ( ; it.current(); ++it ) {
1039 if ( it.current() != r && it.current()->
getName() ==
text ) {
1040 kDebug(5100) <<
"rule name" << text <<
" is not unique";
1041 text = KInputDialog::getText(
1042 i18n(
"Choose Another Rule Name" ),
1043 i18n(
"The rule name is already assigned, please choose another name:" ),
1050 if ( text != oldName ) {
1058 int i = allRules.findRef( r );
1075 int aindex = allRules.findRef( above );
1076 int bindex = allRules.findRef( below );
1077 if ( aindex <= 0 || bindex < 0 ) {
1080 if ( aindex < bindex ) {
1083 allRules.take( aindex );
1084 allRules.insert( bindex, above );
1089 int bindex = allRules.findRef( below );
1090 int aindex = allRules.findRef( above );
1091 if ( bindex < 0 || bindex >= (
int)allRules.count() - 1 || aindex < 0 ) {
1094 if ( bindex < aindex ) {
1097 allRules.take( bindex );
1098 allRules.insert( aindex + 1, below );
1103 kDebug(5100) <<
"emitting signal finishedEditing";
1112 addRuleInternal( rule );
1118 kWarning(5100) <<
"KScoringManager::applyRules(ScorableGroup* ) isn't implemented";
1129 Q3PtrListIterator<KScoringRule> it(
isCacheValid() ? ruleList : allRules );
1130 for ( ; it.current(); ++it ) {
1131 it.current()->applyRule( a );
1139 Q3PtrListIterator<KScoringRule> it(allRules);
1140 for ( ; it.current(); ++it ) {
1141 if ( it.current()->matchGroup( group ) ) {
1142 ruleList.append( it.current() );
1145 kDebug(5100) <<
"created cache for group" << group
1146 <<
"with" << ruleList.count() <<
"rules";
1159 return ruleList.count() != 0;
1165 Q3PtrListIterator<KScoringRule> it( allRules );
1166 for ( ; it.current(); ++it ) {
1167 l << it.current()->getName();
1174 Q3PtrListIterator<KScoringRule> it( allRules );
1175 for ( ; it.current(); ++it ) {
1176 if ( it.current()->getName() == ruleName ) {
1185 bool res = cacheValid;
1194 bool duplicated =
false;
1196 while ( nr < 99999999 ) {
1198 ret = i18n(
"rule %1", nr );
1201 Q3PtrListIterator<KScoringRule> it( allRules );
1202 for ( ; it.current(); ++it ) {
1203 if ( it.current()->getName() == ret ) {
1209 if ( !duplicated ) {
1236 l.append(
"Subject" );
1239 l.append(
"Message-ID" );
1245 stack.
push( allRules );
1250 stack.
pop( allRules );
1267 kDebug(5100) <<
"RuleStack::push pushing list with" << l.count() <<
" rules";
1269 for (
KScoringRule *r=l.first(); r != 0; r=l.next() ) {
1273 kDebug(5100) <<
"now there are" << stack.count() <<
" lists on the stack";
1280 kDebug(5100) <<
"RuleStack::pop pops list with" << l.count() <<
" rules";
1281 kDebug(5100) <<
"now there are" << stack.count() <<
" lists on the stack";
1293 kDebug(5100) <<
"drop: now there are" << stack.count() <<
" lists on the stack";
1297 #include "kscoring.moc"
virtual QString from() const =0
static int getTypeForName(const QString &name)
KScoringManager(const QString &appName=QString())
virtual void apply(ScorableArticle &) const
KScoringRule(const QString &name)
void setExpireDate(const QDate &d)
void setExpire(const QString &exp)
void top(Q3PtrList< KScoringRule > &)
like pop but without dropping the TOS
void setRule(KScoringRule *)
virtual bool hasFeature(int)
virtual bool canScores() const
virtual void setValue(const QString &s)
virtual ActionSetScore * clone() const
virtual void displayMessage(const QString &)
virtual QByteArray text(quint32 serialNumber) const =0
virtual ~KScoringManager()
void setLinkMode(const QString &link)
void moveRuleBelow(KScoringRule *below, KScoringRule *above)
void editRule(KScoringRule *e, QWidget *w=0)
QString getTypeString() const
virtual QString toString() const
void addNote(const ScorableArticle &, const QString &)
bool matchGroup(const QString &group) const
virtual void apply(ScorableArticle &article) const
ActionNotify(const QString &)
virtual ~ActionSetScore()
Q3PtrList< KScoringRule > ScoringRuleList
bool setCacheValid(bool v)
virtual bool canColors() const
void pop(Q3PtrList< KScoringRule > &)
clears the argument list and copy the content of the TOS into it after that the TOS gets dropped ...
void deleteRule(KScoringRule *)
virtual QString subject() const =0
virtual void apply(ScorableArticle &) const
void applyAction(ScorableArticle &a) const
virtual void markAsRead()
Base class for other Action classes.
virtual ActionBase * clone() const =0
QString findUniqueName() const
virtual void addScore(short)
void setGroup(const QString &g)
Q3PtrList< KScoringExpression > ScoreExprList
static void display(ScorableArticle &, const QString &)
ActionColor(const QColor &)
KScoringRule * findRule(const QString &)
virtual ActionNotify * clone() const
virtual void changeColor(const QColor &)
bool match(ScorableArticle &a) const
void moveRuleAbove(KScoringRule *above, KScoringRule *below)
void addExpression(KScoringExpression *)
virtual void apply(ScorableArticle &) const
static int getTypeForUserName(const QString &name)
QString collection() const
void addAction(int, const QString &)
QString getLinkModeName() const
static QString toXml(const QString &str)
void displayCollection(QWidget *p=0) const
void editorReady()
called from an editor whenever it finishes editing the rule base, causes the finishedEditing signal t...
virtual QString toString() const
void applyRules(ScorableArticle &article, const QString &group)
static QStringList conditionNames()
virtual bool canNotes() const
void setRuleName(KScoringRule *, const QString &)
void initCache(const QString &group)
static KScoringEditor * createEditor(KScoringManager *m, QWidget *parent=0)
The following classes ScorableArticle, ScorableGroup define the interface for the scoring...
void changedRuleName(const QString &oldName, const QString &newName)
virtual ActionColor * clone() const
QStringList getRuleNames()
virtual QString toString() const
virtual bool canMarkAsRead() const
virtual QString toString() const =0
virtual ~ScorableArticle()
static QString getNameForCondition(int)
void cancelNewRule(KScoringRule *)
static int getConditionForName(const QString &)
static QStringList userNames()
void write(QTextStream &) const
void addGroup(const QString &group)
virtual ActionMarkAsRead * clone() const
bool hasRulesForCurrentGroup()
void setName(const QString &n)
assert that the name is unique
KScoringRule * copyRule(KScoringRule *)
virtual QString getHeaderByType(const QString &) const =0
QString getExpireDateString() const
virtual QStringList getDefaultHeaders() const
returns a list of common (or available) headers defaults to returning { Subject, From, Message-ID, Date }
virtual QString toString() const
static ActionBase * factory(int type, const QString &value)
void write(QTextStream &) const
static QStringList headerNames()
void applyRule(ScorableArticle &a) const
void push(Q3PtrList< KScoringRule > &)
puts the list on the stack, doesn't change the list
KScoringExpression(const QString &, const QString &, const QString &, const QString &)
Q3PtrList< ActionBase > ActionList