1 /***************************************************************************
2 * Copyright (C) 2005-2018 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 "qtuimessageprocessor.h"
24 #include "clientsettings.h"
26 #include "messagemodel.h"
29 QtUiMessageProcessor::QtUiMessageProcessor(QObject *parent)
30 : AbstractMessageProcessor(parent),
32 _processMode(TimerBased)
34 NotificationSettings notificationSettings;
35 _nicksCaseSensitive = notificationSettings.nicksCaseSensitive();
36 _nickMatcher.setCaseSensitive(_nicksCaseSensitive);
37 _highlightNick = notificationSettings.highlightNick();
38 _nickMatcher.setHighlightMode(
39 static_cast<NickHighlightMatcher::HighlightNickType>(_highlightNick));
40 highlightListChanged(notificationSettings.highlightList());
41 notificationSettings.notify("Highlights/NicksCaseSensitive", this, SLOT(nicksCaseSensitiveChanged(const QVariant &)));
42 notificationSettings.notify("Highlights/CustomList", this, SLOT(highlightListChanged(const QVariant &)));
43 notificationSettings.notify("Highlights/HighlightNick", this, SLOT(highlightNickChanged(const QVariant &)));
45 _processTimer.setInterval(0);
46 connect(&_processTimer, SIGNAL(timeout()), this, SLOT(processNextMessage()));
50 void QtUiMessageProcessor::reset()
52 if (processMode() == TimerBased) {
53 if (_processTimer.isActive()) _processTimer.stop();
55 _currentBatch.clear();
56 _processQueue.clear();
61 void QtUiMessageProcessor::process(Message &msg)
63 checkForHighlight(msg);
65 Client::messageModel()->insertMessage(msg);
69 void QtUiMessageProcessor::process(QList<Message> &msgs)
71 QList<Message>::iterator msgIter = msgs.begin();
72 QList<Message>::iterator msgIterEnd = msgs.end();
73 while (msgIter != msgIterEnd) {
74 checkForHighlight(*msgIter);
78 Client::messageModel()->insertMessages(msgs);
81 if (msgs.isEmpty()) return;
82 _processQueue.append(msgs);
88 void QtUiMessageProcessor::startProcessing()
90 if (processMode() == TimerBased) {
91 if (_currentBatch.isEmpty() && _processQueue.isEmpty())
94 if (!_processTimer.isActive())
95 _processTimer.start();
100 void QtUiMessageProcessor::processNextMessage()
102 if (_currentBatch.isEmpty()) {
103 if (_processQueue.isEmpty()) {
104 _processTimer.stop();
108 _currentBatch = _processQueue.takeFirst();
110 Message msg = _currentBatch.takeFirst();
115 void QtUiMessageProcessor::checkForHighlight(Message &msg)
117 if (!((msg.type() & (Message::Plain | Message::Notice | Message::Action)) && !(msg.flags() & Message::Self)))
120 // Cached per network
121 const NetworkId &netId = msg.bufferInfo().networkId();
122 const Network *net = Client::network(netId);
124 if (net && !net->myNick().isEmpty()) {
126 QString currentNick = net->myNick();
127 // Get identity nicks
128 QStringList identityNicks = {};
129 const Identity *myIdentity = Client::identity(net->identity());
131 identityNicks = myIdentity->nicks();
134 // Get buffer name, message contents
135 QString bufferName = msg.bufferInfo().bufferName();
136 QString msgContents = msg.contents();
137 bool matches = false;
139 for (int i = 0; i < _highlightRuleList.count(); i++) {
140 auto &rule = _highlightRuleList.at(i);
141 if (!rule.isEnabled())
144 // Skip if channel name doesn't match and channel rule is not empty
146 // Match succeeds if...
147 // Channel name matches a defined rule
148 // Defined rule is empty
149 // And take the inverse of the above
150 if (!rule.chanNameMatcher().match(bufferName, true)) {
151 // A channel name rule is specified and does NOT match the current buffer name, skip
156 // Check message according to specified rule, allowing empty rules to match
157 bool contentsMatch = rule.contentsMatcher().match(stripFormatCodes(msgContents), true);
159 // Support for sender matching can be added here
162 // Support for inverse rules can be added here
168 msg.setFlags(msg.flags() | Message::Highlight);
173 if (_highlightNick != HighlightNickType::NoNick && !currentNick.isEmpty()) {
174 // Nickname matching allowed and current nickname is known
175 // Run the nickname matcher on the unformatted string
176 if (_nickMatcher.match(stripFormatCodes(msgContents), netId, currentNick,
178 msg.setFlags(msg.flags() | Message::Highlight);
186 void QtUiMessageProcessor::nicksCaseSensitiveChanged(const QVariant &variant)
188 _nicksCaseSensitive = variant.toBool();
189 // Update nickname matcher, too
190 _nickMatcher.setCaseSensitive(_nicksCaseSensitive);
194 void QtUiMessageProcessor::highlightListChanged(const QVariant &variant)
196 QVariantList varList = variant.toList();
198 _highlightRuleList.clear();
199 QVariantList::const_iterator iter = varList.constBegin();
200 while (iter != varList.constEnd()) {
201 QVariantMap rule = iter->toMap();
202 _highlightRuleList << LegacyHighlightRule(rule["Name"].toString(),
203 rule["RegEx"].toBool(),
205 rule["Enable"].toBool(),
206 rule["Channel"].toString());
212 void QtUiMessageProcessor::highlightNickChanged(const QVariant &variant)
214 _highlightNick = (HighlightNickType)variant.toInt();
215 // Convert from QtUiMessageProcessor::HighlightNickType (which is from NotificationSettings) to
216 // NickHighlightMatcher::HighlightNickType
217 _nickMatcher.setHighlightMode(
218 static_cast<NickHighlightMatcher::HighlightNickType>(_highlightNick));
222 /**************************************************************************
223 * LegacyHighlightRule
224 *************************************************************************/
225 bool QtUiMessageProcessor::LegacyHighlightRule::operator!=(const LegacyHighlightRule &other) const
227 return (_contents != other._contents ||
228 _isRegEx != other._isRegEx ||
229 _isCaseSensitive != other._isCaseSensitive ||
230 _isEnabled != other._isEnabled ||
231 _chanName != other._chanName);
232 // Don't compare ExpressionMatch objects as they are created as needed from the above
236 void QtUiMessageProcessor::LegacyHighlightRule::determineExpressions() const
238 // Don't update if not needed
239 if (!_cacheInvalid) {
243 // Set up matching rules
244 // Message is either phrase or regex
245 ExpressionMatch::MatchMode contentsMode =
246 _isRegEx ? ExpressionMatch::MatchMode::MatchRegEx :
247 ExpressionMatch::MatchMode::MatchPhrase;
248 // Sender (when added) and channel are either multiple wildcard entries or regex
249 ExpressionMatch::MatchMode scopeMode =
250 _isRegEx ? ExpressionMatch::MatchMode::MatchRegEx :
251 ExpressionMatch::MatchMode::MatchMultiWildcard;
253 _contentsMatch = ExpressionMatch(_contents, contentsMode, _isCaseSensitive);
254 _chanNameMatch = ExpressionMatch(_chanName, scopeMode, _isCaseSensitive);
256 _cacheInvalid = false;