X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%2Fqtuimessageprocessor.cpp;h=bd745bbc417a63e5c0615d91f2c7ea562bfbfc26;hp=eee90aab002d88c805ef0509dae0556709388b13;hb=HEAD;hpb=f34bb7b60263683e8527b7b19cc5d1590390c4b1 diff --git a/src/qtui/qtuimessageprocessor.cpp b/src/qtui/qtuimessageprocessor.cpp index eee90aab..bd745bbc 100644 --- a/src/qtui/qtuimessageprocessor.cpp +++ b/src/qtui/qtuimessageprocessor.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2016 by the Quassel Project * + * Copyright (C) 2005-2022 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -26,44 +26,44 @@ #include "messagemodel.h" #include "network.h" -QtUiMessageProcessor::QtUiMessageProcessor(QObject *parent) - : AbstractMessageProcessor(parent), - _processing(false), - _processMode(TimerBased) +QtUiMessageProcessor::QtUiMessageProcessor(QObject* parent) + : AbstractMessageProcessor(parent) + , _processing(false) + , _processMode(TimerBased) { NotificationSettings notificationSettings; _nicksCaseSensitive = notificationSettings.nicksCaseSensitive(); + _nickMatcher.setCaseSensitive(_nicksCaseSensitive); _highlightNick = notificationSettings.highlightNick(); + _nickMatcher.setHighlightMode(static_cast(_highlightNick)); highlightListChanged(notificationSettings.highlightList()); - notificationSettings.notify("Highlights/NicksCaseSensitive", this, SLOT(nicksCaseSensitiveChanged(const QVariant &))); - notificationSettings.notify("Highlights/CustomList", this, SLOT(highlightListChanged(const QVariant &))); - notificationSettings.notify("Highlights/HighlightNick", this, SLOT(highlightNickChanged(const QVariant &))); + notificationSettings.notify("Highlights/NicksCaseSensitive", this, &QtUiMessageProcessor::nicksCaseSensitiveChanged); + notificationSettings.notify("Highlights/CustomList", this, &QtUiMessageProcessor::highlightListChanged); + notificationSettings.notify("Highlights/HighlightNick", this, &QtUiMessageProcessor::highlightNickChanged); _processTimer.setInterval(0); - connect(&_processTimer, SIGNAL(timeout()), this, SLOT(processNextMessage())); + connect(&_processTimer, &QTimer::timeout, this, &QtUiMessageProcessor::processNextMessage); } - void QtUiMessageProcessor::reset() { if (processMode() == TimerBased) { - if (_processTimer.isActive()) _processTimer.stop(); + if (_processTimer.isActive()) + _processTimer.stop(); _processing = false; _currentBatch.clear(); _processQueue.clear(); } } - -void QtUiMessageProcessor::process(Message &msg) +void QtUiMessageProcessor::process(Message& msg) { checkForHighlight(msg); preProcess(msg); Client::messageModel()->insertMessage(msg); } - -void QtUiMessageProcessor::process(QList &msgs) +void QtUiMessageProcessor::process(QList& msgs) { QList::iterator msgIter = msgs.begin(); QList::iterator msgIterEnd = msgs.end(); @@ -75,13 +75,13 @@ void QtUiMessageProcessor::process(QList &msgs) Client::messageModel()->insertMessages(msgs); return; - if (msgs.isEmpty()) return; + if (msgs.isEmpty()) + return; _processQueue.append(msgs); if (!isProcessing()) startProcessing(); } - void QtUiMessageProcessor::startProcessing() { if (processMode() == TimerBased) { @@ -93,7 +93,6 @@ void QtUiMessageProcessor::startProcessing() } } - void QtUiMessageProcessor::processNextMessage() { if (_currentBatch.isEmpty()) { @@ -108,61 +107,68 @@ void QtUiMessageProcessor::processNextMessage() process(msg); } - -void QtUiMessageProcessor::checkForHighlight(Message &msg) +void QtUiMessageProcessor::checkForHighlight(Message& msg) { if (!((msg.type() & (Message::Plain | Message::Notice | Message::Action)) && !(msg.flags() & Message::Self))) return; - // TODO: Cache this (per network) - const Network *net = Client::network(msg.bufferInfo().networkId()); + // Cached per network + const NetworkId& netId = msg.bufferInfo().networkId(); + const Network* net = Client::network(netId); + if (net && !net->myNick().isEmpty()) { - QStringList nickList; - if (_highlightNick == NotificationSettings::CurrentNick) { - nickList << net->myNick(); - } - else if (_highlightNick == NotificationSettings::AllNicks) { - const Identity *myIdentity = Client::identity(net->identity()); - if (myIdentity) - nickList = myIdentity->nicks(); - if (!nickList.contains(net->myNick())) - nickList.prepend(net->myNick()); - } - foreach(QString nickname, nickList) { - QRegExp nickRegExp("(^|\\W)" + QRegExp::escape(nickname) + "(\\W|$)", _nicksCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); - if (nickRegExp.indexIn(stripFormatCodes(msg.contents())) >= 0) { - msg.setFlags(msg.flags() | Message::Highlight); - return; - } + // Get current nick + QString currentNick = net->myNick(); + // Get identity nicks + QStringList identityNicks = {}; + const Identity* myIdentity = Client::identity(net->identity()); + if (myIdentity) { + identityNicks = myIdentity->nicks(); } - for (int i = 0; i < _highlightRules.count(); i++) { - const HighlightRule &rule = _highlightRules.at(i); - if (!rule.isEnabled) + // Get buffer name, message contents + QString bufferName = msg.bufferInfo().bufferName(); + QString msgContents = msg.contents(); + bool matches = false; + + for (int i = 0; i < _highlightRuleList.count(); i++) { + auto& rule = _highlightRuleList.at(i); + if (!rule.isEnabled()) continue; - if (rule.chanName.size() > 0 && rule.chanName.compare(".*") != 0) { - if (rule.chanName.startsWith("!")) { - QRegExp rx(rule.chanName.mid(1), Qt::CaseInsensitive); - if (rx.exactMatch(msg.bufferInfo().bufferName())) - continue; - } - else { - QRegExp rx(rule.chanName, Qt::CaseInsensitive); - if (!rx.exactMatch(msg.bufferInfo().bufferName())) - continue; - } + // Skip if channel name doesn't match and channel rule is not empty + // + // Match succeeds if... + // Channel name matches a defined rule + // Defined rule is empty + // And take the inverse of the above + if (!rule.chanNameMatcher().match(bufferName, true)) { + // A channel name rule is specified and does NOT match the current buffer name, skip + // this rule + continue; } - QRegExp rx; - if (rule.isRegExp) { - rx = QRegExp(rule.name, rule.caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); - } - else { - rx = QRegExp("(^|\\W)" + QRegExp::escape(rule.name) + "(\\W|$)", rule.caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); + // Check message according to specified rule, allowing empty rules to match + bool contentsMatch = rule.contentsMatcher().match(stripFormatCodes(msgContents), true); + + // Support for sender matching can be added here + + if (contentsMatch) { + // Support for inverse rules can be added here + matches = true; } - bool match = (rx.indexIn(stripFormatCodes(msg.contents())) >= 0); - if (match) { + } + + if (matches) { + msg.setFlags(msg.flags() | Message::Highlight); + return; + } + + // Check nicknames + if (_highlightNick != HighlightNickType::NoNick && !currentNick.isEmpty()) { + // Nickname matching allowed and current nickname is known + // Run the nickname matcher on the unformatted string + if (_nickMatcher.match(stripFormatCodes(msgContents), netId, currentNick, identityNicks)) { msg.setFlags(msg.flags() | Message::Highlight); return; } @@ -170,32 +176,69 @@ void QtUiMessageProcessor::checkForHighlight(Message &msg) } } - -void QtUiMessageProcessor::nicksCaseSensitiveChanged(const QVariant &variant) +void QtUiMessageProcessor::nicksCaseSensitiveChanged(const QVariant& variant) { _nicksCaseSensitive = variant.toBool(); + // Update nickname matcher, too + _nickMatcher.setCaseSensitive(_nicksCaseSensitive); } - -void QtUiMessageProcessor::highlightListChanged(const QVariant &variant) +void QtUiMessageProcessor::highlightListChanged(const QVariant& variant) { QVariantList varList = variant.toList(); - _highlightRules.clear(); + _highlightRuleList.clear(); QVariantList::const_iterator iter = varList.constBegin(); while (iter != varList.constEnd()) { QVariantMap rule = iter->toMap(); - _highlightRules << HighlightRule(rule["Name"].toString(), - rule["Enable"].toBool(), - rule["CS"].toBool() ? Qt::CaseSensitive : Qt::CaseInsensitive, - rule["RegEx"].toBool(), - rule["Channel"].toString()); + _highlightRuleList << LegacyHighlightRule(rule["Name"].toString(), + rule["RegEx"].toBool(), + rule["CS"].toBool(), + rule["Enable"].toBool(), + rule["Channel"].toString()); ++iter; } } +void QtUiMessageProcessor::highlightNickChanged(const QVariant& variant) +{ + _highlightNick = (HighlightNickType)variant.toInt(); + // Convert from QtUiMessageProcessor::HighlightNickType (which is from NotificationSettings) to + // NickHighlightMatcher::HighlightNickType + _nickMatcher.setHighlightMode(static_cast(_highlightNick)); +} -void QtUiMessageProcessor::highlightNickChanged(const QVariant &variant) +void QtUiMessageProcessor::networkRemoved(NetworkId id) { - _highlightNick = (NotificationSettings::HighlightNickType)variant.toInt(); + // Clean up nickname matching cache + _nickMatcher.removeNetwork(id); +} + +/************************************************************************** + * LegacyHighlightRule + *************************************************************************/ +bool QtUiMessageProcessor::LegacyHighlightRule::operator!=(const LegacyHighlightRule& other) const +{ + return (_contents != other._contents || _isRegEx != other._isRegEx || _isCaseSensitive != other._isCaseSensitive + || _isEnabled != other._isEnabled || _chanName != other._chanName); + // Don't compare ExpressionMatch objects as they are created as needed from the above +} + +void QtUiMessageProcessor::LegacyHighlightRule::determineExpressions() const +{ + // Don't update if not needed + if (!_cacheInvalid) { + return; + } + + // Set up matching rules + // Message is either phrase or regex + ExpressionMatch::MatchMode contentsMode = _isRegEx ? ExpressionMatch::MatchMode::MatchRegEx : ExpressionMatch::MatchMode::MatchPhrase; + // Sender (when added) and channel are either multiple wildcard entries or regex + ExpressionMatch::MatchMode scopeMode = _isRegEx ? ExpressionMatch::MatchMode::MatchRegEx : ExpressionMatch::MatchMode::MatchMultiWildcard; + + _contentsMatch = ExpressionMatch(_contents, contentsMode, _isCaseSensitive); + _chanNameMatch = ExpressionMatch(_chanName, scopeMode, _isCaseSensitive); + + _cacheInvalid = false; }