1 /***************************************************************************
2 * Copyright (C) 2005-2020 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include "highlightrulemanager.h"
25 #include "expressionmatch.h"
28 int HighlightRuleManager::indexOf(int id) const
30 for (int i = 0; i < _highlightRuleList.count(); i++) {
31 if (_highlightRuleList[i].id() == id)
37 int HighlightRuleManager::nextId()
40 for (int i = 0; i < _highlightRuleList.count(); i++) {
41 int id = _highlightRuleList[i].id();
49 QVariantMap HighlightRuleManager::initHighlightRuleList() const
52 QVariantMap highlightRuleListMap;
55 QVariantList isCaseSensitive;
56 QVariantList isActive;
57 QVariantList isInverse;
61 for (int i = 0; i < _highlightRuleList.count(); i++) {
62 id << _highlightRuleList[i].id();
63 name << _highlightRuleList[i].contents();
64 isRegEx << _highlightRuleList[i].isRegEx();
65 isCaseSensitive << _highlightRuleList[i].isCaseSensitive();
66 isActive << _highlightRuleList[i].isEnabled();
67 isInverse << _highlightRuleList[i].isInverse();
68 sender << _highlightRuleList[i].sender();
69 channel << _highlightRuleList[i].chanName();
72 highlightRuleListMap["id"] = id;
73 highlightRuleListMap["name"] = name;
74 highlightRuleListMap["isRegEx"] = isRegEx;
75 highlightRuleListMap["isCaseSensitive"] = isCaseSensitive;
76 highlightRuleListMap["isEnabled"] = isActive;
77 highlightRuleListMap["isInverse"] = isInverse;
78 highlightRuleListMap["sender"] = sender;
79 highlightRuleListMap["channel"] = channel;
80 return highlightRuleListMap;
83 void HighlightRuleManager::initSetHighlightRuleList(const QVariantMap& highlightRuleList)
85 QVariantList id = highlightRuleList["id"].toList();
86 QStringList name = highlightRuleList["name"].toStringList();
87 QVariantList isRegEx = highlightRuleList["isRegEx"].toList();
88 QVariantList isCaseSensitive = highlightRuleList["isCaseSensitive"].toList();
89 QVariantList isActive = highlightRuleList["isEnabled"].toList();
90 QVariantList isInverse = highlightRuleList["isInverse"].toList();
91 QStringList sender = highlightRuleList["sender"].toStringList();
92 QStringList channel = highlightRuleList["channel"].toStringList();
94 int count = id.count();
95 if (count != name.count() || count != isRegEx.count() || count != isCaseSensitive.count() || count != isActive.count()
96 || count != isInverse.count() || count != sender.count() || count != channel.count()) {
97 qWarning() << "Corrupted HighlightRuleList settings! (Count mismatch)";
101 _highlightRuleList.clear();
102 for (int i = 0; i < name.count(); i++) {
103 _highlightRuleList << HighlightRule(id[i].toInt(),
106 isCaseSensitive[i].toBool(),
107 isActive[i].toBool(),
108 isInverse[i].toBool(),
114 void HighlightRuleManager::addHighlightRule(
115 int id, const QString& name, bool isRegEx, bool isCaseSensitive, bool isActive, bool isInverse, const QString& sender, const QString& channel)
121 HighlightRule newItem = HighlightRule(id, name, isRegEx, isCaseSensitive, isActive, isInverse, sender, channel);
122 _highlightRuleList << newItem;
124 SYNC(ARG(id), ARG(name), ARG(isRegEx), ARG(isCaseSensitive), ARG(isActive), ARG(isInverse), ARG(sender), ARG(channel))
127 bool HighlightRuleManager::match(const NetworkId& netId,
128 const QString& msgContents,
129 const QString& msgSender,
130 Message::Type msgType,
131 Message::Flags msgFlags,
132 const QString& bufferName,
133 const QString& currentNick,
134 const QStringList& identityNicks)
136 if (!((msgType & (Message::Plain | Message::Notice | Message::Action)) && !(msgFlags & Message::Self))) {
140 bool matches = false;
142 for (int i = 0; i < _highlightRuleList.count(); i++) {
143 auto& rule = _highlightRuleList.at(i);
144 if (!rule.isEnabled())
147 // Skip if channel name doesn't match and channel rule is not empty
149 // Match succeeds if...
150 // Channel name matches a defined rule
151 // Defined rule is empty
152 // And take the inverse of the above
153 if (!rule.chanNameMatcher().match(bufferName, true)) {
154 // A channel name rule is specified and does NOT match the current buffer name, skip
159 // Check message according to specified rule, allowing empty rules to match
160 bool contentsMatch = rule.contentsMatcher().match(stripFormatCodes(msgContents), true);
162 // Check sender according to specified rule, allowing empty rules to match
163 bool senderMatch = rule.senderMatcher().match(msgSender, true);
165 if (contentsMatch && senderMatch) {
166 // If an inverse rule matches, then we know that we never want to return a highlight.
167 if (rule.isInverse()) {
180 if (_highlightNick != HighlightNickType::NoNick && !currentNick.isEmpty()) {
181 // Nickname matching allowed and current nickname is known
182 // Run the nickname matcher on the unformatted string
183 if (_nickMatcher.match(stripFormatCodes(msgContents), netId, currentNick, identityNicks)) {
191 void HighlightRuleManager::removeHighlightRule(int highlightRule)
193 removeAt(indexOf(highlightRule));
194 SYNC(ARG(highlightRule))
197 void HighlightRuleManager::toggleHighlightRule(int highlightRule)
199 int idx = indexOf(highlightRule);
202 _highlightRuleList[idx].setIsEnabled(!_highlightRuleList[idx].isEnabled());
203 SYNC(ARG(highlightRule))
206 bool HighlightRuleManager::match(const Message& msg, const QString& currentNick, const QStringList& identityNicks)
208 return match(msg.bufferInfo().networkId(),
213 msg.bufferInfo().bufferName(),
218 /**************************************************************************
220 *************************************************************************/
221 bool HighlightRuleManager::HighlightRule::operator!=(const HighlightRule& other) const
223 return (_id != other._id || _contents != other._contents || _isRegEx != other._isRegEx || _isCaseSensitive != other._isCaseSensitive
224 || _isEnabled != other._isEnabled || _isInverse != other._isInverse || _sender != other._sender || _chanName != other._chanName);
225 // Don't compare ExpressionMatch objects as they are created as needed from the above
228 void HighlightRuleManager::HighlightRule::determineExpressions() const
230 // Don't update if not needed
231 if (!_cacheInvalid) {
235 // Set up matching rules
236 // Message is either phrase or regex
237 ExpressionMatch::MatchMode contentsMode = _isRegEx ? ExpressionMatch::MatchMode::MatchRegEx : ExpressionMatch::MatchMode::MatchPhrase;
238 // Sender and channel are either multiple wildcard entries or regex
239 ExpressionMatch::MatchMode scopeMode = _isRegEx ? ExpressionMatch::MatchMode::MatchRegEx : ExpressionMatch::MatchMode::MatchMultiWildcard;
241 _contentsMatch = ExpressionMatch(_contents, contentsMode, _isCaseSensitive);
242 _senderMatch = ExpressionMatch(_sender, scopeMode, _isCaseSensitive);
243 _chanNameMatch = ExpressionMatch(_chanName, scopeMode, _isCaseSensitive);
245 _cacheInvalid = false;