Clean up the HighlightRuleManager a bit
[quassel.git] / src / common / highlightrulemanager.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include "highlightrulemanager.h"
22
23 #include <QDebug>
24
25 #include "util.h"
26
27 INIT_SYNCABLE_OBJECT(HighlightRuleManager)
28
29 HighlightRuleManager &HighlightRuleManager::operator=(const HighlightRuleManager &other)
30 {
31     if (this == &other)
32         return *this;
33
34     SyncableObject::operator=(other);
35     _highlightRuleList = other._highlightRuleList;
36     _nicksCaseSensitive = other._nicksCaseSensitive;
37     _highlightNick = other._highlightNick;
38     return *this;
39 }
40
41
42 int HighlightRuleManager::indexOf(int id) const
43 {
44     for (int i = 0; i < _highlightRuleList.count(); i++) {
45         if (_highlightRuleList[i].id == id)
46             return i;
47     }
48     return -1;
49 }
50
51
52 int HighlightRuleManager::nextId()
53 {
54     int max = 0;
55     for (int i = 0; i < _highlightRuleList.count(); i++) {
56         int id = _highlightRuleList[i].id;
57         if (id > max) {
58             max = id;
59         }
60     }
61     return max + 1;
62 }
63
64
65 QVariantMap HighlightRuleManager::initHighlightRuleList() const
66 {
67     QVariantList id;
68     QVariantMap highlightRuleListMap;
69     QStringList name;
70     QVariantList isRegEx;
71     QVariantList isCaseSensitive;
72     QVariantList isActive;
73     QVariantList isInverse;
74     QStringList sender;
75     QStringList channel;
76
77     for (int i = 0; i < _highlightRuleList.count(); i++) {
78         id << _highlightRuleList[i].id;
79         name << _highlightRuleList[i].name;
80         isRegEx << _highlightRuleList[i].isRegEx;
81         isCaseSensitive << _highlightRuleList[i].isCaseSensitive;
82         isActive << _highlightRuleList[i].isEnabled;
83         isInverse << _highlightRuleList[i].isInverse;
84         sender << _highlightRuleList[i].sender;
85         channel << _highlightRuleList[i].chanName;
86     }
87
88     highlightRuleListMap["id"] = id;
89     highlightRuleListMap["name"] = name;
90     highlightRuleListMap["isRegEx"] = isRegEx;
91     highlightRuleListMap["isCaseSensitive"] = isCaseSensitive;
92     highlightRuleListMap["isEnabled"] = isActive;
93     highlightRuleListMap["isInverse"] = isInverse;
94     highlightRuleListMap["sender"] = sender;
95     highlightRuleListMap["channel"] = channel;
96     return highlightRuleListMap;
97 }
98
99
100 void HighlightRuleManager::initSetHighlightRuleList(const QVariantMap &highlightRuleList)
101 {
102     QVariantList id = highlightRuleList["id"].toList();
103     QStringList name = highlightRuleList["name"].toStringList();
104     QVariantList isRegEx = highlightRuleList["isRegEx"].toList();
105     QVariantList isCaseSensitive = highlightRuleList["isCaseSensitive"].toList();
106     QVariantList isActive = highlightRuleList["isEnabled"].toList();
107     QVariantList isInverse = highlightRuleList["isInverse"].toList();
108     QStringList sender = highlightRuleList["sender"].toStringList();
109     QStringList channel = highlightRuleList["channel"].toStringList();
110
111     int count = id.count();
112     if (count != name.count() || count != isRegEx.count() || count != isCaseSensitive.count() ||
113         count != isActive.count() || count != isInverse.count() || count != sender.count() ||
114         count != channel.count()) {
115         qWarning() << "Corrupted HighlightRuleList settings! (Count mismatch)";
116         return;
117     }
118
119     _highlightRuleList.clear();
120     for (int i = 0; i < name.count(); i++) {
121         _highlightRuleList << HighlightRule(id[i].toInt(), name[i], isRegEx[i].toBool(), isCaseSensitive[i].toBool(),
122                                             isActive[i].toBool(), isInverse[i].toBool(), sender[i], channel[i]);
123     }
124 }
125
126
127 void HighlightRuleManager::addHighlightRule(int id, const QString &name, bool isRegEx, bool isCaseSensitive,
128                                             bool isActive, bool isInverse, const QString &sender,
129                                             const QString &channel)
130 {
131     if (contains(id)) {
132         return;
133     }
134
135     HighlightRule newItem = HighlightRule(id, name, isRegEx, isCaseSensitive, isActive, isInverse, sender, channel);
136     _highlightRuleList << newItem;
137
138     SYNC(ARG(id), ARG(name), ARG(isRegEx), ARG(isCaseSensitive), ARG(isActive), ARG(isInverse), ARG(sender),
139          ARG(channel))
140 }
141
142
143 bool HighlightRuleManager::match(const QString &msgContents,
144                                  const QString &msgSender,
145                                  Message::Type msgType,
146                                  Message::Flags msgFlags,
147                                  const QString &bufferName,
148                                  const QString &currentNick,
149                                  const QStringList identityNicks)
150 {
151     if (!((msgType & (Message::Plain | Message::Notice | Message::Action)) && !(msgFlags & Message::Self))) {
152        return false;
153     }
154
155     bool matches = false;
156
157     for (int i = 0; i < _highlightRuleList.count(); i++) {
158         const HighlightRule &rule = _highlightRuleList.at(i);
159         if (!rule.isEnabled)
160             continue;
161
162         if (!rule.chanName.isEmpty()
163                 && !scopeMatch(bufferName, rule.chanName, rule.isRegEx, rule.isCaseSensitive)) {
164             // A channel name rule is specified and does NOT match the current buffer name, skip
165             // this rule
166             continue;
167         }
168
169         QRegExp rx;
170         if (rule.isRegEx) {
171             rx = QRegExp(rule.name, rule.isCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
172         } else {
173             rx = QRegExp("(^|\\W)" + QRegExp::escape(rule.name) + "(\\W|$)", rule.isCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
174         }
175         bool nameMatch = (rx.indexIn(stripFormatCodes(msgContents)) >= 0);
176
177         bool senderMatch;
178         if (rule.sender.isEmpty()) {
179             senderMatch = true;
180         } else {
181             // A sender name rule is specified, match according to scope rules.
182             senderMatch = scopeMatch(msgSender, rule.sender, rule.isRegEx, rule.isCaseSensitive);
183         }
184
185         if (nameMatch && senderMatch) {
186             // If an inverse rule matches, then we know that we never want to return a highlight.
187             if (rule.isInverse) {
188                 return false;
189             } else {
190                 matches = true;
191             }
192         }
193     }
194
195     if (matches)
196         return true;
197
198     if (!currentNick.isEmpty()) {
199         QStringList nickList;
200         if (_highlightNick == CurrentNick) {
201             nickList << currentNick;
202         }
203         else if (_highlightNick == AllNicks) {
204             nickList = identityNicks;
205             if (!nickList.contains(currentNick))
206                 nickList.prepend(currentNick);
207         }
208
209         for(const QString &nickname : nickList) {
210             QRegExp nickRegExp("(^|\\W)" + QRegExp::escape(nickname) + "(\\W|$)", _nicksCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
211             if (nickRegExp.indexIn(stripFormatCodes(msgContents)) >= 0) {
212                 return true;
213             }
214         }
215     }
216
217     return false;
218 }
219
220
221 void HighlightRuleManager::removeHighlightRule(int highlightRule)
222 {
223     removeAt(indexOf(highlightRule));
224     SYNC(ARG(highlightRule))
225 }
226
227
228 void HighlightRuleManager::toggleHighlightRule(int highlightRule)
229 {
230     int idx = indexOf(highlightRule);
231     if (idx == -1)
232         return;
233     _highlightRuleList[idx].isEnabled = !_highlightRuleList[idx].isEnabled;
234     SYNC(ARG(highlightRule))
235 }
236
237
238 bool HighlightRuleManager::match(const Message &msg, const QString &currentNick, const QStringList &identityNicks)
239 {
240     return match(msg.contents(), msg.sender(), msg.type(), msg.flags(), msg.bufferInfo().bufferName(), currentNick, identityNicks);
241 }