From 7678f2402fdb01b03b359a2266f1e7427894d82a Mon Sep 17 00:00:00 2001 From: Janne Koschinski Date: Tue, 5 Sep 2017 17:18:57 +0200 Subject: [PATCH] Implement inverse highlight rules - Implements configuration, sync, and matching behavior for inverse rules - If a message matches a positive rule (or nick), and an inverse rule, the inverse rule takes precedence, and no highlight is generated - Implements an "inverse rule" checkbox in the core-side highlight rule configuration dialog. --- src/common/highlightrulemanager.cpp | 89 +++++++++++-------- src/common/highlightrulemanager.h | 15 ++-- src/core/corehighlightrulemanager.h | 4 +- .../corehighlightsettingspage.cpp | 20 ++++- .../settingspages/corehighlightsettingspage.h | 7 +- .../corehighlightsettingspage.ui | 5 ++ 6 files changed, 88 insertions(+), 52 deletions(-) diff --git a/src/common/highlightrulemanager.cpp b/src/common/highlightrulemanager.cpp index 98548bd1..7b24ed51 100644 --- a/src/common/highlightrulemanager.cpp +++ b/src/common/highlightrulemanager.cpp @@ -56,6 +56,7 @@ QVariantMap HighlightRuleManager::initHighlightRuleList() const QVariantList isRegEx; QVariantList isCaseSensitive; QVariantList isActive; + QVariantList isInverse; QStringList channel; for (int i = 0; i < _highlightRuleList.count(); i++) { @@ -63,6 +64,7 @@ QVariantMap HighlightRuleManager::initHighlightRuleList() const isRegEx << _highlightRuleList[i].isRegEx; isCaseSensitive << _highlightRuleList[i].isCaseSensitive; isActive << _highlightRuleList[i].isEnabled; + isInverse << _highlightRuleList[i].isInverse; channel << _highlightRuleList[i].chanName; } @@ -70,6 +72,7 @@ QVariantMap HighlightRuleManager::initHighlightRuleList() const highlightRuleListMap["isRegEx"] = isRegEx; highlightRuleListMap["isCaseSensitive"] = isCaseSensitive; highlightRuleListMap["isEnabled"] = isActive; + highlightRuleListMap["isInverse"] = isInverse; highlightRuleListMap["channel"] = channel; highlightRuleListMap["highlightNick"] = _highlightNick; highlightRuleListMap["nicksCaseSensitive"] = _nicksCaseSensitive; @@ -83,6 +86,7 @@ void HighlightRuleManager::initSetHighlightRuleList(const QVariantMap &highlight QVariantList isRegEx = highlightRuleList["isRegEx"].toList(); QVariantList isCaseSensitive = highlightRuleList["isCaseSensitive"].toList(); QVariantList isActive = highlightRuleList["isEnabled"].toList(); + QVariantList isInverse = highlightRuleList["isInverse"].toList(); QStringList channel = highlightRuleList["channel"].toStringList(); int count = name.count(); @@ -95,23 +99,23 @@ void HighlightRuleManager::initSetHighlightRuleList(const QVariantMap &highlight _highlightRuleList.clear(); for (int i = 0; i < name.count(); i++) { _highlightRuleList << HighlightRule(name[i], isRegEx[i].toBool(), isCaseSensitive[i].toBool(), - isActive[i].toBool(), channel[i]); + isActive[i].toBool(), isInverse[i].toBool(), channel[i]); } _highlightNick = HighlightNickType(highlightRuleList["highlightNick"].toInt()); _nicksCaseSensitive = highlightRuleList["nicksCaseSensitive"].toBool(); } void HighlightRuleManager::addHighlightRule(const QString &name, bool isRegEx, bool isCaseSensitive, bool isActive, - const QString &channel) + bool isInverse, const QString &channel) { if (contains(name)) { return; } - HighlightRule newItem = HighlightRule(name, isRegEx, isCaseSensitive, isActive, channel); + HighlightRule newItem = HighlightRule(name, isRegEx, isCaseSensitive, isActive, isInverse, channel); _highlightRuleList << newItem; - SYNC(ARG(name), ARG(isRegEx), ARG(isCaseSensitive), ARG(isActive), ARG(channel)) + SYNC(ARG(name), ARG(isRegEx), ARG(isCaseSensitive), ARG(isActive), ARG(isInverse), ARG(channel)) } @@ -121,6 +125,47 @@ bool HighlightRuleManager::_match(const QString &msgContents, const QString &msg return false; } + bool matches = false; + + for (int i = 0; i < _highlightRuleList.count(); i++) { + const HighlightRule &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(bufferName)) + continue; + } + else { + QRegExp rx(rule.chanName, Qt::CaseInsensitive); + if (!rx.exactMatch(bufferName)) + continue; + } + } + + QRegExp rx; + if (rule.isRegEx) { + rx = QRegExp(rule.name, rule.isCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); + } + else { + rx = QRegExp("(^|\\W)" + QRegExp::escape(rule.name) + "(\\W|$)", rule.isCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); + } + bool match = (rx.indexIn(stripFormatCodes(msgContents)) >= 0); + if (match) { + // If an inverse rule matches, then we know that we never want to return a highlight. + if (rule.isInverse) { + return false; + } else { + matches = true; + } + } + } + + if (matches) + return true; + if (!currentNick.isEmpty()) { QStringList nickList; if (_highlightNick == CurrentNick) { @@ -131,40 +176,10 @@ bool HighlightRuleManager::_match(const QString &msgContents, const QString &msg if (!nickList.contains(currentNick)) nickList.prepend(currentNick); } - foreach(QString nickname, nickList) { - QRegExp nickRegExp("(^|\\W)" + QRegExp::escape(nickname) + "(\\W|$)", _nicksCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); - if (nickRegExp.indexIn(stripFormatCodes(msgContents)) >= 0) { - return true; - } - } - for (int i = 0; i < _highlightRuleList.count(); i++) { - const HighlightRule &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(bufferName)) - continue; - } - else { - QRegExp rx(rule.chanName, Qt::CaseInsensitive); - if (!rx.exactMatch(bufferName)) - continue; - } - } - - QRegExp rx; - if (rule.isRegEx) { - rx = QRegExp(rule.name, rule.isCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); - } - else { - rx = QRegExp("(^|\\W)" + QRegExp::escape(rule.name) + "(\\W|$)", rule.isCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); - } - bool match = (rx.indexIn(stripFormatCodes(msgContents)) >= 0); - if (match) { + for(const QString &nickname : nickList) { + QRegExp nickRegExp("(^|\\W)" + QRegExp::escape(nickname) + "(\\W|$)", _nicksCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); + if (nickRegExp.indexIn(stripFormatCodes(msgContents)) >= 0) { return true; } } diff --git a/src/common/highlightrulemanager.h b/src/common/highlightrulemanager.h index 75834d15..b606a0a8 100644 --- a/src/common/highlightrulemanager.h +++ b/src/common/highlightrulemanager.h @@ -46,11 +46,13 @@ public: bool isRegEx = false; bool isCaseSensitive = false; bool isEnabled = true; + bool isInverse = false; QString chanName; HighlightRule() {} HighlightRule(const QString &name_, bool isRegEx_, bool isCaseSensitive_, - bool isEnabled_, const QString &chanName_) - : name(name_), isRegEx(isRegEx_), isCaseSensitive(isCaseSensitive_), isEnabled(isEnabled_), chanName(chanName_) { + bool isEnabled_, bool isInverse_, const QString &chanName_) + : name(name_), isRegEx(isRegEx_), isCaseSensitive(isCaseSensitive_), isEnabled(isEnabled_), + isInverse(isInverse_), chanName(chanName_) { } bool operator!=(const HighlightRule &other) { @@ -58,6 +60,7 @@ public: isRegEx != other.isRegEx || isCaseSensitive != other.isCaseSensitive || isEnabled != other.isEnabled || + isInverse != other.isInverse || chanName != other.chanName); } }; @@ -111,14 +114,14 @@ public slots: * @param chanName The channel in which the rule should apply */ virtual inline void requestAddHighlightRule(const QString &name, bool isRegEx, bool isCaseSensitive, bool isEnabled, - const QString &chanName) + bool isInverse, const QString &chanName) { - REQUEST(ARG(name), ARG(isRegEx), ARG(isCaseSensitive), ARG(isEnabled), ARG(chanName)) + REQUEST(ARG(name), ARG(isRegEx), ARG(isCaseSensitive), ARG(isEnabled), ARG(isInverse), ARG(chanName)) } virtual void addHighlightRule(const QString &name, bool isRegEx, bool isCaseSensitive, - bool isEnabled, const QString &chanName); + bool isEnabled, bool isInverse, const QString &chanName); virtual inline void requestSetHighlightNick(HighlightNickType highlightNick) { @@ -138,7 +141,7 @@ protected: bool _match(const QString &msgContents, const QString &msgSender, Message::Type msgType, Message::Flags msgFlags, const QString &bufferName, const QString ¤tNick, const QStringList identityNicks); signals: - void ruleAdded(QString name, bool isRegEx, bool isCaseSensitive, bool isEnabled, QString chanName); + void ruleAdded(QString name, bool isRegEx, bool isCaseSensitive, bool isEnabled, bool isInverse, QString chanName); private: HighlightRuleList _highlightRuleList; diff --git a/src/core/corehighlightrulemanager.h b/src/core/corehighlightrulemanager.h index c8fb4508..9cb1b634 100644 --- a/src/core/corehighlightrulemanager.h +++ b/src/core/corehighlightrulemanager.h @@ -41,9 +41,9 @@ public slots: virtual inline void requestToggleHighlightRule(const QString &highlightRule) { toggleHighlightRule(highlightRule); } virtual inline void requestRemoveHighlightRule(const QString &highlightRule) { removeHighlightRule(highlightRule); } virtual inline void requestAddHighlightRule(const QString &name, bool isRegEx, bool isCaseSensitive, - bool isEnabled, const QString &chanName) + bool isEnabled, bool isInverse, const QString &chanName) { - addHighlightRule(name, isRegEx, isCaseSensitive, isEnabled, chanName); + addHighlightRule(name, isRegEx, isCaseSensitive, isEnabled, isInverse, chanName); } diff --git a/src/qtui/settingspages/corehighlightsettingspage.cpp b/src/qtui/settingspages/corehighlightsettingspage.cpp index bad2400a..467991fb 100644 --- a/src/qtui/settingspages/corehighlightsettingspage.cpp +++ b/src/qtui/settingspages/corehighlightsettingspage.cpp @@ -53,6 +53,7 @@ CoreHighlightSettingsPage::CoreHighlightSettingsPage(QWidget *parent) ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents); ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents); ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents); + ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::InverseColumn, QHeaderView::ResizeToContents); ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents); #endif @@ -106,7 +107,7 @@ void CoreHighlightSettingsPage::defaults() } -void CoreHighlightSettingsPage::addNewRow(QString name, bool regex, bool cs, bool enable, QString chanName, bool self) +void CoreHighlightSettingsPage::addNewRow(QString name, bool regex, bool cs, bool enable, bool inverse, QString chanName, bool self) { ui.highlightTable->setRowCount(ui.highlightTable->rowCount()+1); @@ -133,6 +134,13 @@ void CoreHighlightSettingsPage::addNewRow(QString name, bool regex, bool cs, boo enableItem->setCheckState(Qt::Unchecked); enableItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable); + auto *inverseItem = new QTableWidgetItem(""); + if (inverse) + inverseItem->setCheckState(Qt::Checked); + else + inverseItem->setCheckState(Qt::Unchecked); + inverseItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable); + auto *chanNameItem = new QTableWidgetItem(chanName); int lastRow = ui.highlightTable->rowCount()-1; @@ -140,12 +148,13 @@ void CoreHighlightSettingsPage::addNewRow(QString name, bool regex, bool cs, boo ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem); ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem); ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem); + ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::InverseColumn, inverseItem); ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem); if (!self) ui.highlightTable->setCurrentItem(nameItem); - highlightList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, chanName); + highlightList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, inverse, chanName); } @@ -215,6 +224,9 @@ void CoreHighlightSettingsPage::tableChanged(QTableWidgetItem *item) case CoreHighlightSettingsPage::EnableColumn: highlightRule.isEnabled = (item->checkState() == Qt::Checked); break; + case CoreHighlightSettingsPage::InverseColumn: + highlightRule.isInverse = (item->checkState() == Qt::Checked); + break; case CoreHighlightSettingsPage::ChanColumn: if (!item->text().isEmpty() && item->text().trimmed().isEmpty()) item->setText(""); @@ -232,7 +244,7 @@ void CoreHighlightSettingsPage::load() auto ruleManager = Client::highlightRuleManager(); for (HighlightRuleManager::HighlightRule rule : ruleManager->highlightRuleList()) { - addNewRow(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, rule.chanName); + addNewRow(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, rule.isInverse, rule.chanName); } switch (ruleManager->highlightNick()) @@ -270,7 +282,7 @@ void CoreHighlightSettingsPage::save() clonedManager.clear(); for (const HighlightRuleManager::HighlightRule &rule : highlightList) { - clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isRegEx, rule.chanName); + clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, rule.isInverse, rule.chanName); } HighlightRuleManager::HighlightNickType highlightNickType = HighlightRuleManager::NoNick; diff --git a/src/qtui/settingspages/corehighlightsettingspage.h b/src/qtui/settingspages/corehighlightsettingspage.h index a411662e..915ea785 100644 --- a/src/qtui/settingspages/corehighlightsettingspage.h +++ b/src/qtui/settingspages/corehighlightsettingspage.h @@ -47,7 +47,7 @@ public slots: private slots: void widgetHasChanged(); - void addNewRow(QString name = tr("highlight rule"), bool regex = false, bool cs = false, bool enable = true, QString chanName = "", bool self = false); + void addNewRow(QString name = tr("highlight rule"), bool regex = false, bool cs = false, bool enable = true, bool inverse = false, QString chanName = "", bool self = false); void removeSelectedRows(); void selectRow(QTableWidgetItem *item); void tableChanged(QTableWidgetItem *item); @@ -60,8 +60,9 @@ private: RegExColumn = 1, CsColumn = 2, EnableColumn = 3, - ChanColumn = 4, - ColumnCount = 5 + InverseColumn = 4, + ChanColumn = 5, + ColumnCount = 6 }; void emptyTable(); diff --git a/src/qtui/settingspages/corehighlightsettingspage.ui b/src/qtui/settingspages/corehighlightsettingspage.ui index 42b60419..6e18ecbc 100644 --- a/src/qtui/settingspages/corehighlightsettingspage.ui +++ b/src/qtui/settingspages/corehighlightsettingspage.ui @@ -48,6 +48,11 @@ Enable + + + Inverse + + Channel -- 2.20.1