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/Highlighting
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/Highlighting')
-rw-r--r--Swift/Controllers/Highlighting/HighlightAction.cpp75
-rw-r--r--Swift/Controllers/Highlighting/HighlightAction.h68
-rw-r--r--Swift/Controllers/Highlighting/HighlightConfiguration.cpp36
-rw-r--r--Swift/Controllers/Highlighting/HighlightConfiguration.h81
-rw-r--r--Swift/Controllers/Highlighting/HighlightEditorController.cpp57
-rw-r--r--Swift/Controllers/Highlighting/HighlightEditorController.h49
-rw-r--r--Swift/Controllers/Highlighting/HighlightManager.cpp102
-rw-r--r--Swift/Controllers/Highlighting/HighlightManager.h64
-rw-r--r--Swift/Controllers/Highlighting/Highlighter.cpp113
-rw-r--r--Swift/Controllers/Highlighting/Highlighter.h38
10 files changed, 683 insertions, 0 deletions
diff --git a/Swift/Controllers/Highlighting/HighlightAction.cpp b/Swift/Controllers/Highlighting/HighlightAction.cpp
new file mode 100644
index 0000000..e9f14df
--- /dev/null
+++ b/Swift/Controllers/Highlighting/HighlightAction.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2012 Maciej Niedzielski
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2015-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/Controllers/Highlighting/HighlightAction.h>
+
+namespace Swift {
+
+
+
+bool operator ==(const HighlightAction& a, const HighlightAction& b) {
+ if (a.getFrontColor() != b.getFrontColor()) {
+ return false;
+ }
+ if (a.getBackColor() != b.getBackColor()) {
+ return false;
+ }
+ if (a.getSoundFilePath() != b.getSoundFilePath()) {
+ return false;
+ }
+ if (a.isSystemNotificationEnabled() != b.isSystemNotificationEnabled()) {
+ return false;
+ }
+ return true;
+}
+
+bool operator !=(const HighlightAction& a, const HighlightAction& b) {
+ return !(a == b);
+}
+
+void HighlightAction::setFrontColor(const boost::optional<std::string>& frontColor) {
+ frontColor_ = frontColor;
+}
+
+boost::optional<std::string> HighlightAction::getFrontColor() const {
+ return frontColor_;
+}
+
+void HighlightAction::setBackColor(const boost::optional<std::string>& backColor) {
+ backColor_ = backColor;
+}
+
+boost::optional<std::string> HighlightAction::getBackColor() const {
+ return backColor_;
+}
+
+void HighlightAction::setSoundFilePath(const boost::optional<std::string>& soundFilePath) {
+ soundFilePath_ = soundFilePath;
+}
+
+boost::optional<std::string> HighlightAction::getSoundFilePath() const {
+ return soundFilePath_;
+}
+
+void HighlightAction::setSystemNotificationEnabled(bool systemNotificationEnabled) {
+ systemNotificaitonEnabled_ = systemNotificationEnabled;
+}
+
+bool HighlightAction::isSystemNotificationEnabled() const {
+ return systemNotificaitonEnabled_;
+}
+
+bool HighlightAction::isEmpty() const {
+ return !frontColor_.is_initialized() && !backColor_.is_initialized() && !soundFilePath_.is_initialized() && !systemNotificaitonEnabled_;
+}
+
+}
diff --git a/Swift/Controllers/Highlighting/HighlightAction.h b/Swift/Controllers/Highlighting/HighlightAction.h
new file mode 100644
index 0000000..da92901
--- /dev/null
+++ b/Swift/Controllers/Highlighting/HighlightAction.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012 Maciej Niedzielski
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2014-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <boost/archive/text_iarchive.hpp>
+#include <boost/archive/text_oarchive.hpp>
+#include <boost/optional.hpp>
+#include <boost/serialization/optional.hpp>
+
+namespace Swift {
+ class HighlightAction {
+ public:
+ void setFrontColor(const boost::optional<std::string>& frontColor);
+ boost::optional<std::string> getFrontColor() const;
+
+ void setBackColor(const boost::optional<std::string>& backColor);
+ boost::optional<std::string> getBackColor() const;
+
+ void setSoundFilePath(const boost::optional<std::string>& soundFilePath);
+ boost::optional<std::string> getSoundFilePath() const;
+
+ void setSystemNotificationEnabled(bool systemNotificationEnabled);
+ bool isSystemNotificationEnabled() const;
+
+ // @return returns true if the HighlightAction would result in no
+ // noticable action to the user.
+ bool isEmpty() const;
+
+ private:
+ friend class boost::serialization::access;
+ template<class Archive> void serialize(Archive & ar, const unsigned int version);
+
+ private:
+ // Highlight color.
+ boost::optional<std::string> frontColor_;
+ boost::optional<std::string> backColor_;
+
+ // Audio notification.
+ boost::optional<std::string> soundFilePath_;
+
+ // macOS Notification Center or similar.
+ bool systemNotificaitonEnabled_ = false;
+ };
+
+ bool operator ==(const HighlightAction& a, const HighlightAction& b);
+ bool operator !=(const HighlightAction& a, const HighlightAction& b);
+
+ template<class Archive>
+ void HighlightAction::serialize(Archive& ar, const unsigned int /*version*/) {
+ ar & frontColor_;
+ ar & backColor_;
+ ar & soundFilePath_;
+ ar & systemNotificaitonEnabled_;
+ }
+
+}
diff --git a/Swift/Controllers/Highlighting/HighlightConfiguration.cpp b/Swift/Controllers/Highlighting/HighlightConfiguration.cpp
new file mode 100644
index 0000000..e82adb8
--- /dev/null
+++ b/Swift/Controllers/Highlighting/HighlightConfiguration.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/Controllers/Highlighting/HighlightConfiguration.h>
+
+namespace Swift {
+
+bool operator ==(const HighlightConfiguration& a, const HighlightConfiguration& b) {
+ if (a.keywordHighlights != b.keywordHighlights) {
+ return false;
+ }
+ if (a.contactHighlights != b.contactHighlights) {
+ return false;
+ }
+ if (a.ownMentionAction != b.ownMentionAction) {
+ return false;
+ }
+ if (a.playSoundOnIncomingDirectMessages != b.playSoundOnIncomingDirectMessages) {
+ return false;
+ }
+ if (a.showNotificationOnIncomingDirectMessages != b.showNotificationOnIncomingDirectMessages) {
+ return false;
+ }
+ if (a.playSoundOnIncomingGroupchatMessages != b.playSoundOnIncomingGroupchatMessages) {
+ return false;
+ }
+ if (a.showNotificationOnIncomingGroupchatMessages != b.showNotificationOnIncomingGroupchatMessages) {
+ return false;
+ }
+ return true;
+}
+
+}
diff --git a/Swift/Controllers/Highlighting/HighlightConfiguration.h b/Swift/Controllers/Highlighting/HighlightConfiguration.h
new file mode 100644
index 0000000..d262dba
--- /dev/null
+++ b/Swift/Controllers/Highlighting/HighlightConfiguration.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <boost/archive/text_iarchive.hpp>
+#include <boost/archive/text_oarchive.hpp>
+
+#include <Swift/Controllers/Highlighting/HighlightAction.h>
+
+namespace Swift {
+
+class HighlightConfiguration {
+public:
+ class ContactHighlight {
+ public:
+ friend class boost::serialization::access;
+ template<class Archive> void serialize(Archive & ar, const unsigned int version);
+
+ public:
+ std::string name;
+ HighlightAction action;
+ };
+
+ class KeywordHightlight {
+ public:
+ friend class boost::serialization::access;
+ template<class Archive> void serialize(Archive & ar, const unsigned int version);
+
+ public:
+ std::string keyword;
+ bool matchCaseSensitive = false;
+ HighlightAction action;
+ };
+
+ friend class boost::serialization::access;
+ template<class Archive> void serialize(Archive & ar, const unsigned int version);
+
+public:
+ std::vector<KeywordHightlight> keywordHighlights;
+ std::vector<ContactHighlight> contactHighlights;
+ HighlightAction ownMentionAction;
+ bool playSoundOnIncomingDirectMessages = false;
+ bool showNotificationOnIncomingDirectMessages = false;
+ bool playSoundOnIncomingGroupchatMessages = false;
+ bool showNotificationOnIncomingGroupchatMessages = false;
+};
+
+bool operator ==(HighlightConfiguration const& a, HighlightConfiguration const& b);
+
+template<class Archive>
+void HighlightConfiguration::ContactHighlight::serialize(Archive& ar, const unsigned int /*version*/) {
+ ar & name;
+ ar & action;
+}
+
+template<class Archive>
+void HighlightConfiguration::KeywordHightlight::serialize(Archive& ar, const unsigned int /*version*/) {
+ ar & keyword;
+ ar & matchCaseSensitive;
+ ar & action;
+}
+
+template<class Archive>
+void HighlightConfiguration::serialize(Archive& ar, const unsigned int /*version*/) {
+ ar & keywordHighlights;
+ ar & contactHighlights;
+ ar & ownMentionAction;
+ ar & playSoundOnIncomingDirectMessages;
+ ar & showNotificationOnIncomingDirectMessages;
+ ar & playSoundOnIncomingGroupchatMessages;
+ ar & showNotificationOnIncomingGroupchatMessages;
+}
+
+}
diff --git a/Swift/Controllers/Highlighting/HighlightEditorController.cpp b/Swift/Controllers/Highlighting/HighlightEditorController.cpp
new file mode 100644
index 0000000..50da3dc
--- /dev/null
+++ b/Swift/Controllers/Highlighting/HighlightEditorController.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012 Maciej Niedzielski
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2014-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/Controllers/Highlighting/HighlightEditorController.h>
+
+#include <boost/bind.hpp>
+
+#include <Swift/Controllers/ContactSuggester.h>
+#include <Swift/Controllers/UIEvents/RequestHighlightEditorUIEvent.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIInterfaces/HighlightEditorWindow.h>
+#include <Swift/Controllers/UIInterfaces/HighlightEditorWindowFactory.h>
+
+namespace Swift {
+
+HighlightEditorController::HighlightEditorController(UIEventStream* uiEventStream, HighlightEditorWindowFactory* highlightEditorWindowFactory, HighlightManager* highlightManager)
+: highlightEditorWindowFactory_(highlightEditorWindowFactory), highlightEditorWindow_(nullptr), highlightManager_(highlightManager), contactSuggester_(nullptr)
+{
+ uiEventStream->onUIEvent.connect(boost::bind(&HighlightEditorController::handleUIEvent, this, _1));
+}
+
+HighlightEditorController::~HighlightEditorController()
+{
+ delete highlightEditorWindow_;
+ highlightEditorWindow_ = nullptr;
+}
+
+void HighlightEditorController::handleUIEvent(std::shared_ptr<UIEvent> rawEvent)
+{
+ std::shared_ptr<RequestHighlightEditorUIEvent> event = std::dynamic_pointer_cast<RequestHighlightEditorUIEvent>(rawEvent);
+ if (event) {
+ if (!highlightEditorWindow_) {
+ highlightEditorWindow_ = highlightEditorWindowFactory_->createHighlightEditorWindow();
+ highlightEditorWindow_->setHighlightManager(highlightManager_);
+ highlightEditorWindow_->onContactSuggestionsRequested.connect(boost::bind(&HighlightEditorController::handleContactSuggestionsRequested, this, _1));
+ }
+ highlightEditorWindow_->show();
+ }
+}
+
+void HighlightEditorController::handleContactSuggestionsRequested(const std::string& text)
+{
+ if (contactSuggester_) {
+ highlightEditorWindow_->setContactSuggestions(contactSuggester_->getSuggestions(text, true));
+ }
+}
+
+}
diff --git a/Swift/Controllers/Highlighting/HighlightEditorController.h b/Swift/Controllers/Highlighting/HighlightEditorController.h
new file mode 100644
index 0000000..d7608a5
--- /dev/null
+++ b/Swift/Controllers/Highlighting/HighlightEditorController.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012 Maciej Niedzielski
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2014-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
+namespace Swift {
+
+ class UIEventStream;
+
+ class HighlightEditorWindowFactory;
+ class HighlightEditorWindow;
+
+ class HighlightManager;
+ class ContactSuggester;
+
+ class HighlightEditorController {
+ public:
+ HighlightEditorController(UIEventStream* uiEventStream, HighlightEditorWindowFactory* highlightEditorWindowFactory, HighlightManager* highlightManager);
+ ~HighlightEditorController();
+
+ HighlightManager* getHighlightManager() const { return highlightManager_; }
+ void setContactSuggester(ContactSuggester *suggester) { contactSuggester_ = suggester; }
+
+ private:
+ void handleUIEvent(std::shared_ptr<UIEvent> event);
+ void handleContactSuggestionsRequested(const std::string& text);
+
+ private:
+ HighlightEditorWindowFactory* highlightEditorWindowFactory_;
+ HighlightEditorWindow* highlightEditorWindow_;
+ HighlightManager* highlightManager_;
+ ContactSuggester* contactSuggester_;
+ };
+
+}
diff --git a/Swift/Controllers/Highlighting/HighlightManager.cpp b/Swift/Controllers/Highlighting/HighlightManager.cpp
new file mode 100644
index 0000000..89261af
--- /dev/null
+++ b/Swift/Controllers/Highlighting/HighlightManager.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2012 Maciej Niedzielski
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2014-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/Controllers/Highlighting/HighlightManager.h>
+
+#include <cassert>
+#include <sstream>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/archive/text_iarchive.hpp>
+#include <boost/archive/text_oarchive.hpp>
+#include <boost/bind.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+#include <boost/regex.hpp>
+#include <boost/serialization/vector.hpp>
+
+#include <Swiften/Base/Log.h>
+
+#include <Swift/Controllers/Highlighting/HighlightConfiguration.h>
+#include <Swift/Controllers/Highlighting/Highlighter.h>
+#include <Swift/Controllers/SettingConstants.h>
+#include <Swift/Controllers/Settings/SettingsProvider.h>
+
+namespace Swift {
+
+HighlightManager::HighlightManager(SettingsProvider* settings)
+ : settings_(settings)
+ , storingSettings_(false) {
+ highlightConfiguration_ = std::make_shared<HighlightConfiguration>();
+ loadSettings();
+ handleSettingChangedConnection_ = settings_->onSettingChanged.connect(boost::bind(&HighlightManager::handleSettingChanged, this, _1));
+}
+
+void HighlightManager::handleSettingChanged(const std::string& settingPath) {
+ if (!storingSettings_ && SettingConstants::HIGHLIGHT_RULES.getKey() == settingPath) {
+ loadSettings();
+ }
+}
+
+HighlightConfiguration HighlightManager::getDefaultConfig() {
+ HighlightConfiguration defaultConfiguration;
+ defaultConfiguration.playSoundOnIncomingDirectMessages = true;
+ defaultConfiguration.showNotificationOnIncomingDirectMessages = true;
+ defaultConfiguration.ownMentionAction.setFrontColor(std::string("black"));
+ defaultConfiguration.ownMentionAction.setBackColor(std::string("yellow"));
+ defaultConfiguration.ownMentionAction.setSoundFilePath(std::string("/sounds/message-received.wav"));
+ defaultConfiguration.ownMentionAction.setSystemNotificationEnabled(true);
+ return defaultConfiguration;
+}
+
+void HighlightManager::storeSettings() {
+ storingSettings_ = true; // don't reload settings while saving
+ settings_->storeSetting(SettingConstants::HIGHLIGHT_RULES_V2, highlightConfigurationToString(*highlightConfiguration_));
+ storingSettings_ = false;
+}
+
+void HighlightManager::loadSettings() {
+ std::string configString = settings_->getSetting(SettingConstants::HIGHLIGHT_RULES_V2);
+ *highlightConfiguration_ = highlightConfigurationFromString(configString);
+}
+
+Highlighter* HighlightManager::createHighlighter(NickResolver* nickResolver) {
+ return new Highlighter(this, nickResolver);
+}
+
+void HighlightManager::resetToDefaultConfiguration() {
+ *highlightConfiguration_ = getDefaultConfig();
+}
+
+HighlightConfiguration HighlightManager::highlightConfigurationFromString(const std::string& dataString) {
+ std::stringstream stream;
+ stream << dataString;
+
+ HighlightConfiguration configuration;
+ try {
+ boost::archive::text_iarchive archive(stream);
+ archive >> configuration;
+ }
+ catch (boost::archive::archive_exception&) {
+ configuration = getDefaultConfig();
+ SWIFT_LOG(warning) << "Failed to load highlight configuration. Will use default configuration instead." << std::endl;
+ }
+ return configuration;
+}
+
+std::string HighlightManager::highlightConfigurationToString(const HighlightConfiguration& configuration) {
+ std::stringstream stream;
+ boost::archive::text_oarchive archive(stream);
+ archive & configuration;
+ return stream.str();
+}
+
+}
diff --git a/Swift/Controllers/Highlighting/HighlightManager.h b/Swift/Controllers/Highlighting/HighlightManager.h
new file mode 100644
index 0000000..13f59fa
--- /dev/null
+++ b/Swift/Controllers/Highlighting/HighlightManager.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2012 Maciej Niedzielski
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2014-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include <boost/signals2.hpp>
+
+#include <Swift/Controllers/Highlighting/HighlightConfiguration.h>
+
+namespace Swift {
+
+ class NickResolver;
+ class SettingsProvider;
+ class Highlighter;
+
+ class HighlightManager {
+ public:
+ HighlightManager(SettingsProvider* settings);
+
+ Highlighter* createHighlighter(NickResolver* nickResolver);
+
+ std::shared_ptr<HighlightConfiguration> getConfiguration() const {
+ return highlightConfiguration_;
+ }
+
+ void setConfiguration(const HighlightConfiguration& config) {
+ *highlightConfiguration_ = config;
+ storeSettings();
+ }
+
+ void resetToDefaultConfiguration();
+
+ void storeSettings();
+ void loadSettings();
+
+ boost::signals2::signal<void (const HighlightAction&)> onHighlight;
+
+ private:
+ void handleSettingChanged(const std::string& settingPath);
+
+ static HighlightConfiguration getDefaultConfig();
+
+ static HighlightConfiguration highlightConfigurationFromString(const std::string& dataString);
+ static std::string highlightConfigurationToString(const HighlightConfiguration& configuration);
+
+ private:
+ SettingsProvider* settings_;
+ bool storingSettings_;
+ std::shared_ptr<HighlightConfiguration> highlightConfiguration_;
+ boost::signals2::scoped_connection handleSettingChangedConnection_;
+ };
+}
diff --git a/Swift/Controllers/Highlighting/Highlighter.cpp b/Swift/Controllers/Highlighting/Highlighter.cpp
new file mode 100644
index 0000000..b05de2d
--- /dev/null
+++ b/Swift/Controllers/Highlighting/Highlighter.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2012 Maciej Niedzielski
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2014-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swift/Controllers/Highlighting/Highlighter.h>
+
+#include <set>
+#include <string>
+
+#include <Swiften/Base/String.h>
+#include <Swiften/Base/format.h>
+#include <Swiften/Client/NickResolver.h>
+
+#include <Swift/Controllers/Highlighting/HighlightManager.h>
+#include <Swift/Controllers/Intl.h>
+#include <Swift/Controllers/XMPPEvents/MessageEvent.h>
+
+namespace Swift {
+
+Highlighter::Highlighter(HighlightManager* manager, NickResolver* nickResolver)
+ : manager_(manager), nickResolver_(nickResolver) {
+}
+
+void Highlighter::handleSystemNotifications(const ChatWindow::ChatMessage& message, std::shared_ptr<MessageEvent> event) {
+ if (std::shared_ptr<MessageEvent> messageEvent = std::dynamic_pointer_cast<MessageEvent>(event)) {
+ JID jid = messageEvent->getStanza()->getFrom();
+ std::string nickname = nickResolver_->jidToNick(jid);
+
+ std::string messageText = messageEvent->getStanza()->getBody().get_value_or("");
+ if (boost::starts_with(messageText, "/me ")) {
+ messageText = "*" + String::getSplittedAtFirst(messageText, ' ').second + "*";
+ }
+
+ if (message.getHighlightActionDirectMessage().isSystemNotificationEnabled()) {
+ // title: Romeo says
+ // message: message
+ std::string title = str(format(QT_TRANSLATE_NOOP("", "%1% says")) % nickname);
+ event->addNotification(title, messageText);
+ }
+ if (message.getHighlightActionGroupMessage().isSystemNotificationEnabled()) {
+ // title: Romeo in $roomJID says
+ // message: message
+ std::string roomName = jid.getNode();
+ std::string title = str(format(QT_TRANSLATE_NOOP("", "%1% in %2% says")) % nickname % roomName);
+ event->addNotification(title, messageText);
+ }
+ if (message.getHighlightActionOwnMention().isSystemNotificationEnabled()) {
+ // title: Romeo mentioned you in $roomJID
+ // message: message
+ std::string roomName = jid.getNode();
+ std::string title = str(format(QT_TRANSLATE_NOOP("", "%1% mentioned you in %2%")) % nickname % roomName);
+ event->addNotification(title, messageText);
+ }
+ if (message.getHighlightActionSender().isSystemNotificationEnabled()) {
+ // title: Romeo says
+ // message: message
+ auto title = str(format(QT_TRANSLATE_NOOP("", "%1% says")) % nickname);
+ event->addNotification(title, messageText);
+ }
+ for (auto&& part : message.getParts()) {
+ auto highlightPart = std::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(part);
+ if (highlightPart && highlightPart->action.isSystemNotificationEnabled()) {
+ // title: Romeo mentioned '$keyword'
+ // message: message
+ auto title = str(format(QT_TRANSLATE_NOOP("", "%1% mentioned '%2%'")) % nickname % highlightPart->text);
+ event->addNotification(title, messageText);
+ }
+ }
+ }
+}
+
+void Highlighter::handleSoundNotifications(const ChatWindow::ChatMessage& chatMessage) {
+ std::set<std::string> playedSoundPaths;
+ std::vector<HighlightAction> actionsToPlay;
+
+ // collect unique sounds to play
+ auto checkSoundActionAndQueueUnique = [&](const HighlightAction& action) {
+ if (action.getSoundFilePath()) {
+ auto soundFilePath = action.getSoundFilePath().get_value_or("");
+ if (playedSoundPaths.find(soundFilePath) == playedSoundPaths.end()) {
+ playedSoundPaths.insert(soundFilePath);
+ actionsToPlay.push_back(action);
+ }
+ }
+ };
+
+ for (auto&& part : chatMessage.getParts()) {
+ auto highlightMessage = std::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(part);
+ if (highlightMessage) {
+ checkSoundActionAndQueueUnique(highlightMessage->action);
+ }
+ }
+
+ checkSoundActionAndQueueUnique(chatMessage.getHighlightActionSender());
+ checkSoundActionAndQueueUnique(chatMessage.getHighlightActionOwnMention());
+ checkSoundActionAndQueueUnique(chatMessage.getHighlightActionDirectMessage());
+ checkSoundActionAndQueueUnique(chatMessage.getHighlightActionGroupMessage());
+
+ // play sounds
+ for (const auto& action : actionsToPlay) {
+ manager_->onHighlight(action);
+ }
+}
+
+}
diff --git a/Swift/Controllers/Highlighting/Highlighter.h b/Swift/Controllers/Highlighting/Highlighter.h
new file mode 100644
index 0000000..51308a1
--- /dev/null
+++ b/Swift/Controllers/Highlighting/Highlighter.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012 Maciej Niedzielski
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2016-2017 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <Swift/Controllers/Highlighting/HighlightConfiguration.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <Swift/Controllers/XMPPEvents/MessageEvent.h>
+
+namespace Swift {
+
+ class HighlightManager;
+ class NickResolver;
+
+ class Highlighter {
+ public:
+ Highlighter(HighlightManager* manager, NickResolver* nickResolver);
+
+ void handleSystemNotifications(const ChatWindow::ChatMessage& message, std::shared_ptr<MessageEvent> event);
+ void handleSoundNotifications(const ChatWindow::ChatMessage& chatMessage);
+
+ private:
+ HighlightManager* manager_;
+ NickResolver* nickResolver_;
+ };
+
+}