summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2017-01-31 14:57:22 (GMT)
committerEdwin Mons <edwin.mons@isode.com>2017-02-27 14:07:13 (GMT)
commitfc8f5b31c22ed7af4f0e2473f269601a87a0438c (patch)
tree0c59a9debf72247c7409947a3a4cccb6c616dd06 /Swift/Controllers/Chat
parentabd81d4a3cf08ffaa1e5265d204cdd80c8c0583b (diff)
downloadswift-fc8f5b31c22ed7af4f0e2473f269601a87a0438c.zip
swift-fc8f5b31c22ed7af4f0e2473f269601a87a0438c.tar.bz2
Redesign highlight logic and processing
The new highlight logic follows a simpler model. It supports: * highlighting of whole words in a message * highlighting messages by sender name * highlighting if the user’s name is mentioned Possible actions for these highlights are text colouring, sound playback of WAV files, and system notifications. In addition the user can decide to receive sound and system notification on general incoming direct and group messages. Redesigned the highlight configuration UI dialog for this new model. ChatMessageParser class now deals with all parsing and marking up the chat message with the matching HighlightActions. Highlighter class has been extended to deal with all sound and system notification highlights that should be emitted by a specified chat message. Moved some tests over to gtest in the process. Test-Information: Tested UI on macOS 10.12.3 with Qt 5.7.1. Manually tested that correct system notification are emitted on mentions, keyword highlights and general messages. Added new unit tests to cover new highlighting behaviour. Change-Id: I1c89e29d81022174187fb44af0d384036ec51594
Diffstat (limited to 'Swift/Controllers/Chat')
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp9
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp42
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.h24
-rw-r--r--Swift/Controllers/Chat/ChatMessageParser.cpp167
-rw-r--r--Swift/Controllers/Chat/ChatMessageParser.h30
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp8
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp13
-rw-r--r--Swift/Controllers/Chat/MUCController.h4
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp487
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp84
-rw-r--r--Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp10
11 files changed, 471 insertions, 407 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index a498067..d1cd1fe 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -28,7 +28,7 @@
#include <Swift/Controllers/Chat/ChatMessageParser.h>
#include <Swift/Controllers/FileTransfer/FileTransferController.h>
-#include <Swift/Controllers/Highlighter.h>
+#include <Swift/Controllers/Highlighting/Highlighter.h>
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/StatusUtil.h>
@@ -51,7 +51,7 @@ namespace Swift {
* The controller does not gain ownership of the stanzaChannel, nor the factory.
*/
ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider)
- : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider), userWantsReceipts_(userWantsReceipts), settings_(settings), clientBlockListManager_(clientBlockListManager) {
+ : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, nickResolver, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider), userWantsReceipts_(userWantsReceipts), settings_(settings), clientBlockListManager_(clientBlockListManager) {
isInMUC_ = isInMUC;
lastWasPresence_ = false;
chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider);
@@ -210,9 +210,10 @@ void ChatController::preHandleIncomingMessage(std::shared_ptr<MessageEvent> mess
}
void ChatController::postHandleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent, const ChatWindow::ChatMessage& chatMessage) {
+ highlighter_->handleSystemNotifications(chatMessage, messageEvent);
eventController_->handleIncomingEvent(messageEvent);
if (!messageEvent->getConcluded()) {
- handleHighlightActions(chatMessage);
+ highlighter_->handleSoundNotifications(chatMessage);
}
}
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index da9064e..5839d6c 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -26,8 +26,8 @@
#include <Swift/Controllers/Chat/AutoAcceptMUCInviteDecider.h>
#include <Swift/Controllers/Chat/ChatMessageParser.h>
-#include <Swift/Controllers/HighlightManager.h>
-#include <Swift/Controllers/Highlighter.h>
+#include <Swift/Controllers/Highlighting/HighlightManager.h>
+#include <Swift/Controllers/Highlighting/Highlighter.h>
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
@@ -38,13 +38,13 @@
namespace Swift {
-ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory), entityCapsProvider_(entityCapsProvider), historyController_(historyController), mucRegistry_(mucRegistry), chatMessageParser_(chatMessageParser), autoAcceptMUCInviteDecider_(autoAcceptMUCInviteDecider), eventStream_(eventStream) {
+ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory), entityCapsProvider_(entityCapsProvider), historyController_(historyController), mucRegistry_(mucRegistry), chatMessageParser_(chatMessageParser), autoAcceptMUCInviteDecider_(autoAcceptMUCInviteDecider), eventStream_(eventStream) {
chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream);
chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this));
chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2));
chatWindow_->onLogCleared.connect(boost::bind(&ChatControllerBase::handleLogCleared, this));
entityCapsProvider_->onCapsChanged.connect(boost::bind(&ChatControllerBase::handleCapsChanged, this, _1));
- highlighter_ = highlightManager->createHighlighter();
+ highlighter_ = highlightManager->createHighlighter(nickResolver);
ChatControllerBase::setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable());
createDayChangeTimer();
}
@@ -204,30 +204,12 @@ bool ChatControllerBase::hasOpenWindow() const {
return chatWindow_ && chatWindow_->isVisible();
}
-ChatWindow::ChatMessage ChatControllerBase::buildChatWindowChatMessage(const std::string& message, bool senderIsSelf, const HighlightAction& fullMessageHighlightAction) {
+ChatWindow::ChatMessage ChatControllerBase::buildChatWindowChatMessage(const std::string& message, const std::string& senderName, bool senderIsSelf) {
ChatWindow::ChatMessage chatMessage;
- chatMessage = chatMessageParser_->parseMessageBody(message, highlighter_->getNick(), senderIsSelf);
- chatMessage.setFullMessageHighlightAction(fullMessageHighlightAction);
+ chatMessage = chatMessageParser_->parseMessageBody(message, senderName, senderIsSelf);
return chatMessage;
}
-void ChatControllerBase::handleHighlightActions(const ChatWindow::ChatMessage& chatMessage) {
- std::set<std::string> playedSounds;
- if (chatMessage.getFullMessageHighlightAction().playSound()) {
- highlighter_->handleHighlightAction(chatMessage.getFullMessageHighlightAction());
- playedSounds.insert(chatMessage.getFullMessageHighlightAction().getSoundFile());
- }
- for (std::shared_ptr<ChatWindow::ChatMessagePart> part : chatMessage.getParts()) {
- std::shared_ptr<ChatWindow::ChatHighlightingMessagePart> highlightMessage = std::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(part);
- if (highlightMessage && highlightMessage->action.playSound()) {
- if (playedSounds.find(highlightMessage->action.getSoundFile()) == playedSounds.end()) {
- highlighter_->handleHighlightAction(highlightMessage->action);
- playedSounds.insert(highlightMessage->action.getSoundFile());
- }
- }
- }
-}
-
void ChatControllerBase::updateMessageCount() {
chatWindow_->setUnreadMessageCount(boost::numeric_cast<int>(unreadMessages_.size()));
onUnreadCountChanged();
@@ -314,12 +296,6 @@ void ChatControllerBase::handleIncomingMessage(std::shared_ptr<MessageEvent> mes
}
onActivity(body);
- // Highlight
- HighlightAction fullMessageHighlight;
- if (!isIncomingMessageFromMe(message)) {
- fullMessageHighlight = highlighter_->findFirstFullMessageMatchAction(body, senderHighlightNameFromMessage(from));
- }
-
std::shared_ptr<Replace> replace = message->getPayload<Replace>();
bool senderIsSelf = isIncomingMessageFromMe(message);
if (replace) {
@@ -327,12 +303,12 @@ void ChatControllerBase::handleIncomingMessage(std::shared_ptr<MessageEvent> mes
std::map<JID, std::string>::iterator lastMessage;
lastMessage = lastMessagesUIID_.find(from);
if (lastMessage != lastMessagesUIID_.end()) {
- chatMessage = buildChatWindowChatMessage(body, senderIsSelf, fullMessageHighlight);
+ chatMessage = buildChatWindowChatMessage(body, senderHighlightNameFromMessage(from), senderIsSelf);
replaceMessage(chatMessage, lastMessagesUIID_[from], timeStamp);
}
}
else {
- chatMessage = buildChatWindowChatMessage(body, senderIsSelf, fullMessageHighlight);
+ chatMessage = buildChatWindowChatMessage(body, senderHighlightNameFromMessage(from), senderIsSelf);
addMessageHandleIncomingMessage(from, chatMessage, senderIsSelf, label, timeStamp);
}
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index 4255c19..7f118bd 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -28,24 +28,25 @@
#include <Swiften/Presence/PresenceOracle.h>
#include <Swiften/Queries/IQRouter.h>
-#include <Swift/Controllers/HighlightManager.h>
+#include <Swift/Controllers/Highlighting/HighlightManager.h>
#include <Swift/Controllers/HistoryController.h>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h>
#include <Swift/Controllers/XMPPEvents/MessageEvent.h>
namespace Swift {
- class IQRouter;
- class StanzaChannel;
- class ChatWindowFactory;
+ class AutoAcceptMUCInviteDecider;
class AvatarManager;
- class UIEventStream;
- class EventController;
+ class ChatMessageParser;
+ class ChatWindowFactory;
class EntityCapsProvider;
+ class EventController;
class HighlightManager;
class Highlighter;
- class ChatMessageParser;
- class AutoAcceptMUCInviteDecider;
+ class IQRouter;
+ class NickResolver;
+ class StanzaChannel;
+ class UIEventStream;
class ChatControllerBase : public boost::signals2::trackable {
public:
@@ -73,7 +74,7 @@ namespace Swift {
boost::signals2::signal<void(ChatWindow* /*window to reuse*/, const std::vector<JID>& /*invite people*/, const std::string& /*reason*/)> onConvertToMUC;
protected:
- ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider);
+ ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, std::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider);
/**
* Pass the Message appended, and the stanza used to send it.
@@ -96,8 +97,7 @@ namespace Swift {
/** JID any iq for account should go to - bare except for PMs */
virtual JID getBaseJID();
virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) = 0;
- ChatWindow::ChatMessage buildChatWindowChatMessage(const std::string& message, bool senderIsSelf, const HighlightAction& fullMessageHighlightAction);
- void handleHighlightActions(const ChatWindow::ChatMessage& chatMessage);
+ ChatWindow::ChatMessage buildChatWindowChatMessage(const std::string& message, const std::string& senderName, bool senderIsSelf);
void updateMessageCount();
private:
diff --git a/Swift/Controllers/Chat/ChatMessageParser.cpp b/Swift/Controllers/Chat/ChatMessageParser.cpp
index ec7df6c..1a822a1 100644
--- a/Swift/Controllers/Chat/ChatMessageParser.cpp
+++ b/Swift/Controllers/Chat/ChatMessageParser.cpp
@@ -1,16 +1,18 @@
/*
- * Copyright (c) 2013-2016 Isode Limited.
+ * Copyright (c) 2013-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/Chat/ChatMessageParser.h>
+#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include <boost/algorithm/string.hpp>
+#include <boost/regex.hpp>
#include <Swiften/Base/Regex.h>
#include <Swiften/Base/String.h>
@@ -19,14 +21,14 @@
namespace Swift {
- ChatMessageParser::ChatMessageParser(const std::map<std::string, std::string>& emoticons, HighlightRulesListPtr highlightRules, bool mucMode)
- : emoticons_(emoticons), highlightRules_(highlightRules), mucMode_(mucMode) {
+ ChatMessageParser::ChatMessageParser(const std::map<std::string, std::string>& emoticons, std::shared_ptr<HighlightConfiguration> highlightConfiguration, Mode mode) : emoticons_(emoticons), highlightConfiguration_(highlightConfiguration), mode_(mode) {
}
typedef std::pair<std::string, std::string> StringPair;
- ChatWindow::ChatMessage ChatMessageParser::parseMessageBody(const std::string& body, const std::string& nick, bool senderIsSelf) {
+ ChatWindow::ChatMessage ChatMessageParser::parseMessageBody(const std::string& body, const std::string& senderNickname, bool senderIsSelf) {
ChatWindow::ChatMessage parsedMessage;
+
std::string remaining = body;
if (boost::starts_with(body, "/me ")) {
remaining = String::getSplittedAtFirst(body, ' ').second;
@@ -60,8 +62,12 @@ namespace Swift {
parsedMessage = emoticonHighlight(parsedMessage);
if (!senderIsSelf) { /* do not highlight our own messsages */
- /* do word-based color highlighting */
- parsedMessage = splitHighlight(parsedMessage, nick);
+ // Highlight keywords and own mentions.
+ parsedMessage = splitHighlight(parsedMessage);
+
+ // Highlight full message events like, specific sender, general
+ // incoming group message, or general incoming direct message.
+ parsedMessage = fullMessageHighlight(parsedMessage, senderNickname);
}
return parsedMessage;
@@ -89,7 +95,7 @@ namespace Swift {
boost::regex emoticonRegex(regexString);
ChatWindow::ChatMessage newMessage;
- for (std::shared_ptr<ChatWindow::ChatMessagePart> part : parsedMessage.getParts()) {
+ for (const auto& part : parsedMessage.getParts()) {
std::shared_ptr<ChatWindow::ChatTextMessagePart> textPart;
if ((textPart = std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(part))) {
try {
@@ -137,61 +143,122 @@ namespace Swift {
}
parsedMessage.setParts(newMessage.getParts());
}
+
return parsedMessage;
}
- ChatWindow::ChatMessage ChatMessageParser::splitHighlight(const ChatWindow::ChatMessage& message, const std::string& nick) {
- ChatWindow::ChatMessage parsedMessage = message;
-
- for (size_t i = 0; i < highlightRules_->getSize(); ++i) {
- const HighlightRule& rule = highlightRules_->getRule(i);
- if (rule.getMatchMUC() && !mucMode_) {
- continue; /* this rule only applies to MUC's, and this is a CHAT */
- } else if (rule.getMatchChat() && mucMode_) {
- continue; /* this rule only applies to CHAT's, and this is a MUC */
- } else if (rule.getAction().getTextBackground().empty() && rule.getAction().getTextColor().empty()) {
- continue; /* do not try to highlight text, if no highlight color is specified */
+ ChatWindow::ChatMessage ChatMessageParser::splitHighlight(const ChatWindow::ChatMessage& message) {
+ auto keywordToRegEx = [](const std::string& keyword, bool matchCaseSensitive) {
+ std::string escaped = Regex::escape(keyword);
+ boost::regex::flag_type flags = boost::regex::normal;
+ if (!matchCaseSensitive) {
+ flags |= boost::regex::icase;
}
- const std::vector<boost::regex> keywordRegex = rule.getKeywordRegex(nick);
- for (const boost::regex& regex : keywordRegex) {
- ChatWindow::ChatMessage newMessage;
- for (std::shared_ptr<ChatWindow::ChatMessagePart> part : parsedMessage.getParts()) {
- std::shared_ptr<ChatWindow::ChatTextMessagePart> textPart;
- if ((textPart = std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(part))) {
- try {
- boost::match_results<std::string::const_iterator> match;
- const std::string& text = textPart->text;
- std::string::const_iterator start = text.begin();
- while (regex_search(start, text.end(), match, regex)) {
- std::string::const_iterator matchStart = match[0].first;
- std::string::const_iterator matchEnd = match[0].second;
- if (start != matchStart) {
- /* If we're skipping over plain text since the previous emoticon, record it as plain text */
- newMessage.append(std::make_shared<ChatWindow::ChatTextMessagePart>(std::string(start, matchStart)));
- }
- std::shared_ptr<ChatWindow::ChatHighlightingMessagePart> highlightPart = std::make_shared<ChatWindow::ChatHighlightingMessagePart>();
- highlightPart->text = match.str();
- highlightPart->action = rule.getAction();
- newMessage.append(highlightPart);
- start = matchEnd;
- }
- if (start != text.end()) {
- /* If there's plain text after the last emoticon, record it */
- newMessage.append(std::make_shared<ChatWindow::ChatTextMessagePart>(std::string(start, text.end())));
+ return boost::regex("\\b" + escaped + "\\b", flags);
+ };
+
+ auto highlightKeywordInChatMessage = [&](const ChatWindow::ChatMessage& message, const std::string& keyword, bool matchCaseSensitive, const HighlightAction& action) {
+ ChatWindow::ChatMessage resultMessage;
+
+ for (const auto& part : message.getParts()) {
+ std::shared_ptr<ChatWindow::ChatTextMessagePart> textPart;
+ if ((textPart = std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(part))) {
+ try {
+ boost::match_results<std::string::const_iterator> match;
+ const std::string& text = textPart->text;
+ std::string::const_iterator start = text.begin();
+ while (regex_search(start, text.end(), match, keywordToRegEx(keyword, matchCaseSensitive))) {
+ std::string::const_iterator matchStart = match[0].first;
+ std::string::const_iterator matchEnd = match[0].second;
+ if (start != matchStart) {
+ /* If we're skipping over plain text since the previous emoticon, record it as plain text */
+ resultMessage.append(std::make_shared<ChatWindow::ChatTextMessagePart>(std::string(start, matchStart)));
}
+ std::shared_ptr<ChatWindow::ChatHighlightingMessagePart> highlightPart = std::make_shared<ChatWindow::ChatHighlightingMessagePart>();
+ highlightPart->text = match.str();
+ highlightPart->action = action;
+ resultMessage.append(highlightPart);
+ start = matchEnd;
}
- catch (std::runtime_error) {
- /* Basically too expensive to compute the regex results and it gave up, so pass through as text */
- newMessage.append(part);
+ if (start != text.end()) {
+ /* If there's plain text after the last emoticon, record it */
+ resultMessage.append(std::make_shared<ChatWindow::ChatTextMessagePart>(std::string(start, text.end())));
}
- } else {
- newMessage.append(part);
}
+ catch (std::runtime_error) {
+ /* Basically too expensive to compute the regex results and it gave up, so pass through as text */
+ resultMessage.append(part);
+ }
+ } else {
+ resultMessage.append(part);
}
- parsedMessage.setParts(newMessage.getParts());
}
+ return resultMessage;
+ };
+
+ ChatWindow::ChatMessage parsedMessage = message;
+
+ // detect mentions of own nickname
+ HighlightAction ownMentionKeywordAction = highlightConfiguration_->ownMentionAction;
+ ownMentionKeywordAction.setSoundFilePath(boost::optional<std::string>());
+ ownMentionKeywordAction.setSystemNotificationEnabled(false);
+ if (!getNick().empty() && !highlightConfiguration_->ownMentionAction.isEmpty()) {
+ auto nicknameHighlightedMessage = highlightKeywordInChatMessage(parsedMessage, nick_, false, ownMentionKeywordAction);
+ auto highlightedParts = nicknameHighlightedMessage.getParts();
+ auto ownNicknamePart = std::find_if(highlightedParts.begin(), highlightedParts.end(), [&](std::shared_ptr<ChatWindow::ChatMessagePart>& part){
+ auto highlightPart = std::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(part);
+ if (highlightPart && highlightPart->text == nick_) {
+ return true;
+ }
+ return false;
+ });
+ if (ownNicknamePart != highlightedParts.end()) {
+ parsedMessage.setHighlightActionOwnMention(highlightConfiguration_->ownMentionAction);
+ }
+ parsedMessage.setParts(nicknameHighlightedMessage.getParts());
+ }
+
+ // detect keywords
+ for (const auto& keywordHighlight : highlightConfiguration_->keywordHighlights) {
+ if (keywordHighlight.keyword.empty() || keywordHighlight.action.isEmpty()) {
+ continue;
+ }
+ auto newMessage = highlightKeywordInChatMessage(parsedMessage, keywordHighlight.keyword, keywordHighlight.matchCaseSensitive, keywordHighlight.action);
+ parsedMessage.setParts(newMessage.getParts());
}
return parsedMessage;
}
+
+ ChatWindow::ChatMessage ChatMessageParser::fullMessageHighlight(const ChatWindow::ChatMessage& parsedMessage, const std::string& sender) {
+ auto fullHighlightedMessage = parsedMessage;
+
+ // contact highlighting
+ for (const auto& contactHighlight : highlightConfiguration_->contactHighlights) {
+ if (sender == contactHighlight.name) {
+ fullHighlightedMessage.setHighlightActionSender(contactHighlight.action);
+ break;
+ }
+ }
+
+ // general incoming messages
+ HighlightAction groupAction;
+ HighlightAction chatAction;
+
+ switch (mode_) {
+ case Mode::GroupChat:
+ groupAction.setSoundFilePath(highlightConfiguration_->playSoundOnIncomingGroupchatMessages ? boost::optional<std::string>("") : boost::optional<std::string>());
+ groupAction.setSystemNotificationEnabled(highlightConfiguration_->showNotificationOnIncomingGroupchatMessages);
+ fullHighlightedMessage.setHighlightActionGroupMessage(groupAction);
+ break;
+
+ case Mode::Chat:
+ chatAction.setSoundFilePath(highlightConfiguration_->playSoundOnIncomingDirectMessages ? boost::optional<std::string>("") : boost::optional<std::string>());
+ chatAction.setSystemNotificationEnabled(highlightConfiguration_->showNotificationOnIncomingDirectMessages);
+ fullHighlightedMessage.setHighlightActonDirectMessage(chatAction);
+ break;
+ }
+
+ return fullHighlightedMessage;
+ }
}
diff --git a/Swift/Controllers/Chat/ChatMessageParser.h b/Swift/Controllers/Chat/ChatMessageParser.h
index 4bed669..de5eac9 100644
--- a/Swift/Controllers/Chat/ChatMessageParser.h
+++ b/Swift/Controllers/Chat/ChatMessageParser.h
@@ -1,26 +1,44 @@
/*
- * Copyright (c) 2013-2014 Isode Limited.
+ * Copyright (c) 2013-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <memory>
#include <string>
+#include <Swift/Controllers/Highlighting/HighlightConfiguration.h>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
namespace Swift {
+ /**
+ * @brief The ChatMessageParser class takes an emoticon map, a \ref HighlightConfiguration, and a boolean that indicates if the message context is in a MUC or not.
+ * The class handles parsing a message string and identifies emoticons, URLs, and various highlights.
+ */
class ChatMessageParser {
public:
- ChatMessageParser(const std::map<std::string, std::string>& emoticons, HighlightRulesListPtr highlightRules, bool mucMode = false);
- ChatWindow::ChatMessage parseMessageBody(const std::string& body, const std::string& nick = "", bool senderIsSelf = false);
+ enum class Mode { Chat, GroupChat };
+
+ public:
+ ChatMessageParser(const std::map<std::string, std::string>& emoticons, std::shared_ptr<HighlightConfiguration> highlightConfiguration, Mode mode = Mode::Chat);
+
+ void setNick(const std::string& nick) { nick_ = nick; }
+ std::string getNick() const { return nick_; }
+
+ ChatWindow::ChatMessage parseMessageBody(const std::string& body, const std::string& sender = "", bool senderIsSelf = false);
+
private:
ChatWindow::ChatMessage emoticonHighlight(const ChatWindow::ChatMessage& parsedMessage);
- ChatWindow::ChatMessage splitHighlight(const ChatWindow::ChatMessage& parsedMessage, const std::string& nick);
+ ChatWindow::ChatMessage splitHighlight(const ChatWindow::ChatMessage& parsedMessage);
+ ChatWindow::ChatMessage fullMessageHighlight(const ChatWindow::ChatMessage& parsedMessage, const std::string& sender);
+
+ private:
std::map<std::string, std::string> emoticons_;
- HighlightRulesListPtr highlightRules_;
- bool mucMode_;
+ std::shared_ptr<HighlightConfiguration> highlightConfiguration_;
+ Mode mode_;
+ std::string nick_;
};
}
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index f55df1e..fc96701 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -746,7 +746,7 @@ ChatController* ChatsManager::getChatControllerOrFindAnother(const JID &contact)
ChatController* ChatsManager::createNewChatController(const JID& contact) {
assert(chatControllers_.find(contact) == chatControllers_.end());
- std::shared_ptr<ChatMessageParser> chatMessageParser = std::make_shared<ChatMessageParser>(emoticons_, highlightManager_->getRules(), false); /* a message parser that knows this is a chat (not a room/MUC) */
+ std::shared_ptr<ChatMessageParser> chatMessageParser = std::make_shared<ChatMessageParser>(emoticons_, highlightManager_->getConfiguration(), ChatMessageParser::Mode::Chat); /* a message parser that knows this is a chat (not a room/MUC) */
ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_, userWantsReceipts_, settings_, historyController_, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser, autoAcceptMUCInviteDecider_);
chatControllers_[contact] = controller;
controller->setAvailableServerFeatures(serverDiscoInfo_);
@@ -835,8 +835,8 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti
if (reuseChatwindow) {
chatWindowFactoryAdapter = new SingleChatWindowFactoryAdapter(reuseChatwindow);
}
- std::shared_ptr<ChatMessageParser> chatMessageParser = std::make_shared<ChatMessageParser>(emoticons_, highlightManager_->getRules(), true); /* a message parser that knows this is a room/MUC (not a chat) */
- controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, reuseChatwindow ? chatWindowFactoryAdapter : chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_, roster_, historyController_, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser, isImpromptu, autoAcceptMUCInviteDecider_, vcardManager_, mucBookmarkManager_);
+ std::shared_ptr<ChatMessageParser> chatMessageParser = std::make_shared<ChatMessageParser>(emoticons_, highlightManager_->getConfiguration(), ChatMessageParser::Mode::GroupChat); /* a message parser that knows this is a room/MUC (not a chat) */
+ controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, reuseChatwindow ? chatWindowFactoryAdapter : chatWindowFactory_, nickResolver_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_, roster_, historyController_, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser, isImpromptu, autoAcceptMUCInviteDecider_, vcardManager_, mucBookmarkManager_);
if (chatWindowFactoryAdapter) {
/* The adapters are only passed to chat windows, which are deleted in their
* controllers' dtor, which are deleted in ChatManager's dtor. The adapters
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index df54d73..c476cf3 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -34,7 +34,7 @@
#include <SwifTools/TabComplete.h>
#include <Swift/Controllers/Chat/ChatMessageParser.h>
-#include <Swift/Controllers/Highlighter.h>
+#include <Swift/Controllers/Highlighting/Highlighter.h>
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/Roster/ContactRosterItem.h>
#include <Swift/Controllers/Roster/GroupRosterItem.h>
@@ -80,6 +80,7 @@ MUCController::MUCController (
StanzaChannel* stanzaChannel,
IQRouter* iqRouter,
ChatWindowFactory* chatWindowFactory,
+ NickResolver* nickResolver,
PresenceOracle* presenceOracle,
AvatarManager* avatarManager,
UIEventStream* uiEventStream,
@@ -97,7 +98,7 @@ MUCController::MUCController (
AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider,
VCardManager* vcardManager,
MUCBookmarkManager* mucBookmarkManager) :
- ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider), muc_(muc), nick_(nick), desiredNick_(nick), password_(password), renameCounter_(0), isImpromptu_(isImpromptu), isImpromptuAlreadyConfigured_(false), clientBlockListManager_(clientBlockListManager), mucBookmarkManager_(mucBookmarkManager) {
+ ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), nickResolver, presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider), muc_(muc), nick_(nick), desiredNick_(nick), password_(password), renameCounter_(0), isImpromptu_(isImpromptu), isImpromptuAlreadyConfigured_(false), clientBlockListManager_(clientBlockListManager), mucBookmarkManager_(mucBookmarkManager) {
parting_ = true;
joined_ = false;
lastWasPresence_ = false;
@@ -134,8 +135,7 @@ MUCController::MUCController (
muc_->onAffiliationListReceived.connect(boost::bind(&MUCController::handleAffiliationListReceived, this, _1, _2));
muc_->onConfigurationFailed.connect(boost::bind(&MUCController::handleConfigurationFailed, this, _1));
muc_->onConfigurationFormReceived.connect(boost::bind(&MUCController::handleConfigurationFormReceived, this, _1));
- highlighter_->setMode(isImpromptu_ ? Highlighter::ChatMode : Highlighter::MUCMode);
- highlighter_->setNick(nick_);
+ chatMessageParser_->setNick(nick_);
if (timerFactory && stanzaChannel_->isAvailable()) {
loginCheckTimer_ = std::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS));
loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this));
@@ -593,10 +593,11 @@ void MUCController::postHandleIncomingMessage(std::shared_ptr<MessageEvent> mess
std::shared_ptr<Message> message = messageEvent->getStanza();
if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && !message->getPayload<Delay>()) {
if (messageTargetsMe(message) || isImpromptu_) {
+ highlighter_->handleSystemNotifications(chatMessage, messageEvent);
eventController_->handleIncomingEvent(messageEvent);
}
if (!messageEvent->getConcluded()) {
- handleHighlightActions(chatMessage);
+ highlighter_->handleSoundNotifications(chatMessage);
}
}
}
@@ -1111,7 +1112,7 @@ void MUCController::checkDuplicates(std::shared_ptr<Message> newMessage) {
void MUCController::setNick(const std::string& nick) {
nick_ = nick;
- highlighter_->setNick(nick_);
+ chatMessageParser_->setNick(nick);
}
Form::ref MUCController::buildImpromptuRoomConfiguration(Form::ref roomConfigurationForm) {
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index 7ec2eb4..6244f6d 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -54,7 +54,7 @@ namespace Swift {
class MUCController : public ChatControllerBase {
public:
- MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* xmppRoster, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, bool isImpromptu, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, VCardManager* vcardManager, MUCBookmarkManager* mucBookmarkManager);
+ MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* xmppRoster, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, bool isImpromptu, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, VCardManager* vcardManager, MUCBookmarkManager* mucBookmarkManager);
virtual ~MUCController();
boost::signals2::signal<void ()> onUserLeft;
boost::signals2::signal<void ()> onUserJoined;
diff --git a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
index bc72b33..163a38a 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
@@ -1,30 +1,22 @@
/*
- * Copyright (c) 2013-2016 Isode Limited.
+ * Copyright (c) 2013-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
-#include <cppunit/extensions/HelperMacros.h>
-#include <cppunit/extensions/TestFactoryRegistry.h>
-#include <hippomocks.h>
+#include <gtest/gtest.h>
#include <Swift/Controllers/Chat/ChatMessageParser.h>
+#include <Swift/Controllers/Highlighting/HighlightConfiguration.h>
+#include <Swift/Controllers/Highlighting/HighlightManager.h>
+#include <Swift/Controllers/Settings/DummySettingsProvider.h>
using namespace Swift;
-class ChatMessageParserTest : public CppUnit::TestFixture {
- CPPUNIT_TEST_SUITE(ChatMessageParserTest);
- CPPUNIT_TEST(testFullBody);
- CPPUNIT_TEST(testOneEmoticon);
- CPPUNIT_TEST(testBareEmoticon);
- CPPUNIT_TEST(testHiddenEmoticon);
- CPPUNIT_TEST(testEndlineEmoticon);
- CPPUNIT_TEST(testBoundedEmoticons);
- CPPUNIT_TEST(testNoColourNoHighlight);
- CPPUNIT_TEST_SUITE_END();
-
-public:
- void setUp() {
+// Common test state
+class ChatMessageParserTest : public ::testing::Test {
+protected:
+ virtual void SetUp() {
smile1_ = ":)";
smile1Path_ = "/blah/smile1.png";
smile2_ = ":(";
@@ -33,244 +25,54 @@ public:
emoticons_[smile2_] = smile2Path_;
}
- void tearDown() {
+ virtual void TearDown() {
emoticons_.clear();
}
- void assertText(const ChatWindow::ChatMessage& result, size_t index, const std::string& text) {
+ static void assertText(const ChatWindow::ChatMessage& result, size_t index, const std::string& text) {
+ ASSERT_LT(index, result.getParts().size());
std::shared_ptr<ChatWindow::ChatTextMessagePart> part = std::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(result.getParts()[index]);
- CPPUNIT_ASSERT_EQUAL(text, part->text);
+ ASSERT_EQ(text, part->text);
}
- void assertEmoticon(const ChatWindow::ChatMessage& result, size_t index, const std::string& text, const std::string& path) {
+ static void assertEmoticon(const ChatWindow::ChatMessage& result, size_t index, const std::string& text, const std::string& path) {
+ ASSERT_LT(index, result.getParts().size());
std::shared_ptr<ChatWindow::ChatEmoticonMessagePart> part = std::dynamic_pointer_cast<ChatWindow::ChatEmoticonMessagePart>(result.getParts()[index]);
- CPPUNIT_ASSERT(!!part);
- CPPUNIT_ASSERT_EQUAL(text, part->alternativeText);
- CPPUNIT_ASSERT_EQUAL(path, part->imagePath);
+ ASSERT_NE(nullptr, part);
+ ASSERT_EQ(text, part->alternativeText);
+ ASSERT_EQ(path, part->imagePath);
}
-#define assertHighlight(RESULT, INDEX, TEXT, EXPECTED_HIGHLIGHT) \
- { \
- std::shared_ptr<ChatWindow::ChatHighlightingMessagePart> part = std::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(RESULT.getParts()[INDEX]); \
- CPPUNIT_ASSERT_EQUAL(std::string(TEXT), part->text); \
- CPPUNIT_ASSERT(EXPECTED_HIGHLIGHT == part->action); \
- }
-
- void assertURL(const ChatWindow::ChatMessage& result, size_t index, const std::string& text) {
+ static void assertURL(const ChatWindow::ChatMessage& result, size_t index, const std::string& text) {
+ ASSERT_LT(index, result.getParts().size());
std::shared_ptr<ChatWindow::ChatURIMessagePart> part = std::dynamic_pointer_cast<ChatWindow::ChatURIMessagePart>(result.getParts()[index]);
- CPPUNIT_ASSERT_EQUAL(text, part->target);
- }
-
- static HighlightRule ruleFromKeyword(const std::string& keyword, bool matchCase, bool matchWholeWord)
- {
- HighlightRule rule;
- std::vector<std::string> keywords;
- keywords.push_back(keyword);
- rule.setKeywords(keywords);
- rule.setMatchCase(matchCase);
- rule.setMatchWholeWords(matchWholeWord);
- rule.setMatchChat(true);
- rule.getAction().setTextBackground("white");
- return rule;
- }
-
- static const HighlightRulesListPtr ruleListFromKeyword(const std::string& keyword, bool matchCase, bool matchWholeWord)
- {
- std::shared_ptr<HighlightManager::HighlightRulesList> list = std::make_shared<HighlightManager::HighlightRulesList>();
- list->addRule(ruleFromKeyword(keyword, matchCase, matchWholeWord));
- return list;
- }
-
- static const HighlightRulesListPtr ruleListFromKeywords(const HighlightRule &rule1, const HighlightRule &rule2)
- {
- std::shared_ptr<HighlightManager::HighlightRulesList> list = std::make_shared<HighlightManager::HighlightRulesList>();
- list->addRule(rule1);
- list->addRule(rule2);
- return list;
- }
-
- static HighlightRulesListPtr ruleListWithNickHighlight(bool withHighlightColour = true)
- {
- HighlightRule rule;
- rule.setMatchChat(true);
- rule.setNickIsKeyword(true);
- rule.setMatchCase(true);
- rule.setMatchWholeWords(true);
- if (withHighlightColour) {
- rule.getAction().setTextBackground("white");
- }
- std::shared_ptr<HighlightManager::HighlightRulesList> list = std::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_, std::make_shared<HighlightManager::HighlightRulesList>());
- ChatWindow::ChatMessage result = testling.parseMessageBody(no_special_message);
- assertText(result, 0, no_special_message);
-
- HighlightRulesListPtr highlightRuleList = ruleListFromKeyword("trigger", false, false);
- testling = ChatMessageParser(emoticons_, highlightRuleList);
- result = testling.parseMessageBody(":) shiny :( trigger :) http://wonderland.lit/blah http://denmark.lit boom boom");
- assertEmoticon(result, 0, smile1_, smile1Path_);
- assertText(result, 1, " shiny ");
- assertEmoticon(result, 2, smile2_, smile2Path_);
- assertText(result, 3, " ");
- assertHighlight(result, 4, "trigger", highlightRuleList->getRule(0).getAction());
- assertText(result, 5, " ");
- assertEmoticon(result, 6, smile1_, smile1Path_);
- assertText(result, 7, " ");
- assertURL(result, 8, "http://wonderland.lit/blah");
- assertText(result, 9, " ");
- assertURL(result, 10, "http://denmark.lit");
- assertText(result, 11, " boom boom");
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", false, false));
- result = testling.parseMessageBody("testtriggermessage");
- assertText(result, 0, "test");
- assertHighlight(result, 1, "trigger", highlightRuleList->getRule(0).getAction());
- assertText(result, 2, "message");
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", false, true));
- result = testling.parseMessageBody("testtriggermessage");
- assertText(result, 0, "testtriggermessage");
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", true, false));
- result = testling.parseMessageBody("TrIgGeR");
- assertText(result, 0, "TrIgGeR");
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", false, false));
- result = testling.parseMessageBody("TrIgGeR");
- assertHighlight(result, 0, "TrIgGeR", highlightRuleList->getRule(0).getAction());
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", false, false));
- result = testling.parseMessageBody("partialTrIgGeRmatch");
- assertText(result, 0, "partial");
- assertHighlight(result, 1, "TrIgGeR", highlightRuleList->getRule(0).getAction());
- assertText(result, 2, "match");
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, false), ruleFromKeyword("three", false, false)));
- result = testling.parseMessageBody("zero one two three");
- assertText(result, 0, "zero ");
- assertHighlight(result, 1, "one", highlightRuleList->getRule(0).getAction());
- assertText(result, 2, " two ");
- assertHighlight(result, 3, "three", highlightRuleList->getRule(0).getAction());
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, false), ruleFromKeyword("three", false, false)));
- result = testling.parseMessageBody("zero oNe two tHrEe");
- assertText(result, 0, "zero ");
- assertHighlight(result, 1, "oNe", highlightRuleList->getRule(0).getAction());
- assertText(result, 2, " two ");
- assertHighlight(result, 3, "tHrEe", highlightRuleList->getRule(0).getAction());
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, false), ruleFromKeyword("three", true, false)));
- result = testling.parseMessageBody("zero oNe two tHrEe");
- assertText(result, 0, "zero ");
- assertHighlight(result, 1, "oNe", highlightRuleList->getRule(0).getAction());
- assertText(result, 2, " two tHrEe");
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", true, false), ruleFromKeyword("three", true, false)));
- result = testling.parseMessageBody("zero oNe two tHrEe");
- assertText(result, 0, "zero oNe two tHrEe");
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, false), ruleFromKeyword("three", false, false)));
- result = testling.parseMessageBody("zeroonetwothree");
- assertText(result, 0, "zero");
- assertHighlight(result, 1, "one", highlightRuleList->getRule(0).getAction());
- assertText(result, 2, "two");
- assertHighlight(result, 3, "three", highlightRuleList->getRule(0).getAction());
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", true, false), ruleFromKeyword("three", false, false)));
- result = testling.parseMessageBody("zeroOnEtwoThReE");
- assertText(result, 0, "zeroOnEtwo");
- assertHighlight(result, 1, "ThReE", highlightRuleList->getRule(0).getAction());
-
- testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, true), ruleFromKeyword("three", false, false)));
- result = testling.parseMessageBody("zeroonetwothree");
- assertText(result, 0, "zeroonetwo");
- assertHighlight(result, 1, "three", highlightRuleList->getRule(0).getAction());
-
- 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", highlightRuleList->getRule(0).getAction());
-
- 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", highlightRuleList->getRule(0).getAction());
- assertText(result, 2, " Text");
-
- testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
- result = testling.parseMessageBody("Alice Text", "Alice");
- assertHighlight(result, 0, "Alice", highlightRuleList->getRule(0).getAction());
- assertText(result, 1, " Text");
-
- testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
- result = testling.parseMessageBody("Text Alice", "Alice");
- assertText(result, 0, "Text ");
- assertHighlight(result, 1, "Alice", highlightRuleList->getRule(0).getAction());
- }
-
- void testOneEmoticon() {
- ChatMessageParser testling(emoticons_, std::make_shared<HighlightManager::HighlightRulesList>());
- ChatWindow::ChatMessage result = testling.parseMessageBody(" :) ");
- assertText(result, 0, " ");
- assertEmoticon(result, 1, smile1_, smile1Path_);
- assertText(result, 2, " ");
- }
-
-
- void testBareEmoticon() {
- ChatMessageParser testling(emoticons_, std::make_shared<HighlightManager::HighlightRulesList>());
- ChatWindow::ChatMessage result = testling.parseMessageBody(":)");
- assertEmoticon(result, 0, smile1_, smile1Path_);
- }
-
- void testHiddenEmoticon() {
- ChatMessageParser testling(emoticons_, std::make_shared<HighlightManager::HighlightRulesList>());
- ChatWindow::ChatMessage result = testling.parseMessageBody("b:)a");
- assertText(result, 0, "b:)a");
- }
-
- void testEndlineEmoticon() {
- ChatMessageParser testling(emoticons_, std::make_shared<HighlightManager::HighlightRulesList>());
- ChatWindow::ChatMessage result = testling.parseMessageBody("Lazy:)");
- assertText(result, 0, "Lazy");
- assertEmoticon(result, 1, smile1_, smile1Path_);
+ ASSERT_EQ(text, part->target);
}
- void testBoundedEmoticons() {
- ChatMessageParser testling(emoticons_, std::make_shared<HighlightManager::HighlightRulesList>());
- ChatWindow::ChatMessage result = testling.parseMessageBody(":)Lazy:(");
- assertEmoticon(result, 0, smile1_, smile1Path_);
- assertText(result, 1, "Lazy");
- assertEmoticon(result, 2, smile2_, smile2Path_);
+ void assertHighlight(const ChatWindow::ChatMessage& result, size_t index, const std::string& text, const HighlightAction& action) {
+ ASSERT_LT(index, result.getParts().size());
+ std::shared_ptr<ChatWindow::ChatHighlightingMessagePart> part = std::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(result.getParts()[index]);
+ ASSERT_EQ(std::string(text), part->text);
+ ASSERT_EQ(action, part->action);
}
- void testEmoticonParenthesis() {
- ChatMessageParser testling(emoticons_, std::make_shared<HighlightManager::HighlightRulesList>());
- ChatWindow::ChatMessage result = testling.parseMessageBody("(Like this :))");
- assertText(result, 0, "(Like this ");
- assertEmoticon(result, 1, smile1_, smile1Path_);
- assertText(result, 2, ")");
+ static const std::shared_ptr<HighlightConfiguration> highlightConfigFromKeyword(const std::string& keyword, bool matchCase) {
+ std::shared_ptr<HighlightConfiguration> config = std::make_shared<HighlightConfiguration>();
+ HighlightConfiguration::KeywordHightlight keywordHighlight;
+ keywordHighlight.keyword = keyword;
+ keywordHighlight.matchCaseSensitive = matchCase;
+ keywordHighlight.action.setFrontColor(std::string("#121212"));
+ config->keywordHighlights.push_back(keywordHighlight);
+ return config;
}
- void testNoColourNoHighlight() {
- ChatMessageParser testling(emoticons_, ruleListWithNickHighlight(false));
- ChatWindow::ChatMessage result = testling.parseMessageBody("Alice", "Alice");
- assertText(result, 0, "Alice");
+ static const std::shared_ptr<HighlightConfiguration> mergeHighlightConfig(const std::shared_ptr<HighlightConfiguration>& configA, const std::shared_ptr<HighlightConfiguration>& configB) {
+ std::shared_ptr<HighlightConfiguration> config = std::make_shared<HighlightConfiguration>();
+ config->keywordHighlights.insert(config->keywordHighlights.end(), configA->keywordHighlights.begin(), configA->keywordHighlights.end());
+ config->keywordHighlights.insert(config->keywordHighlights.end(), configB->keywordHighlights.begin(), configB->keywordHighlights.end());
+ return config;
}
-private:
std::map<std::string, std::string> emoticons_;
std::string smile1_;
std::string smile1Path_;
@@ -278,4 +80,213 @@ private:
std::string smile2Path_;
};
-CPPUNIT_TEST_SUITE_REGISTRATION(ChatMessageParserTest);
+TEST_F(ChatMessageParserTest, testNoHighlightingWithEmtpyConfiguration) {
+ const std::string no_special_message = "a message with no special content";
+ ChatMessageParser testling(emoticons_, std::make_shared<HighlightConfiguration>());
+ auto result = testling.parseMessageBody(no_special_message);
+ assertText(result, 0, no_special_message);
+}
+
+TEST_F(ChatMessageParserTest, testSimpleHighlightAndEmojiAndUrlParsing) {
+ auto highlightConfig = highlightConfigFromKeyword("trigger", false);
+ auto testling = ChatMessageParser(emoticons_, highlightConfig);
+ auto result = testling.parseMessageBody(":) shiny :( trigger :) http://wonderland.lit/blah http://denmark.lit boom boom");
+ assertEmoticon(result, 0, smile1_, smile1Path_);
+ assertText(result, 1, " shiny ");
+ assertEmoticon(result, 2, smile2_, smile2Path_);
+ assertText(result, 3, " ");
+ assertHighlight(result, 4, "trigger", highlightConfig->keywordHighlights[0].action);
+ assertText(result, 5, " ");
+ assertEmoticon(result, 6, smile1_, smile1Path_);
+ assertText(result, 7, " ");
+ assertURL(result, 8, "http://wonderland.lit/blah");
+ assertText(result, 9, " ");
+ assertURL(result, 10, "http://denmark.lit");
+ assertText(result, 11, " boom boom");
+}
+
+TEST_F(ChatMessageParserTest, testNoKeywordHighlightAsPartOfLongerWords) {
+ auto testling = ChatMessageParser(emoticons_, highlightConfigFromKeyword("trigger", false));
+ auto result = testling.parseMessageBody("testtriggermessage");
+ assertText(result, 0, "testtriggermessage");
+}
+
+TEST_F(ChatMessageParserTest, testCaseInsensitiveKeyordHighlight) {
+ auto config = highlightConfigFromKeyword("trigger", true);
+ auto testling = ChatMessageParser(emoticons_, config);
+ auto result = testling.parseMessageBody("TrIgGeR");
+ assertText(result, 0, "TrIgGeR");
+
+ testling = ChatMessageParser(emoticons_, highlightConfigFromKeyword("trigger", false));
+ result = testling.parseMessageBody("TrIgGeR");
+ assertHighlight(result, 0, "TrIgGeR", config->keywordHighlights[0].action);
+}
+
+TEST_F(ChatMessageParserTest, testMultipleKeywordHighlights) {
+ auto config = mergeHighlightConfig(highlightConfigFromKeyword("one", false), highlightConfigFromKeyword("three", false));
+ auto testling = ChatMessageParser(emoticons_, config);
+ auto result = testling.parseMessageBody("zero one two three");
+ assertText(result, 0, "zero ");
+ assertHighlight(result, 1, "one", config->keywordHighlights[0].action);
+ assertText(result, 2, " two ");
+ assertHighlight(result, 3, "three", config->keywordHighlights[0].action);
+}
+
+TEST_F(ChatMessageParserTest, testMultipleCaseInsensitiveKeywordHighlights) {
+ auto config = mergeHighlightConfig(highlightConfigFromKeyword("one", false), highlightConfigFromKeyword("three", false));
+ auto testling = ChatMessageParser(emoticons_, config);
+ auto result = testling.parseMessageBody("zero oNe two tHrEe");
+ assertText(result, 0, "zero ");
+ assertHighlight(result, 1, "oNe", config->keywordHighlights[0].action);
+ assertText(result, 2, " two ");
+ assertHighlight(result, 3, "tHrEe", config->keywordHighlights[0].action);
+}
+
+TEST_F(ChatMessageParserTest, testMultipleCaseSensitiveKeywordHighlights) {
+ auto config = mergeHighlightConfig(highlightConfigFromKeyword("one", false), highlightConfigFromKeyword("three", true));
+ auto testling = ChatMessageParser(emoticons_, config);
+ auto result = testling.parseMessageBody("zero oNe two tHrEe");
+ assertText(result, 0, "zero ");
+ assertHighlight(result, 1, "oNe", config->keywordHighlights[0].action);
+ assertText(result, 2, " two tHrEe");
+
+ config = mergeHighlightConfig(highlightConfigFromKeyword("one", true), highlightConfigFromKeyword("three", false));
+ testling = ChatMessageParser(emoticons_, config);
+ result = testling.parseMessageBody("zero oNe two tHrEe");
+ assertText(result, 0, "zero oNe two ");
+ assertHighlight(result, 1, "tHrEe", config->keywordHighlights[0].action);
+}
+
+TEST_F(ChatMessageParserTest, testOneEmoticon) {
+ auto testling = ChatMessageParser(emoticons_, std::make_shared<HighlightConfiguration>());
+ auto result = testling.parseMessageBody(" :) ");
+ assertText(result, 0, " ");
+ assertEmoticon(result, 1, smile1_, smile1Path_);
+ assertText(result, 2, " ");
+}
+
+TEST_F(ChatMessageParserTest, testBareEmoticon) {
+ auto testling = ChatMessageParser(emoticons_, std::make_shared<HighlightConfiguration>());
+ auto result = testling.parseMessageBody(":)");
+ assertEmoticon(result, 0, smile1_, smile1Path_);
+}
+
+TEST_F(ChatMessageParserTest, testHiddenEmoticon) {
+ auto testling = ChatMessageParser(emoticons_, std::make_shared<HighlightConfiguration>());
+ auto result = testling.parseMessageBody("b:)a");
+ assertText(result, 0, "b:)a");
+}
+
+TEST_F(ChatMessageParserTest, testEndlineEmoticon) {
+ auto testling = ChatMessageParser(emoticons_, std::make_shared<HighlightConfiguration>());
+ auto result = testling.parseMessageBody("Lazy:)");
+ assertText(result, 0, "Lazy");
+ assertEmoticon(result, 1, smile1_, smile1Path_);
+}
+
+TEST_F(ChatMessageParserTest, testBoundedEmoticons) {
+ auto testling = ChatMessageParser(emoticons_, std::make_shared<HighlightConfiguration>());
+ auto result = testling.parseMessageBody(":)Lazy:(");
+ assertEmoticon(result, 0, smile1_, smile1Path_);
+ assertText(result, 1, "Lazy");
+ assertEmoticon(result, 2, smile2_, smile2Path_);
+}
+
+TEST_F(ChatMessageParserTest, testEmoticonParenthesis) {
+ auto testling = ChatMessageParser(emoticons_, std::make_shared<HighlightConfiguration>());
+ auto result = testling.parseMessageBody("(Like this :))");
+ assertText(result, 0, "(Like this ");
+ assertEmoticon(result, 1, smile1_, smile1Path_);
+ assertText(result, 2, ")");
+}
+
+TEST_F(ChatMessageParserTest, testSenderAndKeywordHighlighting) {
+ auto config = std::make_shared<HighlightConfiguration>();
+ auto contactHighlight = HighlightConfiguration::ContactHighlight();
+ contactHighlight.action.setFrontColor(std::string("#f0f0f0"));
+ contactHighlight.action.setBackColor(std::string("#0f0f0f"));
+ contactHighlight.name = "Romeo";
+ config->contactHighlights.push_back(contactHighlight);
+ auto keywordHighlight = HighlightConfiguration::KeywordHightlight();
+ keywordHighlight.action.setFrontColor(std::string("#abcdef"));
+ keywordHighlight.action.setBackColor(std::string("#fedcba"));
+ keywordHighlight.keyword = "XMPP";
+ config->keywordHighlights.push_back(keywordHighlight);
+ auto testling = ChatMessageParser(emoticons_, config);
+ auto result = testling.parseMessageBody("Heard any news about xmpp recently?", "Romeo");
+ assertText(result, 0, "Heard any news about ");
+ assertHighlight(result, 1, "xmpp", keywordHighlight.action);
+ assertText(result, 2, " recently?");
+ ASSERT_EQ(contactHighlight.action, result.getHighlightActionSender());
+ ASSERT_EQ(HighlightAction(), result.getHighlightActionOwnMention());
+}
+
+TEST_F(ChatMessageParserTest, testKeywordWithEmptyActionIsIgnored) {
+ auto config = std::make_shared<HighlightConfiguration>();
+ auto keywordHighlight = HighlightConfiguration::KeywordHightlight();
+ keywordHighlight.keyword = "XMPP";
+ config->keywordHighlights.push_back(keywordHighlight);
+ auto testling = ChatMessageParser(emoticons_, config);
+ auto result = testling.parseMessageBody("Heard any news about xmpp recently?", "Romeo");
+ assertText(result, 0, "Heard any news about xmpp recently?");
+ ASSERT_EQ(HighlightAction(), result.getHighlightActionOwnMention());
+}
+
+TEST_F(ChatMessageParserTest, testMeMessageAndOwnMention) {
+ auto config = std::make_shared<HighlightConfiguration>();
+ config->ownMentionAction.setFrontColor(std::string("#f0f0f0"));
+ config->ownMentionAction.setBackColor(std::string("#0f0f0f"));
+ config->ownMentionAction.setSoundFilePath(std::string("someSoundFile.wav"));
+ auto ownMentionActionForPart = config->ownMentionAction;
+ ownMentionActionForPart.setSoundFilePath(boost::optional<std::string>());
+ auto testling = ChatMessageParser(emoticons_, config);
+ testling.setNick("Juliet");
+ auto result = testling.parseMessageBody("/me wonders when Juliet is coming?", "Romeo");
+ assertText(result, 0, "wonders when ");
+ assertHighlight(result, 1, "Juliet", ownMentionActionForPart);
+ assertText(result, 2, " is coming?");
+ ASSERT_EQ(true, result.isMeCommand());
+ ASSERT_EQ(config->ownMentionAction, result.getHighlightActionOwnMention());
+}
+
+TEST_F(ChatMessageParserTest, testSoundAndNotificationOnDirectMessage) {
+ auto defaultConfiguration = std::make_shared<HighlightConfiguration>();
+ defaultConfiguration->playSoundOnIncomingDirectMessages = true;
+ defaultConfiguration->showNotificationOnIncomingDirectMessages = true;
+ defaultConfiguration->ownMentionAction.setFrontColor(std::string("black"));
+ defaultConfiguration->ownMentionAction.setBackColor(std::string("yellow"));
+ defaultConfiguration->ownMentionAction.setSoundFilePath(std::string(""));
+ defaultConfiguration->ownMentionAction.setSystemNotificationEnabled(true);
+
+ auto testling = ChatMessageParser(emoticons_, defaultConfiguration, ChatMessageParser::Mode::Chat);
+ auto result = testling.parseMessageBody("I wonder when Juliet is coming?", "Romeo");
+
+ ASSERT_EQ(std::string(""), result.getHighlightActionDirectMessage().getSoundFilePath().get_value_or(std::string("somethingElse")));
+ ASSERT_EQ(true, result.getHighlightActionDirectMessage().isSystemNotificationEnabled());
+ ASSERT_EQ(HighlightAction(), result.getHighlightActionOwnMention());
+}
+
+TEST_F(ChatMessageParserTest, testWithDefaultConfiguration) {
+ DummySettingsProvider settings;
+ HighlightManager manager(&settings);
+ manager.resetToDefaultConfiguration();
+ auto testling = ChatMessageParser(emoticons_, manager.getConfiguration(), ChatMessageParser::Mode::GroupChat);
+ testling.setNick("Juliet");
+ auto result = testling.parseMessageBody("Hello, how is it going?", "Romeo");
+ assertText(result, 0, "Hello, how is it going?");
+ ASSERT_EQ(HighlightAction(), result.getHighlightActionOwnMention());
+ ASSERT_EQ(HighlightAction(), result.getHighlightActionDirectMessage());
+ ASSERT_EQ(HighlightAction(), result.getHighlightActionGroupMessage());
+ ASSERT_EQ(HighlightAction(), result.getHighlightActionSender());
+
+ result = testling.parseMessageBody("Juliet, seen the new interface design?", "Romeo");
+ auto mentionKeywordAction = manager.getConfiguration()->ownMentionAction;
+ mentionKeywordAction.setSoundFilePath(boost::optional<std::string>());
+ mentionKeywordAction.setSystemNotificationEnabled(false);
+ assertHighlight(result, 0, "Juliet", mentionKeywordAction);
+ assertText(result, 1, ", seen the new interface design?");
+ ASSERT_EQ(manager.getConfiguration()->ownMentionAction, result.getHighlightActionOwnMention());
+ ASSERT_EQ(HighlightAction(), result.getHighlightActionDirectMessage());
+ ASSERT_EQ(HighlightAction(), result.getHighlightActionGroupMessage());
+ ASSERT_EQ(HighlightAction(), result.getHighlightActionSender());
+}
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index cff54f8..80f8346 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -131,7 +131,7 @@ public:
wbSessionManager_ = new WhiteboardSessionManager(iqRouter_, stanzaChannel_, presenceOracle_, entityCapsProvider_);
wbManager_ = new WhiteboardManager(whiteboardWindowFactory_, uiEventStream_, nickResolver_, wbSessionManager_);
highlightManager_ = new HighlightManager(settings_);
- highlightManager_->resetToDefaultRulesList();
+ highlightManager_->resetToDefaultConfiguration();
handledHighlightActions_ = 0;
soundsPlayed_.clear();
highlightManager_->onHighlight.connect(boost::bind(&ChatsManagerTest::handleHighlightAction, this, _1));
@@ -786,24 +786,17 @@ public:
}
void testChatControllerHighlightingNotificationTesting() {
- HighlightRule keywordRuleA;
- keywordRuleA.setMatchChat(true);
- std::vector<std::string> keywordsA;
- keywordsA.push_back("Romeo");
- keywordRuleA.setKeywords(keywordsA);
- keywordRuleA.getAction().setTextColor("yellow");
- keywordRuleA.getAction().setPlaySound(true);
- highlightManager_->insertRule(0, keywordRuleA);
-
- HighlightRule keywordRuleB;
- keywordRuleB.setMatchChat(true);
- std::vector<std::string> keywordsB;
- keywordsB.push_back("Juliet");
- keywordRuleB.setKeywords(keywordsB);
- keywordRuleB.getAction().setTextColor("green");
- keywordRuleB.getAction().setPlaySound(true);
- keywordRuleB.getAction().setSoundFile("/tmp/someotherfile.wav");
- highlightManager_->insertRule(0, keywordRuleB);
+ HighlightConfiguration::KeywordHightlight keywordRuleA;
+ keywordRuleA.keyword = "Romeo";
+ keywordRuleA.action.setFrontColor(boost::optional<std::string>("yellow"));
+ keywordRuleA.action.setSoundFilePath(boost::optional<std::string>(""));
+ highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordRuleA);
+
+ HighlightConfiguration::KeywordHightlight keywordRuleB;
+ keywordRuleB.keyword = "Juliet";
+ keywordRuleB.action.setFrontColor(boost::optional<std::string>("green"));
+ keywordRuleB.action.setSoundFilePath(boost::optional<std::string>("/tmp/someotherfile.wav"));
+ highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordRuleB);
JID messageJID = JID("testling@test.com");
@@ -817,28 +810,22 @@ public:
manager_->handleIncomingMessage(message);
CPPUNIT_ASSERT_EQUAL(2, handledHighlightActions_);
- CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleA.getAction().getSoundFile()) != soundsPlayed_.end());
- CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleB.getAction().getSoundFile()) != soundsPlayed_.end());
+ CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleA.action.getSoundFilePath().get_value_or("")) != soundsPlayed_.end());
+ CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleB.action.getSoundFilePath().get_value_or("")) != soundsPlayed_.end());
}
void testChatControllerHighlightingNotificationDeduplicateSounds() {
- HighlightRule keywordRuleA;
- keywordRuleA.setMatchChat(true);
- std::vector<std::string> keywordsA;
- keywordsA.push_back("Romeo");
- keywordRuleA.setKeywords(keywordsA);
- keywordRuleA.getAction().setTextColor("yellow");
- keywordRuleA.getAction().setPlaySound(true);
- highlightManager_->insertRule(0, keywordRuleA);
-
- HighlightRule keywordRuleB;
- keywordRuleB.setMatchChat(true);
- std::vector<std::string> keywordsB;
- keywordsB.push_back("Juliet");
- keywordRuleB.setKeywords(keywordsB);
- keywordRuleB.getAction().setTextColor("green");
- keywordRuleB.getAction().setPlaySound(true);
- highlightManager_->insertRule(0, keywordRuleB);
+ auto keywordRuleA = HighlightConfiguration::KeywordHightlight();
+ keywordRuleA.keyword = "Romeo";
+ keywordRuleA.action.setFrontColor(boost::optional<std::string>("yellow"));
+ keywordRuleA.action.setSoundFilePath(boost::optional<std::string>(""));
+ highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordRuleA);
+
+ auto keywordRuleB = HighlightConfiguration::KeywordHightlight();
+ keywordRuleB.keyword = "Juliet";
+ keywordRuleB.action.setFrontColor(boost::optional<std::string>("green"));
+ keywordRuleB.action.setSoundFilePath(boost::optional<std::string>(""));
+ highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordRuleB);
JID messageJID = JID("testling@test.com");
@@ -852,8 +839,8 @@ public:
manager_->handleIncomingMessage(message);
CPPUNIT_ASSERT_EQUAL(1, handledHighlightActions_);
- CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleA.getAction().getSoundFile()) != soundsPlayed_.end());
- CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleB.getAction().getSoundFile()) != soundsPlayed_.end());
+ CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleA.action.getSoundFilePath().get_value_or("")) != soundsPlayed_.end());
+ CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleB.action.getSoundFilePath().get_value_or("")) != soundsPlayed_.end());
}
void testChatControllerMeMessageHandling() {
@@ -922,12 +909,13 @@ public:
JID mucJID("mucroom@rooms.test.com");
std::string nickname = "toodles";
+ //highlightManager_->resetToDefaultConfiguration();
+
// add highlight rule for 'foo'
- HighlightRule fooHighlight;
- fooHighlight.setKeywords({"foo"});
- fooHighlight.setMatchMUC(true);
- fooHighlight.getAction().setTextBackground("green");
- highlightManager_->insertRule(0, fooHighlight);
+ HighlightConfiguration::KeywordHightlight keywordHighlight;
+ keywordHighlight.keyword = "foo";
+ keywordHighlight.action.setBackColor(boost::optional<std::string>("green"));
+ highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordHighlight);
MockChatWindow* window = new MockChatWindow();
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window);
@@ -1141,8 +1129,8 @@ private:
void handleHighlightAction(const HighlightAction& action) {
handledHighlightActions_++;
- if (action.playSound()) {
- soundsPlayed_.insert(action.getSoundFile());
+ if (action.getSoundFilePath()) {
+ soundsPlayed_.insert(action.getSoundFilePath().get_value_or(""));
}
}
diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
index dad021f..eabf4c5 100644
--- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -94,18 +94,20 @@ public:
highlightManager_ = new HighlightManager(settings_);
muc_ = std::make_shared<MockMUC>(mucJID_);
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc_->getJID(), uiEventStream_).Return(window_);
- chatMessageParser_ = std::make_shared<ChatMessageParser>(std::map<std::string, std::string>(), highlightManager_->getRules(), true);
+ chatMessageParser_ = std::make_shared<ChatMessageParser>(std::map<std::string, std::string>(), highlightManager_->getConfiguration(), ChatMessageParser::Mode::GroupChat);
vcardStorage_ = new VCardMemoryStorage(crypto_.get());
vcardManager_ = new VCardManager(self_, iqRouter_, vcardStorage_);
+ nickResolver_ = new NickResolver(self_, xmppRoster_, vcardManager_, mucRegistry_);
clientBlockListManager_ = new ClientBlockListManager(iqRouter_);
mucBookmarkManager_ = new MUCBookmarkManager(iqRouter_);
- controller_ = new MUCController (self_, muc_, boost::optional<std::string>(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_, nullptr, nullptr, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser_, false, nullptr, vcardManager_, mucBookmarkManager_);
+ controller_ = new MUCController (self_, muc_, boost::optional<std::string>(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, nickResolver_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_, nullptr, nullptr, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser_, false, nullptr, vcardManager_, mucBookmarkManager_);
}
void tearDown() {
delete controller_;
delete mucBookmarkManager_;
delete clientBlockListManager_;
+ delete nickResolver_;
delete vcardManager_;
delete vcardStorage_;
delete highlightManager_;
@@ -592,7 +594,7 @@ private:
ChatWindowFactory* chatWindowFactory_;
UserSearchWindowFactory* userSearchWindowFactory_;
MUCController* controller_;
-// NickResolver* nickResolver_;
+ NickResolver* nickResolver_;
PresenceOracle* presenceOracle_;
AvatarManager* avatarManager_;
StanzaChannelPresenceSender* presenceSender_;