QMap<QString, bool> result;
foreach(IgnoreListItem item, ignoreList()) {
if (item.type == SenderIgnore && pureMatch(item, hostmask)
- && ((network.isEmpty() && channel.isEmpty()) || item.scope == GlobalScope || (item.scope == NetworkScope && scopeMatch(item.scopeRule, network))
- || (item.scope == ChannelScope && scopeMatch(item.scopeRule, channel)))) {
+ && ((network.isEmpty() && channel.isEmpty()) || item.scope == GlobalScope || (item.scope == NetworkScope && scopeMatch(network, item.scopeRule))
+ || (item.scope == ChannelScope && scopeMatch(channel, item.scopeRule)))) {
result[item.ignoreRule] = item.isActive;
// qDebug() << "matchingRulesForHostmask found: " << item.ignoreRule << "is active: " << item.isActive;
}
if (!rule.isEnabled)
continue;
- if (!rule.chanName.isEmpty() && !scopeMatch(rule.chanName, bufferName)) {
+ if (!rule.chanName.isEmpty()
+ && !scopeMatch(bufferName, rule.chanName, rule.isRegEx, rule.isCaseSensitive)) {
// A channel name rule is specified and does NOT match the current buffer name, skip
// this rule
continue;
if (rule.sender.isEmpty()) {
senderMatch = true;
} else {
- if (rule.isRegEx) {
- rx = QRegExp(rule.sender, rule.isCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
- } else {
- rx = QRegExp(rule.sender, Qt::CaseInsensitive, QRegExp::Wildcard);
- }
- senderMatch = rx.exactMatch(msgSender);
+ // A sender name rule is specified, match according to scope rules.
+ senderMatch = scopeMatch(msgSender, rule.sender, rule.isRegEx, rule.isCaseSensitive);
}
if (nameMatch && senderMatch) {
if (!item.isActive || item.type == CtcpIgnore)
continue;
if (item.scope == GlobalScope
- || (item.scope == NetworkScope && scopeMatch(item.scopeRule, network))
- || (item.scope == ChannelScope && scopeMatch(item.scopeRule, bufferName))) {
+ || (item.scope == NetworkScope && scopeMatch(network, item.scopeRule))
+ || (item.scope == ChannelScope && scopeMatch(bufferName, item.scopeRule))) {
QString str;
if (item.type == MessageIgnore)
str = msgContents;
foreach(IgnoreListItem item, _ignoreList) {
if (!item.isActive)
continue;
- if (item.scope == GlobalScope || (item.scope == NetworkScope && scopeMatch(item.scopeRule, network))) {
+ if (item.scope == GlobalScope || (item.scope == NetworkScope && scopeMatch(network, item.scopeRule))) {
QString sender_;
QStringList types = item.ignoreRule.split(QRegExp("\\s+"), QString::SkipEmptyParts);
}
-bool scopeMatch(const QString &scopeRule, const QString &string)
+bool scopeMatch(const QString &string, const QString &scopeRule, const bool &isRegEx,
+ const bool &isCaseSensitive)
{
+ // When isRegEx is false:
// A match happens when the string does NOT match ANY inverted rules and matches AT LEAST one
// normal rule, unless no normal rules exist (implicit wildcard match). This gives inverted
// rules higher priority regardless of ordering.
//
+ // When isRegEx is true:
+ // A match happens when the normal regular expression matches. If prefixed with '!', the match
+ // happens UNLESS the following regular expression matches.
+
// TODO: After switching to Qt 5, use of this should be split into two parts, one part that
// would generate compiled QRegularExpressions for match/inverted match, regenerating it on any
// rule changes, and another part that would check each message against these compiled rules.
- // Keep track if any matches are found
- bool matches = false;
- // Keep track if normal rules and inverted rules are found, allowing for implicit wildcard
- bool normalRuleFound = false, invertedRuleFound = false;
-
- // Split each scope rule by separator, ignoring empty parts
- foreach(QString rule, scopeRule.split(";", QString::SkipEmptyParts)) {
- // Trim whitespace from the start/end of the rule
- rule = rule.trimmed();
- // Ignore empty rules
- if (rule.isEmpty())
- continue;
+ // Cache case sensitivity
+ Qt::CaseSensitivity ruleExactCase = (isCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
+ if (isRegEx) {
+ // Regular expression tests
+ // -------
// Check if this is an inverted rule (starts with '!')
- if (rule.startsWith("!")) {
- // Inverted rule found
- invertedRuleFound = true;
-
+ if (scopeRule.startsWith("!")) {
// Take the reminder of the string
- QRegExp ruleRx(rule.mid(1), Qt::CaseInsensitive);
- ruleRx.setPatternSyntax(QRegExp::Wildcard);
- if (ruleRx.exactMatch(string)) {
- // Matches an inverted rule, full rule cannot match
- return false;
- }
+ QRegExp ruleRx(scopeRule.mid(1), ruleExactCase);
+ // Matching an inverted rule: matched (true) implies rule failure (false)
+ return !ruleRx.exactMatch(string);
} else {
- // Normal rule found
- normalRuleFound = true;
-
- QRegExp ruleRx(rule, Qt::CaseInsensitive);
- ruleRx.setPatternSyntax(QRegExp::Wildcard);
- if (ruleRx.exactMatch(string)) {
- // Matches a normal rule, full rule might match
- matches = true;
- // Continue checking in case other inverted rules negate this
+ QRegExp ruleRx(scopeRule, ruleExactCase);
+ // Matching a normal rule: matched (true) implies rule success (true)
+ return ruleRx.exactMatch(string);
+ }
+ } else {
+ // Wildcard expression tests
+ // -------
+ // Keep track if any matches are found
+ bool matches = false;
+ // Keep track if normal rules and inverted rules are found, allowing for implicit wildcard
+ bool normalRuleFound = false, invertedRuleFound = false;
+
+ // Split each scope rule by separator, ignoring empty parts
+ foreach(QString rule, scopeRule.split(";", QString::SkipEmptyParts)) {
+ // Trim whitespace from the start/end of the rule
+ rule = rule.trimmed();
+ // Ignore empty rules
+ if (rule.isEmpty())
+ continue;
+
+ // Check if this is an inverted rule (starts with '!')
+ if (rule.startsWith("!")) {
+ // Inverted rule found
+ invertedRuleFound = true;
+
+ // Take the reminder of the string
+ QRegExp ruleRx(rule.mid(1), ruleExactCase);
+ ruleRx.setPatternSyntax(QRegExp::Wildcard);
+ if (ruleRx.exactMatch(string)) {
+ // Matches an inverted rule, full rule cannot match
+ return false;
+ }
+ } else {
+ // Normal rule found
+ normalRuleFound = true;
+
+ QRegExp ruleRx(rule, ruleExactCase);
+ ruleRx.setPatternSyntax(QRegExp::Wildcard);
+ if (ruleRx.exactMatch(string)) {
+ // Matches a normal rule, full rule might match
+ matches = true;
+ // Continue checking in case other inverted rules negate this
+ }
}
}
+ // No inverted rules matched, okay to match normally
+ // Return true if...
+ // ...we found a normal match
+ // ...implicit wildcard: we had inverted rules (that didn't match) and no normal rules
+ return matches || (invertedRuleFound && !normalRuleFound);
}
- // No inverted rules matched, okay to match normally
- // Return true if...
- // ...we found a normal match
- // ...implicit wildcard: we had inverted rules (that didn't match) and no normal rules
- return matches || (invertedRuleFound && !normalRuleFound);
}
/** Check if a scope rule matches a string
*
+ * When isRegEx is false:
* Checks that the string does NOT match ANY inverted rules (prefixed by '!'), then checks that
* it matches AT LEAST one normal (non-inverted) rule.
*
* If only inverted rules are specified, it'll match so long as the string does not match any
* inverted rules (implicit wildcard).
*
- * @param scopeRule A ';'-separated list of wildcard expressions, prefix of '!' inverts subrule
- * @param string String to test, e.g. network/channel name
+ * When isRegEx is true:
+ * Checks that the string matches the entire scopeRule as a regular expression. If scopeRule starts
+ * with a '!', check that the string does NOT match the regular expression.
+ *
+ * @param string String to test, e.g. network/channel name
+ * @param scopeRule ';'-separated list of wildcard expressions, prefix of '!' inverts subrule
+ * @param isRegEx If true, treat entire scope rule as regular expression, not wildcards
+ * @param isCaseSensitive If true, treat as case-sensitive, else case-insensitive
* @return True if matches, otherwise false
*/
-bool scopeMatch(const QString &scopeRule, const QString &string);
+bool scopeMatch(const QString &string, const QString &scopeRule,
+ const bool &isRegEx = false, const bool &isCaseSensitive = false);
continue;
if (!rule.chanName.isEmpty()
- && !scopeMatch(rule.chanName, msg.bufferInfo().bufferName())) {
+ && !scopeMatch(msg.bufferInfo().bufferName(), rule.chanName,
+ rule.isRegExp, rule.caseSensitive)) {
// A channel name rule is specified and does NOT match the current buffer name, skip
// this rule
continue;
table->verticalHeader()->hide();
table->setShowGrid(false);
+ table->horizontalHeaderItem(CoreHighlightSettingsPage::EnableColumn)->setToolTip(
+ tr("Enable/disable this rule"));
+ table->horizontalHeaderItem(CoreHighlightSettingsPage::EnableColumn)->setWhatsThis(
+ table->horizontalHeaderItem(CoreHighlightSettingsPage::EnableColumn)->toolTip());
+
+ table->horizontalHeaderItem(CoreHighlightSettingsPage::NameColumn)->setToolTip(
+ tr("Phrase to match"));
+ table->horizontalHeaderItem(CoreHighlightSettingsPage::NameColumn)->setWhatsThis(
+ table->horizontalHeaderItem(CoreHighlightSettingsPage::NameColumn)->toolTip());
+
table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setToolTip(
- tr("<b>RegEx</b>: This option determines if the highlight rule should be "
- "interpreted as a <b>regular expression</b> or just as a keyword."));
+ tr("<b>RegEx</b>: This option determines if the highlight rule, <i>Sender</i>, and "
+ "<i>Channel</i> should be interpreted as <b>regular expressions</b> or just as "
+ "keywords."));
table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setWhatsThis(
table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->toolTip());
table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setToolTip(
- tr("<b>CS</b>: This option determines if the highlight rule should be interpreted "
- "<b>case sensitive</b>."));
+ tr("<b>CS</b>: This option determines if the highlight rule, <i>Sender</i>, and "
+ "<i>Channel</i> should be interpreted <b>case sensitive</b>."));
table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setWhatsThis(
table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->toolTip());
+ table->horizontalHeaderItem(CoreHighlightSettingsPage::SenderColumn)->setToolTip(
+ tr("<p><b>Sender</b>: Semicolon separated list of <i>nick!ident@host</i> names, "
+ "leave blank to match any nickname.</p>"
+ "<p><i>Example:</i><br />"
+ "<i>Alice!*; Bob!*@example.com; Carol*!*; !Caroline!*</i><br />"
+ "would match on <i>Alice</i>, <i>Bob</i> with hostmask <i>example.com</i>, and "
+ "any nickname starting with <i>Carol</i> except for <i>Caroline</i><br />"
+ "<p>If only inverted names are specified, it will match anything except for "
+ "what's specified (implicit wildcard).</p>"
+ "<p><i>Example:</i><br />"
+ "<i>!Announce*!*; !Wheatley!aperture@*</i><br />"
+ "would match anything except for <i>Wheatley</i> with ident <i>aperture</i> or "
+ "any nickname starting with <i>Announce</i></p>"));
+ table->horizontalHeaderItem(CoreHighlightSettingsPage::SenderColumn)->setWhatsThis(
+ table->horizontalHeaderItem(CoreHighlightSettingsPage::SenderColumn)->toolTip());
+
table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setToolTip(
- tr("<p><b>Channel</b>: Semicolon separated list of channel names.</p>"
+ tr("<p><b>Channel</b>: Semicolon separated list of channel names, leave blank to "
+ "match any name.</p>"
"<p><i>Example:</i><br />"
"<i>#quassel*; #foobar; !#quasseldroid</i><br />"
- "would match on #foobar and on any channel starting with <i>#quassel</i> except "
- "for <i>#quasseldroid</i><br />"
+ "would match on <i>#foobar</i> and any channel starting with <i>#quassel</i> "
+ "except for <i>#quasseldroid</i><br />"
"<p>If only inverted names are specified, it will match anything except for "
"what's specified (implicit wildcard).</p>"
"<p><i>Example:</i><br />"
"<i>!#quassel*; !#foobar</i><br />"
- "would match anything except for #foobar or any channel starting with "
+ "would match anything except for <i>#foobar</i> or any channel starting with "
"<i>#quassel</i></p>"));
table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setWhatsThis(
table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->toolTip());
enableItem->setCheckState(Qt::Unchecked);
enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
- auto *chanNameItem = new QTableWidgetItem(chanName);
-
auto *senderItem = new QTableWidgetItem(sender);
+ auto *chanNameItem = new QTableWidgetItem(chanName);
+
enableItem->setToolTip(tr("Enable/disable this rule"));
nameItem->setToolTip(tr("Phrase to match"));
regexItem->setToolTip(
- tr("<b>RegEx</b>: This option determines if the highlight rule should be "
- "interpreted as a <b>regular expression</b> or just as a keyword."));
+ tr("<b>RegEx</b>: This option determines if the highlight rule, <i>Sender</i>, and "
+ "<i>Channel</i> should be interpreted as <b>regular expressions</b> or just as "
+ "keywords."));
csItem->setToolTip(
- tr("<b>CS</b>: This option determines if the highlight rule should be interpreted "
- "<b>case sensitive</b>."));
+ tr("<b>CS</b>: This option determines if the highlight rule, <i>Sender</i>, and "
+ "<i>Channel</i> should be interpreted <b>case sensitive</b>."));
senderItem->setToolTip(
- tr("<b>Sender</b>: This option specifies which sender to match. Leave blank to "
- "match any nickname."));
+ tr("<p><b>Sender</b>: Semicolon separated list of <i>nick!ident@host</i> names, "
+ "leave blank to match any nickname.</p>"
+ "<p><i>Example:</i><br />"
+ "<i>Alice!*; Bob!*@example.com; Carol*!*; !Caroline!*</i><br />"
+ "would match on <i>Alice</i>, <i>Bob</i> with hostmask <i>example.com</i>, and "
+ "any nickname starting with <i>Carol</i> except for <i>Caroline</i><br />"
+ "<p>If only inverted names are specified, it will match anything except for "
+ "what's specified (implicit wildcard).</p>"
+ "<p><i>Example:</i><br />"
+ "<i>!Announce*!*; !Wheatley!aperture@*</i><br />"
+ "would match anything except for <i>Wheatley</i> with ident <i>aperture</i> or "
+ "any nickname starting with <i>Announce</i></p>"));
chanNameItem->setToolTip(
- tr("<p><b>Channel</b>: Semicolon separated list of channel names.</p>"
+ tr("<p><b>Channel</b>: Semicolon separated list of channel names, leave blank to "
+ "match any name.</p>"
"<p><i>Example:</i><br />"
"<i>#quassel*; #foobar; !#quasseldroid</i><br />"
- "would match on #foobar and on any channel starting with <i>#quassel</i> except "
- "for <i>#quasseldroid</i><br />"
+ "would match on <i>#foobar</i> and any channel starting with <i>#quassel</i> "
+ "except for <i>#quasseldroid</i><br />"
"<p>If only inverted names are specified, it will match anything except for "
"what's specified (implicit wildcard).</p>"
"<p><i>Example:</i><br />"
"<i>!#quassel*; !#foobar</i><br />"
- "would match anything except for #foobar or any channel starting with "
+ "would match anything except for <i>#foobar</i> or any channel starting with "
"<i>#quassel</i></p>"));
int lastRow = ui.highlightTable->rowCount() - 1;
tr("<p><b>Channel</b>: Semicolon separated list of channel names.</p>"
"<p><i>Example:</i><br />"
"<i>#quassel*; #foobar; !#quasseldroid</i><br />"
- "would match on #foobar and on any channel starting with <i>#quassel</i> except "
+ "would match on #foobar and any channel starting with <i>#quassel</i> except "
"for <i>#quasseldroid</i><br />"
"<p>If only inverted names are specified, it will match anything except for "
"what's specified (implicit wildcard).</p>"
ui.highlightTable->verticalHeader()->hide();
ui.highlightTable->setShowGrid(false);
+
+ ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::EnableColumn)->setToolTip(
+ tr("Enable/disable this rule"));
+ ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::EnableColumn)->setWhatsThis(
+ ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::EnableColumn)->toolTip());
+
+ ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::NameColumn)->setToolTip(
+ tr("Phrase to match"));
+ ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::NameColumn)->setWhatsThis(
+ ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::NameColumn)->toolTip());
+
ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::RegExColumn)->setToolTip(
- tr("<b>RegEx</b>: This option determines if the highlight rule should be "
- "interpreted as a <b>regular expression</b> or just as a keyword."));
+ tr("<b>RegEx</b>: This option determines if the highlight rule and <i>Channel</i> "
+ "should be interpreted as <b>regular expressions</b> or just as keywords."));
ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::RegExColumn)->setWhatsThis(
ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::RegExColumn)->toolTip());
ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::CsColumn)->setToolTip(
- tr("<b>CS</b>: This option determines if the highlight rule should be interpreted "
- "<b>case sensitive</b>."));
+ tr("<b>CS</b>: This option determines if the highlight rule and <i>Channel</i> "
+ "should be interpreted <b>case sensitive</b>."));
ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::CsColumn)->setWhatsThis(
ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::CsColumn)->toolTip());
ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::ChanColumn)->setToolTip(
- tr("<p><b>Channel</b>: Semicolon separated list of channel names.</p>"
+ tr("<p><b>Channel</b>: Semicolon separated list of channel names, leave blank to "
+ "match any name.</p>"
"<p><i>Example:</i><br />"
"<i>#quassel*; #foobar; !#quasseldroid</i><br />"
- "would match on #foobar and on any channel starting with <i>#quassel</i> except "
- "for <i>#quasseldroid</i><br />"
+ "would match on <i>#foobar</i> and any channel starting with <i>#quassel</i> "
+ "except for <i>#quasseldroid</i><br />"
"<p>If only inverted names are specified, it will match anything except for "
"what's specified (implicit wildcard).</p>"
"<p><i>Example:</i><br />"
"<i>!#quassel*; !#foobar</i><br />"
- "would match anything except for #foobar or any channel starting with "
+ "would match anything except for <i>#foobar</i> or any channel starting with "
"<i>#quassel</i></p>"));
ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::ChanColumn)->setWhatsThis(
ui.highlightTable->horizontalHeaderItem(HighlightSettingsPage::ChanColumn)->toolTip());
enableItem->setToolTip(tr("Enable/disable this rule"));
nameItem->setToolTip(tr("Phrase to match"));
regexItem->setToolTip(
- tr("<b>RegEx</b>: This option determines if the highlight rule should be "
- "interpreted as a <b>regular expression</b> or just as a keyword."));
+ tr("<b>RegEx</b>: This option determines if the highlight rule and <i>Channel</i> "
+ "should be interpreted as <b>regular expressions</b> or just as keywords."));
csItem->setToolTip(
- tr("<b>CS</b>: This option determines if the highlight rule should be interpreted "
- "<b>case sensitive</b>."));
+ tr("<b>CS</b>: This option determines if the highlight rule and <i>Channel</i> "
+ "should be interpreted <b>case sensitive</b>."));
chanNameItem->setToolTip(
- tr("<p><b>Channel</b>: Semicolon separated list of channel names.</p>"
+ tr("<p><b>Channel</b>: Semicolon separated list of channel names, leave blank to "
+ "match any name.</p>"
"<p><i>Example:</i><br />"
"<i>#quassel*; #foobar; !#quasseldroid</i><br />"
- "would match on #foobar and on any channel starting with <i>#quassel</i> except "
- "for <i>#quasseldroid</i><br />"
+ "would match on <i>#foobar</i> and any channel starting with <i>#quassel</i> "
+ "except for <i>#quasseldroid</i><br />"
"<p>If only inverted names are specified, it will match anything except for "
"what's specified (implicit wildcard).</p>"
"<p><i>Example:</i><br />"
"<i>!#quassel*; !#foobar</i><br />"
- "would match anything except for #foobar or any channel starting with "
+ "would match anything except for <i>#foobar</i> or any channel starting with "
"<i>#quassel</i></p>"));
int lastRow = ui.highlightTable->rowCount()-1;
<br />
<i>#quassel*; #foobar; !#quasseldroid</i>
<br />
-would match on #foobar and on any channel starting with <i>#quassel</i> except for <i>#quasseldroid</i>
+would match on <i>#foobar</i> and any channel starting with <i>#quassel</i> except for <i>#quasseldroid</i>
<br />
<p>If only inverted names are specified, it will match anything except for what's specified (implicit wildcard).</p>
<p><i>Example:</i>
<br />
<i>!#quassel*; !#foobar</i>
<br />
-would match anything except for #foobar or any channel starting with <i>#quassel</i></p></string>
+would match anything except for <i>#foobar</i> or any channel starting with <i>#quassel</i></p></string>
</property>
</widget>
</item>