From 9c5c731845881996f45b32ea6de12e0647f4634d Mon Sep 17 00:00:00 2001
From: Richard Maudsley <richard.maudsley@isode.com>
Date: Wed, 16 Jul 2014 13:37:25 +0100
Subject: Prevent nick highlight rule highlighting the entire message and
 remove default highlight colours

Test-Information:

Add a nick highlight rule. Verify that it is triggered correctly in MUCs and that only the nick text is highlighted. Added unit tests.

Change-Id: I9af1c900f4767383745afd36a5eadbe08f606432

diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index 24341e6..519deda 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -205,7 +205,7 @@ std::string ChatControllerBase::addMessage(const std::string& message, const std
 	if (boost::starts_with(message, "/me ")) {
 		return chatWindow_->addAction(chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight);
 	} else {
-		return chatWindow_->addMessage(chatMessageParser_->parseMessageBody(message,senderIsSelf), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight);
+		return chatWindow_->addMessage(chatMessageParser_->parseMessageBody(message,highlighter_->getNick(),senderIsSelf), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight);
 	}
 }
 
@@ -213,7 +213,7 @@ void ChatControllerBase::replaceMessage(const std::string& message, const std::s
 	if (boost::starts_with(message, "/me ")) {
 		chatWindow_->replaceWithAction(chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second), id, time, highlight);
 	} else {
-		chatWindow_->replaceMessage(chatMessageParser_->parseMessageBody(message,senderIsSelf), id, time, highlight);
+		chatWindow_->replaceMessage(chatMessageParser_->parseMessageBody(message,highlighter_->getNick(),senderIsSelf), id, time, highlight);
 	}
 }
 
diff --git a/Swift/Controllers/Chat/ChatMessageParser.cpp b/Swift/Controllers/Chat/ChatMessageParser.cpp
index 09d93ac..5a608db 100644
--- a/Swift/Controllers/Chat/ChatMessageParser.cpp
+++ b/Swift/Controllers/Chat/ChatMessageParser.cpp
@@ -26,7 +26,7 @@ namespace Swift {
 
 	typedef std::pair<std::string, std::string> StringPair;
 
-	ChatWindow::ChatMessage ChatMessageParser::parseMessageBody(const std::string& body, bool senderIsSelf) {
+	ChatWindow::ChatMessage ChatMessageParser::parseMessageBody(const std::string& body, const std::string& nick, bool senderIsSelf) {
 		ChatWindow::ChatMessage parsedMessage;
 		std::string remaining = body;
 		/* Parse one, URLs */
@@ -57,7 +57,7 @@ namespace Swift {
 
 		if (!senderIsSelf) { /* do not highlight our own messsages */
 			/* do word-based color highlighting */
-			parsedMessage = splitHighlight(parsedMessage);
+			parsedMessage = splitHighlight(parsedMessage, nick);
 		}
 
 		return parsedMessage;
@@ -138,7 +138,7 @@ namespace Swift {
 		return parsedMessage;
 	}
 
-	ChatWindow::ChatMessage ChatMessageParser::splitHighlight(const ChatWindow::ChatMessage& message)
+	ChatWindow::ChatMessage ChatMessageParser::splitHighlight(const ChatWindow::ChatMessage& message, const std::string& nick)
 	{
 		ChatWindow::ChatMessage parsedMessage = message;
 
@@ -149,7 +149,8 @@ namespace Swift {
 			} else if (rule.getMatchChat() && mucMode_) {
 				continue; /* this rule only applies to CHAT's, and this is a MUC */
 			}
-			foreach(const boost::regex &regex, rule.getKeywordRegex()) {
+			const std::vector<boost::regex> keywordRegex = rule.getKeywordRegex(nick);
+			foreach(const boost::regex& regex, keywordRegex) {
 				ChatWindow::ChatMessage newMessage;
 				foreach (boost::shared_ptr<ChatWindow::ChatMessagePart> part, parsedMessage.getParts()) {
 					boost::shared_ptr<ChatWindow::ChatTextMessagePart> textPart;
diff --git a/Swift/Controllers/Chat/ChatMessageParser.h b/Swift/Controllers/Chat/ChatMessageParser.h
index cff4ffa..2f5c171 100644
--- a/Swift/Controllers/Chat/ChatMessageParser.h
+++ b/Swift/Controllers/Chat/ChatMessageParser.h
@@ -15,10 +15,10 @@ namespace Swift {
 	class ChatMessageParser {
 		public:
 			ChatMessageParser(const std::map<std::string, std::string>& emoticons, HighlightRulesListPtr highlightRules, bool mucMode = false);
-			ChatWindow::ChatMessage parseMessageBody(const std::string& body, bool senderIsSelf = false);
+			ChatWindow::ChatMessage parseMessageBody(const std::string& body, const std::string& nick = "", bool senderIsSelf = false);
 		private:
 			ChatWindow::ChatMessage emoticonHighlight(const ChatWindow::ChatMessage& parsedMessage);
-			ChatWindow::ChatMessage splitHighlight(const ChatWindow::ChatMessage& parsedMessage);
+			ChatWindow::ChatMessage splitHighlight(const ChatWindow::ChatMessage& parsedMessage, const std::string& nick);
 			std::map<std::string, std::string> emoticons_;
 			HighlightRulesListPtr highlightRules_;
 			bool mucMode_;
diff --git a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
index 5dca63a..2a07654 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
@@ -85,6 +85,18 @@ public:
 		return list;
 	}
 
+	static HighlightRulesListPtr ruleListWithNickHighlight()
+	{
+		HighlightRule rule;
+		rule.setMatchChat(true);
+		rule.setNickIsKeyword(true);
+		rule.setMatchCase(true);
+		rule.setMatchWholeWords(true);
+		boost::shared_ptr<HighlightManager::HighlightRulesList> list = boost::make_shared<HighlightManager::HighlightRulesList>();
+		list->addRule(rule);
+		return list;
+	}
+
 	void testFullBody() {
 		const std::string no_special_message = "a message with no special content";
 		ChatMessageParser testling(emoticons_, boost::make_shared<HighlightManager::HighlightRulesList>());
@@ -174,6 +186,30 @@ public:
 		testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, true), ruleFromKeyword("three", false, true)));
 		result = testling.parseMessageBody("zeroonetwothree");
 		assertText(result, 0, "zeroonetwothree");
+
+		testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
+		result = testling.parseMessageBody("Alice", "Alice");
+		assertHighlight(result, 0, "Alice");
+
+		testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
+		result = testling.parseMessageBody("TextAliceText", "Alice");
+		assertText(result, 0, "TextAliceText");
+
+		testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
+		result = testling.parseMessageBody("Text Alice Text", "Alice");
+		assertText(result, 0, "Text ");
+		assertHighlight(result, 1, "Alice");
+		assertText(result, 2, " Text");
+
+		testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
+		result = testling.parseMessageBody("Alice Text", "Alice");
+		assertHighlight(result, 0, "Alice");
+		assertText(result, 1, " Text");
+
+		testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
+		result = testling.parseMessageBody("Text Alice", "Alice");
+		assertText(result, 0, "Text ");
+		assertHighlight(result, 1, "Alice");
 	}
 
 	void testOneEmoticon() {
diff --git a/Swift/Controllers/HighlightAction.cpp b/Swift/Controllers/HighlightAction.cpp
index d4d199d..492d4d2 100644
--- a/Swift/Controllers/HighlightAction.cpp
+++ b/Swift/Controllers/HighlightAction.cpp
@@ -8,7 +8,7 @@
 
 namespace Swift {
 
-void HighlightAction::setHighlightText(bool highlightText)
+void HighlightAction::setHighlightAllText(bool highlightText)
 {
 	highlightText_ = highlightText;
 	if (!highlightText_) {
diff --git a/Swift/Controllers/HighlightAction.h b/Swift/Controllers/HighlightAction.h
index de1f201..90768a7 100644
--- a/Swift/Controllers/HighlightAction.h
+++ b/Swift/Controllers/HighlightAction.h
@@ -25,17 +25,20 @@ namespace Swift {
 		public:
 			HighlightAction() : highlightText_(false), playSound_(false) {}
 
-			bool highlightText() const { return highlightText_; }
-			void setHighlightText(bool highlightText);
+			/**
+			* Gets the flag that indicates the entire message should be highlighted.
+			*/
+			bool highlightAllText() const { return highlightText_; }
+			void setHighlightAllText(bool highlightText);
 
 			/**
-			* Gets the foreground highlight color. If the string is empty, assume a default color.
+			* Gets the foreground highlight color.
 			*/
 			const std::string& getTextColor() const { return textColor_; }
 			void setTextColor(const std::string& textColor) { textColor_ = textColor; }
 
 			/**
-			* Gets the background highlight color. If the string is empty, assume a default color.
+			* Gets the background highlight color.
 			*/
 			const std::string& getTextBackground() const { return textBackground_; }
 			void setTextBackground(const std::string& textBackground) { textBackground_ = textBackground; }
diff --git a/Swift/Controllers/HighlightRule.cpp b/Swift/Controllers/HighlightRule.cpp
index f1a5235..3251067 100644
--- a/Swift/Controllers/HighlightRule.cpp
+++ b/Swift/Controllers/HighlightRule.cpp
@@ -102,6 +102,18 @@ void HighlightRule::setKeywords(const std::vector<std::string>& keywords)
 	updateRegex();
 }
 
+std::vector<boost::regex> HighlightRule::getKeywordRegex(const std::string& nick) const {
+	if (nickIsKeyword_) {
+		std::vector<boost::regex> regex;
+		if (!nick.empty()) {
+			regex.push_back(regexFromString(nick));
+		}
+		return regex;
+	} else {
+		return keywordRegex_;
+	}
+}
+
 void HighlightRule::setNickIsKeyword(bool nickIsKeyword)
 {
 	nickIsKeyword_ = nickIsKeyword;
diff --git a/Swift/Controllers/HighlightRule.h b/Swift/Controllers/HighlightRule.h
index ae1a3d3..c0226bc 100644
--- a/Swift/Controllers/HighlightRule.h
+++ b/Swift/Controllers/HighlightRule.h
@@ -40,7 +40,7 @@ namespace Swift {
 
 			const std::vector<std::string>& getKeywords() const { return keywords_; }
 			void setKeywords(const std::vector<std::string>&);
-			const std::vector<boost::regex>& getKeywordRegex() const { return keywordRegex_; }
+			std::vector<boost::regex> getKeywordRegex(const std::string& nick) const;
 
 			bool getNickIsKeyword() const { return nickIsKeyword_; }
 			void setNickIsKeyword(bool);
diff --git a/Swift/Controllers/Highlighter.h b/Swift/Controllers/Highlighter.h
index d026f29..d5d846b 100644
--- a/Swift/Controllers/Highlighter.h
+++ b/Swift/Controllers/Highlighter.h
@@ -22,6 +22,7 @@ namespace Swift {
 			void setMode(Mode mode);
 
 			void setNick(const std::string& nick) { nick_ = nick; }
+			std::string getNick() const { return nick_; }
 
 			HighlightAction findAction(const std::string& body, const std::string& sender) const;
 
diff --git a/Swift/QtUI/QtHighlightEditor.cpp b/Swift/QtUI/QtHighlightEditor.cpp
index ce07003..8488d7d 100644
--- a/Swift/QtUI/QtHighlightEditor.cpp
+++ b/Swift/QtUI/QtHighlightEditor.cpp
@@ -40,7 +40,6 @@ QtHighlightEditor::QtHighlightEditor(QtSettingsProvider* settings, QWidget* pare
 	connect(ui_.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(onOkButtonClick()));
 
 	connect(ui_.noColorRadio, SIGNAL(clicked()), SLOT(colorOtherSelect()));
-	connect(ui_.defaultColorRadio, SIGNAL(clicked()), SLOT(colorOtherSelect()));
 	connect(ui_.customColorRadio, SIGNAL(clicked()), SLOT(colorCustomSelect()));
 
 	connect(ui_.noSoundRadio, SIGNAL(clicked()), SLOT(soundOtherSelect()));
@@ -68,7 +67,6 @@ QtHighlightEditor::QtHighlightEditor(QtSettingsProvider* settings, QWidget* pare
 	connect(ui_.matchPartialWords, SIGNAL(clicked()), SLOT(widgetClick()));
 	connect(ui_.matchCase, SIGNAL(clicked()), SLOT(widgetClick()));
 	connect(ui_.noColorRadio, SIGNAL(clicked()), SLOT(widgetClick()));
-	connect(ui_.defaultColorRadio, SIGNAL(clicked()), SLOT(widgetClick()));
 	connect(ui_.customColorRadio, SIGNAL(clicked()), SLOT(widgetClick()));
 	connect(ui_.noSoundRadio, SIGNAL(clicked()), SLOT(widgetClick()));
 	connect(ui_.defaultSoundRadio, SIGNAL(clicked()), SLOT(widgetClick()));
@@ -305,7 +303,6 @@ void QtHighlightEditor::disableDialog()
 	ui_.matchPartialWords->setEnabled(false);
 	ui_.matchCase->setEnabled(false);
 	ui_.noColorRadio->setEnabled(false);
-	ui_.defaultColorRadio->setEnabled(false);
 	ui_.customColorRadio->setEnabled(false);
 	ui_.foregroundColor->setEnabled(false);
 	ui_.backgroundColor->setEnabled(false);
@@ -399,22 +396,21 @@ HighlightRule QtHighlightEditor::ruleFromDialog()
 		}
 	}
 
-	rule.setNickIsKeyword(ui_.nickIsKeyword->isChecked());
-	rule.setMatchWholeWords(!ui_.matchPartialWords->isChecked());
-	rule.setMatchCase(ui_.matchCase->isChecked());
+	if (ui_.nickIsKeyword->isChecked()) {
+		rule.setNickIsKeyword(true);
+		rule.setMatchWholeWords(true);
+		rule.setMatchCase(true);
+	} else {
+		rule.setMatchWholeWords(!ui_.matchPartialWords->isChecked());
+		rule.setMatchCase(ui_.matchCase->isChecked());
+	}
 
 	HighlightAction& action = rule.getAction();
 
 	if (ui_.noColorRadio->isChecked()) {
-		action.setHighlightText(false);
-		action.setTextColor("");
-		action.setTextBackground("");
-	} else if (ui_.defaultColorRadio->isChecked()) {
-		action.setHighlightText(true);
 		action.setTextColor("");
 		action.setTextBackground("");
 	} else {
-		action.setHighlightText(true);
 		action.setTextColor(Q2PSTRING(ui_.foregroundColor->getColor().name()));
 		action.setTextBackground(Q2PSTRING(ui_.backgroundColor->getColor().name()));
 	}
@@ -476,26 +472,19 @@ void QtHighlightEditor::ruleToDialog(const HighlightRule& rule)
 	const HighlightAction& action = rule.getAction();
 
 	ui_.noColorRadio->setEnabled(true);
-	ui_.defaultColorRadio->setEnabled(true);
 	ui_.customColorRadio->setEnabled(true);
-	if (action.highlightText()) {
-		if (action.getTextColor().empty() && action.getTextBackground().empty()) {
-			ui_.defaultColorRadio->setChecked(true);
-			ui_.foregroundColor->setEnabled(false);
-			ui_.backgroundColor->setEnabled(false);
-		} else {
-			ui_.foregroundColor->setEnabled(true);
-			ui_.backgroundColor->setEnabled(true);
-			QColor foregroundColor(P2QSTRING(action.getTextColor()));
-			ui_.foregroundColor->setColor(foregroundColor);
-			QColor backgroundColor(P2QSTRING(action.getTextBackground()));
-			ui_.backgroundColor->setColor(backgroundColor);
-			ui_.customColorRadio->setChecked(true);
-		}
-	} else {
+	if (action.getTextColor().empty() && action.getTextBackground().empty()) {
 		ui_.noColorRadio->setChecked(true);
 		ui_.foregroundColor->setEnabled(false);
 		ui_.backgroundColor->setEnabled(false);
+	} else {
+		ui_.foregroundColor->setEnabled(true);
+		ui_.backgroundColor->setEnabled(true);
+		QColor foregroundColor(P2QSTRING(action.getTextColor()));
+		ui_.foregroundColor->setColor(foregroundColor);
+		QColor backgroundColor(P2QSTRING(action.getTextBackground()));
+		ui_.backgroundColor->setColor(backgroundColor);
+		ui_.customColorRadio->setChecked(true);
 	}
 
 	ui_.noSoundRadio->setEnabled(true);
diff --git a/Swift/QtUI/QtHighlightEditor.ui b/Swift/QtUI/QtHighlightEditor.ui
index be2e99b..775771f 100644
--- a/Swift/QtUI/QtHighlightEditor.ui
+++ b/Swift/QtUI/QtHighlightEditor.ui
@@ -277,13 +277,6 @@
              </widget>
             </item>
             <item>
-             <widget class="QRadioButton" name="defaultColorRadio">
-              <property name="text">
-               <string>Default Color</string>
-              </property>
-             </widget>
-            </item>
-            <item>
              <widget class="QRadioButton" name="customColorRadio">
               <property name="text">
                <string>Custom Color</string>
diff --git a/Swift/QtUI/QtWebKitChatView.cpp b/Swift/QtUI/QtWebKitChatView.cpp
index 1486293..a510e34 100644
--- a/Swift/QtUI/QtWebKitChatView.cpp
+++ b/Swift/QtUI/QtWebKitChatView.cpp
@@ -608,8 +608,8 @@ std::string QtWebKitChatView::addMessage(
 
 	QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
 	QString styleSpanEnd = style == "" ? "" : "</span>";
-	QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : "";
-	QString highlightSpanEnd = highlight.highlightText() ? "</span>" : "";
+	QString highlightSpanStart = highlight.highlightAllText() ? getHighlightSpanStart(highlight) : "";
+	QString highlightSpanEnd = highlight.highlightAllText() ? "</span>" : "";
 	htmlString += "<span class='swift_inner_message'>" + styleSpanStart + highlightSpanStart + message + highlightSpanEnd + styleSpanEnd + "</span>" ;
 
 	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf);
@@ -826,8 +826,8 @@ void QtWebKitChatView::replaceMessage(const QString& message, const std::string&
 
 		QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
 		QString styleSpanEnd = style == "" ? "" : "</span>";
-		QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : "";
-		QString highlightSpanEnd = highlight.highlightText() ? "</span>" : "";
+		QString highlightSpanStart = highlight.highlightAllText() ? getHighlightSpanStart(highlight) : "";
+		QString highlightSpanEnd = highlight.highlightAllText() ? "</span>" : "";
 		messageHTML = styleSpanStart + highlightSpanStart + messageHTML + highlightSpanEnd + styleSpanEnd;
 
 		replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time));
-- 
cgit v0.10.2-6-g49f6