summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swift/Controllers')
-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
-rw-r--r--Swift/Controllers/EventNotifier.cpp9
-rw-r--r--Swift/Controllers/HighlightAction.cpp62
-rw-r--r--Swift/Controllers/HighlightAction.h82
-rw-r--r--Swift/Controllers/HighlightManager.cpp148
-rw-r--r--Swift/Controllers/HighlightManager.h81
-rw-r--r--Swift/Controllers/HighlightRule.cpp200
-rw-r--r--Swift/Controllers/HighlightRule.h103
-rw-r--r--Swift/Controllers/Highlighter.cpp51
-rw-r--r--Swift/Controllers/Highlighter.h45
-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.cpp (renamed from Swift/Controllers/HighlightEditorController.cpp)4
-rw-r--r--Swift/Controllers/Highlighting/HighlightEditorController.h (renamed from Swift/Controllers/HighlightEditorController.h)2
-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
-rw-r--r--Swift/Controllers/MainController.cpp6
-rw-r--r--Swift/Controllers/SConscript110
-rw-r--r--Swift/Controllers/SettingConstants.cpp3
-rw-r--r--Swift/Controllers/SettingConstants.h9
-rw-r--r--Swift/Controllers/SoundEventController.cpp8
-rw-r--r--Swift/Controllers/SoundEventController.h7
-rw-r--r--Swift/Controllers/UIInterfaces/ChatWindow.h41
-rw-r--r--Swift/Controllers/UnitTest/HighlightRuleTest.cpp324
-rw-r--r--Swift/Controllers/XMPPEvents/MessageEvent.h22
39 files changed, 1185 insertions, 1587 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,84 +1,84 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/Chat/ChatController.h>
#include <memory>
#include <boost/bind.hpp>
#include <Swiften/Avatars/AvatarManager.h>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/format.h>
#include <Swiften/Chat/ChatStateNotifier.h>
#include <Swiften/Chat/ChatStateTracker.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Disco/EntityCapsProvider.h>
#include <Swiften/Disco/FeatureOracle.h>
#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Elements/DeliveryReceiptRequest.h>
#include <Swiften/Elements/Idle.h>
#include <Swiften/FileTransfer/FileTransferManager.h>
#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>
#include <Swift/Controllers/Translator.h>
#include <Swift/Controllers/UIEvents/AcceptWhiteboardSessionUIEvent.h>
#include <Swift/Controllers/UIEvents/CancelWhiteboardSessionUIEvent.h>
#include <Swift/Controllers/UIEvents/InviteToMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestInviteToMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/XMPPEvents/IncomingFileTransferEvent.h>
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);
chatStateTracker_ = new ChatStateTracker();
nickResolver_ = nickResolver;
presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1));
chatStateTracker_->onChatStateChange.connect(boost::bind(&ChatWindow::setContactChatState, chatWindow_, _1));
stanzaChannel_->onStanzaAcked.connect(boost::bind(&ChatController::handleStanzaAcked, this, _1));
nickResolver_->onNickChanged.connect(boost::bind(&ChatController::handleContactNickChanged, this, _1, _2));
std::string nick = nickResolver_->jidToNick(toJID_);
chatWindow_->setName(nick);
std::string startMessage;
Presence::ref theirPresence;
if (isInMUC) {
startMessage = str(format(QT_TRANSLATE_NOOP("", "Starting chat with %1% in chatroom %2%")) % nick % contact.toBare().toString());
theirPresence = presenceOracle->getLastPresence(contact);
} else {
startMessage = str(format(QT_TRANSLATE_NOOP("", "Starting chat with %1% - %2%")) % nick % contact.toBare().toString());
theirPresence = contact.isBare() ? presenceOracle->getAccountPresence(contact) : presenceOracle->getLastPresence(contact);
}
Idle::ref idle;
if (theirPresence && (idle = theirPresence->getPayload<Idle>())) {
startMessage += str(format(QT_TRANSLATE_NOOP("", ", who has been idle since %1%")) % Swift::Translator::getInstance()->ptimeToHumanReadableString(idle->getSince()));
}
startMessage += ": " + statusShowTypeToFriendlyName(theirPresence ? theirPresence->getShow() : StatusShow::None);
if (theirPresence && !theirPresence->getStatus().empty()) {
startMessage += " (" + theirPresence->getStatus() + ")";
}
lastShownStatus_ = theirPresence ? theirPresence->getShow() : StatusShow::None;
chatStateNotifier_->setContactIsOnline(theirPresence && theirPresence->getType() == Presence::Available);
@@ -183,63 +183,64 @@ void ChatController::preHandleIncomingMessage(std::shared_ptr<MessageEvent> mess
}
chatStateTracker_->handleMessageReceived(message);
chatStateNotifier_->receivedMessageFromContact(!!message->getPayload<ChatState>());
// handle XEP-0184 Message Receipts
// incomming receipts
if (std::shared_ptr<DeliveryReceipt> receipt = message->getPayload<DeliveryReceipt>()) {
SWIFT_LOG(debug) << "received receipt for id: " << receipt->getReceivedID() << std::endl;
if (requestedReceipts_.find(receipt->getReceivedID()) != requestedReceipts_.end()) {
chatWindow_->setMessageReceiptState(requestedReceipts_[receipt->getReceivedID()], ChatWindow::ReceiptReceived);
requestedReceipts_.erase(receipt->getReceivedID());
}
// incomming errors in response to send out receipts
} else if (message->getPayload<DeliveryReceiptRequest>() && (message->getType() == Message::Error)) {
if (requestedReceipts_.find(message->getID()) != requestedReceipts_.end()) {
chatWindow_->setMessageReceiptState(requestedReceipts_[message->getID()], ChatWindow::ReceiptFailed);
requestedReceipts_.erase(message->getID());
}
// incoming receipt requests
} else if (message->getPayload<DeliveryReceiptRequest>()) {
if (receivingPresenceFromUs_) {
std::shared_ptr<Message> receiptMessage = std::make_shared<Message>();
receiptMessage->setTo(toJID_);
receiptMessage->addPayload(std::make_shared<DeliveryReceipt>(message->getID()));
stanzaChannel_->sendMessage(receiptMessage);
}
}
}
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);
}
}
void ChatController::preSendMessageRequest(std::shared_ptr<Message> message) {
chatStateNotifier_->addChatStateRequest(message);
if (userWantsReceipts_ && (contactSupportsReceipts_ != No) && message) {
message->addPayload(std::make_shared<DeliveryReceiptRequest>());
}
}
void ChatController::setContactIsReceivingPresence(bool isReceivingPresence) {
receivingPresenceFromUs_ = isReceivingPresence;
}
void ChatController::handleSettingChanged(const std::string& settingPath) {
if (settingPath == SettingConstants::REQUEST_DELIVERYRECEIPTS.getKey()) {
userWantsReceipts_ = settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS);
checkForDisplayingDisplayReceiptsAlert();
}
}
void ChatController::checkForDisplayingDisplayReceiptsAlert() {
boost::optional<ChatWindow::AlertID> newDeliverReceiptAlert;
if (userWantsReceipts_ && (contactSupportsReceipts_ == No)) {
newDeliverReceiptAlert = chatWindow_->addAlert(QT_TRANSLATE_NOOP("", "This chat doesn't support delivery receipts."));
} else if (userWantsReceipts_ && (contactSupportsReceipts_ == Maybe)) {
newDeliverReceiptAlert = chatWindow_->addAlert(QT_TRANSLATE_NOOP("", "This chat may not support delivery receipts. You might not receive delivery receipts for the messages you send."));
} else {
if (deliveryReceiptAlert_) {
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,77 +1,77 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/Chat/ChatControllerBase.h>
#include <map>
#include <memory>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include <boost/bind.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <Swiften/Avatars/AvatarManager.h>
#include <Swiften/Base/Path.h>
#include <Swiften/Base/format.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Disco/EntityCapsProvider.h>
#include <Swiften/Elements/Delay.h>
#include <Swiften/Elements/MUCInvitationPayload.h>
#include <Swiften/Elements/MUCUserPayload.h>
#include <Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h>
#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>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h>
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();
}
ChatControllerBase::~ChatControllerBase() {
if (dateChangeTimer_) {
dateChangeTimer_->onTick.disconnect(boost::bind(&ChatControllerBase::handleDayChangeTick, this));
dateChangeTimer_->stop();
}
delete highlighter_;
delete chatWindow_;
}
void ChatControllerBase::handleLogCleared() {
cancelReplaces();
}
ChatWindow* ChatControllerBase::detachChatWindow() {
ChatWindow* chatWindow = chatWindow_;
chatWindow_ = nullptr;
return chatWindow;
}
void ChatControllerBase::handleCapsChanged(const JID& jid) {
if (jid.compare(toJID_, JID::WithoutResource) == 0) {
handleBareJIDCapsChanged(jid);
}
}
@@ -177,84 +177,66 @@ void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool
}
void ChatControllerBase::handleSecurityLabelsCatalogResponse(std::shared_ptr<SecurityLabelsCatalog> catalog, ErrorPayload::ref error) {
if (catalog && !error) {
if (catalog->getItems().size() == 0) {
chatWindow_->setSecurityLabelsEnabled(false);
labelsEnabled_ = false;
} else {
labelsEnabled_ = true;
chatWindow_->setAvailableSecurityLabels(catalog->getItems());
chatWindow_->setSecurityLabelsEnabled(true);
}
} else {
labelsEnabled_ = false;
chatWindow_->setSecurityLabelsError();
}
}
void ChatControllerBase::showChatWindow() {
chatWindow_->show();
}
void ChatControllerBase::activateChatWindow() {
chatWindow_->activate();
}
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();
}
std::string ChatControllerBase::addMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& senderName, bool senderIsSelf, const std::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time) {
if (chatMessage.isMeCommand()) {
return chatWindow_->addAction(chatMessage, senderName, senderIsSelf, label, pathToString(avatarPath), time);
}
else {
return chatWindow_->addMessage(chatMessage, senderName, senderIsSelf, label, pathToString(avatarPath), time);
}
}
void ChatControllerBase::replaceMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& id, const boost::posix_time::ptime& time) {
if (chatMessage.isMeCommand()) {
chatWindow_->replaceWithAction(chatMessage, id, time);
}
else {
chatWindow_->replaceMessage(chatMessage, id, time);
}
}
bool ChatControllerBase::isFromContact(const JID& from) {
return from.toBare() == toJID_.toBare();
}
void ChatControllerBase::handleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent) {
preHandleIncomingMessage(messageEvent);
if (messageEvent->isReadable() && !messageEvent->getConcluded()) {
@@ -287,79 +269,73 @@ void ChatControllerBase::handleIncomingMessage(std::shared_ptr<MessageEvent> mes
else if (messageEvent->getStanza()->getPayload<MUCUserPayload>() && messageEvent->getStanza()->getPayload<MUCUserPayload>()->getInvite()) {
handleMediatedMUCInvitation(messageEvent->getStanza());
return;
}
else {
if (!messageEvent->isReadable()) {
return;
}
showChatWindow();
JID from = message->getFrom();
std::vector<std::shared_ptr<Delay> > delayPayloads = message->getPayloads<Delay>();
for (size_t i = 0; useDelayForLatency_ && i < delayPayloads.size(); i++) {
if (!delayPayloads[i]->getFrom()) {
continue;
}
boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
std::ostringstream s;
s << "The following message took " << (now - delayPayloads[i]->getStamp()).total_milliseconds() / 1000.0 << " seconds to be delivered from " << delayPayloads[i]->getFrom()->toString() << ".";
chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(std::string(s.str())), ChatWindow::DefaultDirection);
}
std::shared_ptr<SecurityLabel> label = message->getPayload<SecurityLabel>();
// Determine the timestamp
boost::posix_time::ptime timeStamp = boost::posix_time::microsec_clock::universal_time();
boost::optional<boost::posix_time::ptime> messageTimeStamp = getMessageTimestamp(message);
if (messageTimeStamp) {
timeStamp = *messageTimeStamp;
}
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) {
// Should check if the user has a previous message
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);
}
logMessage(body, from, selfJID_, timeStamp, true);
}
chatWindow_->show();
updateMessageCount();
postHandleIncomingMessage(messageEvent, chatMessage);
}
void ChatControllerBase::addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& timeStamp) {
lastMessagesUIID_[from] = addMessage(message, senderDisplayNameFromMessage(from), senderIsSelf, label, avatarManager_->getAvatarPath(from), timeStamp);
}
std::string ChatControllerBase::getErrorMessage(std::shared_ptr<ErrorPayload> error) {
std::string defaultMessage = QT_TRANSLATE_NOOP("", "Error sending message");
if (!error->getText().empty()) {
return error->getText();
}
else {
switch (error->getCondition()) {
case ErrorPayload::BadRequest: return QT_TRANSLATE_NOOP("", "Bad request");
case ErrorPayload::Conflict: return QT_TRANSLATE_NOOP("", "Conflict");
case ErrorPayload::FeatureNotImplemented: return QT_TRANSLATE_NOOP("", "This feature is not implemented");
case ErrorPayload::Forbidden: return QT_TRANSLATE_NOOP("", "Forbidden");
case ErrorPayload::Gone: return QT_TRANSLATE_NOOP("", "Recipient can no longer be contacted");
case ErrorPayload::InternalServerError: return QT_TRANSLATE_NOOP("", "Internal server error");
case ErrorPayload::ItemNotFound: return QT_TRANSLATE_NOOP("", "Item not found");
case ErrorPayload::JIDMalformed: return QT_TRANSLATE_NOOP("", "JID Malformed");
case ErrorPayload::NotAcceptable: return QT_TRANSLATE_NOOP("", "Message was rejected");
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,130 +1,130 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/optional.hpp>
#include <boost/signals2.hpp>
#include <Swiften/Base/IDGenerator.h>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/Elements/ErrorPayload.h>
#include <Swiften/Elements/SecurityLabelsCatalog.h>
#include <Swiften/Elements/Stanza.h>
#include <Swiften/JID/JID.h>
#include <Swiften/MUC/MUCRegistry.h>
#include <Swiften/Network/Timer.h>
#include <Swiften/Network/TimerFactory.h>
#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:
virtual ~ChatControllerBase();
void showChatWindow();
void activateChatWindow();
bool hasOpenWindow() const;
virtual void setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info);
virtual void handleIncomingOwnMessage(std::shared_ptr<Message> /*message*/) {}
void handleIncomingMessage(std::shared_ptr<MessageEvent> message);
std::string addMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& senderName, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time);
void replaceMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& id, const boost::posix_time::ptime& time);
virtual void setOnline(bool online);
void setEnabled(bool enabled);
virtual void setToJID(const JID& jid) {toJID_ = jid;}
/** Used for determining when something is recent.*/
boost::signals2::signal<void (const std::string& /*activity*/)> onActivity;
boost::signals2::signal<void ()> onUnreadCountChanged;
boost::signals2::signal<void ()> onWindowClosed;
int getUnreadCount();
const JID& getToJID() {return toJID_;}
void handleCapsChanged(const JID& jid);
void setCanStartImpromptuChats(bool supportsImpromptu);
virtual ChatWindow* detachChatWindow();
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.
*/
virtual void postSendMessage(const std::string&, std::shared_ptr<Stanza>) {}
virtual std::string senderDisplayNameFromMessage(const JID& from) = 0;
virtual std::string senderHighlightNameFromMessage(const JID& from) = 0;
virtual bool isIncomingMessageFromMe(std::shared_ptr<Message>) = 0;
virtual void preHandleIncomingMessage(std::shared_ptr<MessageEvent>) {}
virtual void addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time);
virtual void postHandleIncomingMessage(std::shared_ptr<MessageEvent>, const ChatWindow::ChatMessage&) {}
virtual void preSendMessageRequest(std::shared_ptr<Message>) {}
virtual bool isFromContact(const JID& from);
virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(std::shared_ptr<Message>) const = 0;
virtual void dayTicked() {}
virtual void handleBareJIDCapsChanged(const JID& jid) = 0;
std::string getErrorMessage(std::shared_ptr<ErrorPayload>);
virtual void setContactIsReceivingPresence(bool /* isReceivingPresence */) {}
virtual void cancelReplaces() = 0;
/** 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:
IDGenerator idGenerator_;
std::string lastSentMessageStanzaID_;
void createDayChangeTimer();
void handleSendMessageRequest(const std::string &body, bool isCorrectionMessage);
void handleAllMessagesRead();
void handleSecurityLabelsCatalogResponse(std::shared_ptr<SecurityLabelsCatalog>, ErrorPayload::ref error);
void handleDayChangeTick();
void handleMUCInvitation(Message::ref message);
void handleMediatedMUCInvitation(Message::ref message);
void handleGeneralMUCInvitation(MUCInviteEvent::ref event);
void handleLogCleared();
protected:
JID selfJID_;
std::vector<std::shared_ptr<StanzaEvent> > unreadMessages_;
std::vector<std::shared_ptr<StanzaEvent> > targetedUnreadMessages_;
StanzaChannel* stanzaChannel_;
IQRouter* iqRouter_;
ChatWindowFactory* chatWindowFactory_;
ChatWindow* chatWindow_;
JID toJID_;
bool labelsEnabled_;
std::map<JID, std::string> lastMessagesUIID_;
PresenceOracle* presenceOracle_;
AvatarManager* avatarManager_;
bool useDelayForLatency_;
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,197 +1,264 @@
/*
- * 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>
#include <SwifTools/Linkify.h>
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;
parsedMessage.setIsMeCommand(true);
}
/* Parse one, URLs */
while (!remaining.empty()) {
bool found = false;
std::pair<std::vector<std::string>, size_t> links = Linkify::splitLink(remaining);
remaining = "";
for (size_t i = 0; i < links.first.size(); i++) {
const std::string& part = links.first[i];
if (found) {
// Must be on the last part, then
remaining = part;
}
else {
if (i == links.second) {
found = true;
parsedMessage.append(std::make_shared<ChatWindow::ChatURIMessagePart>(part));
}
else {
parsedMessage.append(std::make_shared<ChatWindow::ChatTextMessagePart>(part));
}
}
}
}
/* do emoticon substitution */
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;
}
ChatWindow::ChatMessage ChatMessageParser::emoticonHighlight(const ChatWindow::ChatMessage& message) {
ChatWindow::ChatMessage parsedMessage = message;
std::string regexString;
/* Parse two, emoticons */
for (StringPair emoticon : emoticons_) {
/* Construct a regexp that finds an instance of any of the emoticons inside a group
* at the start or end of the line, or beside whitespace.
*/
regexString += regexString.empty() ? "" : "|";
std::string escaped = "(" + Regex::escape(emoticon.first) + ")";
regexString += "^" + escaped + "|";
regexString += escaped + "$|";
regexString += "\\s" + escaped + "|";
regexString += escaped + "\\s";
}
if (!regexString.empty()) {
regexString += "";
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 {
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, emoticonRegex)) {
int matchIndex = 0;
for (matchIndex = 1; matchIndex < static_cast<int>(match.size()); matchIndex++) {
if (match[matchIndex].length() > 0) {
//This is the matching subgroup
break;
}
}
std::string::const_iterator matchStart = match[matchIndex].first;
std::string::const_iterator matchEnd = match[matchIndex].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::ChatEmoticonMessagePart> emoticonPart = std::make_shared<ChatWindow::ChatEmoticonMessagePart>();
std::string matchString = match[matchIndex].str();
std::map<std::string, std::string>::const_iterator emoticonIterator = emoticons_.find(matchString);
assert (emoticonIterator != emoticons_.end());
const StringPair& emoticon = *emoticonIterator;
emoticonPart->imagePath = emoticon.second;
emoticonPart->alternativeText = emoticon.first;
newMessage.append(emoticonPart);
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())));
}
}
catch (std::runtime_error) {
/* Basically too expensive to compute the regex results and it gave up, so pass through as text */
newMessage.append(part);
}
}
else {
newMessage.append(part);
}
}
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,32 +1,32 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/Chat/ChatsManager.h>
#include <memory>
#include <boost/algorithm/string.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/optional.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <Swiften/Avatars/AvatarManager.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Disco/DiscoServiceWalker.h>
#include <Swiften/Disco/FeatureOracle.h>
#include <Swiften/Elements/CarbonsReceived.h>
#include <Swiften/Elements/CarbonsSent.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Elements/DeliveryReceiptRequest.h>
@@ -719,61 +719,61 @@ void ChatsManager::setOnline(bool enabled) {
localMUCServiceFinderWalker_->onWalkAborted.connect(boost::bind(&ChatsManager::handleLocalServiceWalkFinished, this));
localMUCServiceFinderWalker_->onWalkComplete.connect(boost::bind(&ChatsManager::handleLocalServiceWalkFinished, this));
localMUCServiceFinderWalker_->beginWalk();
}
if (chatListWindow_) {
chatListWindow_->setBookmarksEnabled(enabled);
}
}
void ChatsManager::handleChatRequest(const std::string &contact) {
ChatController* controller = getChatControllerOrFindAnother(JID(contact));
controller->activateChatWindow();
}
ChatController* ChatsManager::getChatControllerOrFindAnother(const JID &contact) {
ChatController* controller = getChatControllerIfExists(contact);
if (!controller && !mucRegistry_->isMUC(contact.toBare())) {
for (JIDChatControllerPair pair : chatControllers_) {
if (pair.first.toBare() == contact.toBare()) {
controller = pair.second;
break;
}
}
}
return controller ? controller : createNewChatController(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_);
controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1, false));
controller->onWindowClosed.connect(boost::bind(&ChatsManager::handleChatClosed, this, contact));
controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller));
controller->onConvertToMUC.connect(boost::bind(&ChatsManager::handleTransformChatToMUC, this, controller, _1, _2, _3));
updatePresenceReceivingStateOnChatController(contact);
controller->setCanStartImpromptuChats(!localMUCServiceJID_.toString().empty());
return controller;
}
ChatController* ChatsManager::getChatControllerOrCreate(const JID &contact) {
ChatController* controller = getChatControllerIfExists(contact);
return controller ? controller : createNewChatController(contact);
}
ChatController* ChatsManager::getChatControllerIfExists(const JID &contact, bool rebindIfNeeded) {
if (chatControllers_.find(contact) == chatControllers_.end()) {
if (mucRegistry_->isMUC(contact.toBare())) {
return nullptr;
}
//Need to look for an unbound window to bind first
JID bare(contact.toBare());
if (chatControllers_.find(bare) != chatControllers_.end()) {
if (rebindIfNeeded) {
rebindControllerJID(bare, contact);
}
else {
return chatControllers_[bare];
@@ -808,62 +808,62 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti
bookmark.setAutojoin(true);
if (nickMaybe) {
bookmark.setNick(*nickMaybe);
}
if (password) {
bookmark.setPassword(*password);
}
mucBookmarkManager_->addBookmark(bookmark);
}
std::map<JID, MUCController*>::iterator it = mucControllers_.find(mucJID);
if (it != mucControllers_.end()) {
if (stanzaChannel_->isAvailable()) {
it->second->rejoin();
}
} else {
std::string nick = (nickMaybe && !(*nickMaybe).empty()) ? nickMaybe.get() : nickResolver_->jidToNick(jid_);
muc = mucManager->createMUC(mucJID);
if (createAsReservedIfNew) {
muc->setCreateAsReservedIfNew();
}
if (isImpromptu) {
muc->setCreateAsReservedIfNew();
}
MUCController* controller = nullptr;
SingleChatWindowFactoryAdapter* chatWindowFactoryAdapter = nullptr;
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
* are also deleted there.*/
chatWindowFactoryAdapters_[controller] = chatWindowFactoryAdapter;
}
mucControllers_[mucJID] = controller;
controller->setAvailableServerFeatures(serverDiscoInfo_);
controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller));
controller->onUserJoined.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), "", true));
controller->onUserNicknameChanged.connect(boost::bind(&ChatsManager::handleUserNicknameChanged, this, controller, _1, _2));
controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), _1, true));
controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller));
if (!stanzaChannel_->isAvailable()) {
/* When online, the MUC is added to the registry in MUCImpl::internalJoin. This method is not
* called when Swift is offline, so we add it here as only MUCs in the registry are rejoined
* when going back online.
*/
mucRegistry_->addMUC(mucJID.toBare());
}
handleChatActivity(mucJID.toBare(), "", true);
}
mucControllers_[mucJID]->showChatWindow();
return muc;
}
void ChatsManager::handleSearchMUCRequest() {
mucSearchController_->openSearchWindow();
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
@@ -7,162 +7,162 @@
#include <Swift/Controllers/Chat/MUCController.h>
#include <algorithm>
#include <memory>
#include <boost/bind.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <Swiften/Avatars/AvatarManager.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/format.h>
#include <Swiften/Base/Tristate.h>
#include <Swiften/Client/BlockList.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Disco/EntityCapsProvider.h>
#include <Swiften/Elements/Delay.h>
#include <Swiften/Elements/Thread.h>
#include <Swiften/MUC/MUC.h>
#include <Swiften/MUC/MUCBookmark.h>
#include <Swiften/MUC/MUCBookmarkManager.h>
#include <Swiften/Network/Timer.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/Roster/XMPPRoster.h>
#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>
#include <Swift/Controllers/Roster/ItemOperations/SetAvatar.h>
#include <Swift/Controllers/Roster/ItemOperations/SetMUC.h>
#include <Swift/Controllers/Roster/ItemOperations/SetPresence.h>
#include <Swift/Controllers/Roster/Roster.h>
#include <Swift/Controllers/Roster/RosterVCardProvider.h>
#include <Swift/Controllers/UIEvents/InviteToMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestInviteToMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#define MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS 60000
namespace Swift {
class MUCBookmarkPredicate {
public:
MUCBookmarkPredicate(const JID& mucJID) : roomJID_(mucJID) { }
bool operator()(const MUCBookmark& operand) {
return operand.getRoom() == roomJID_;
}
private:
JID roomJID_;
};
/**
* The controller does not gain ownership of the stanzaChannel, nor the factory.
*/
MUCController::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* uiEventStream,
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) :
- 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;
shouldJoinOnReconnect_ = true;
doneGettingHistory_ = false;
xmppRoster_ = xmppRoster;
subject_ = "";
isInitialJoin_ = true;
roster_ = std::unique_ptr<Roster>(new Roster(false, true));
rosterVCardProvider_ = new RosterVCardProvider(roster_.get(), vcardManager, JID::WithResource);
completer_ = new TabComplete();
chatWindow_->setRosterModel(roster_.get());
chatWindow_->setTabComplete(completer_);
chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this));
chatWindow_->onOccupantSelectionChanged.connect(boost::bind(&MUCController::handleWindowOccupantSelectionChanged, this, _1));
chatWindow_->onOccupantActionSelected.connect(boost::bind(&MUCController::handleActionRequestedOnOccupant, this, _1, _2));
chatWindow_->onChangeSubjectRequest.connect(boost::bind(&MUCController::handleChangeSubjectRequest, this, _1));
chatWindow_->onBookmarkRequest.connect(boost::bind(&MUCController::handleBookmarkRequest, this));
chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this, _1));
chatWindow_->onConfigurationFormCancelled.connect(boost::bind(&MUCController::handleConfigurationCancelled, this));
chatWindow_->onDestroyRequest.connect(boost::bind(&MUCController::handleDestroyRoomRequest, this));
chatWindow_->onInviteToChat.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, this, _1));
chatWindow_->onGetAffiliationsRequest.connect(boost::bind(&MUCController::handleGetAffiliationsRequest, this));
chatWindow_->onChangeAffiliationsRequest.connect(boost::bind(&MUCController::handleChangeAffiliationsRequest, this, _1));
chatWindow_->onUnblockUserRequest.connect(boost::bind(&MUCController::handleUnblockUserRequest, this));
muc_->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1));
muc_->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1));
muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
muc_->onOccupantNicknameChanged.connect(boost::bind(&MUCController::handleOccupantNicknameChanged, this, _1, _2));
muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
muc_->onRoleChangeFailed.connect(boost::bind(&MUCController::handleOccupantRoleChangeFailed, this, _1, _2, _3));
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));
loginCheckTimer_->start();
}
else {
chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(QT_TRANSLATE_NOOP("", "You are currently offline. You will enter this room when you are connected.")), ChatWindow::DefaultDirection);
}
if (isImpromptu) {
muc_->onUnlocked.connect(boost::bind(&MUCController::handleRoomUnlocked, this));
chatWindow_->convertToMUC(ChatWindow::ImpromptuMUC);
} else {
muc_->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
muc_->onOccupantAffiliationChanged.connect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3));
chatWindow_->convertToMUC(ChatWindow::StandardMUC);
chatWindow_->setName(muc->getJID().getNode());
}
if (stanzaChannel->isAvailable()) {
MUCController::setOnline(true);
}
if (avatarManager_ != nullptr) {
avatarChangedConnection_ = (avatarManager_->onAvatarChanged.connect(boost::bind(&MUCController::handleAvatarChanged, this, _1)));
}
MUCController::handleBareJIDCapsChanged(muc->getJID());
eventStream_->onUIEvent.connect(boost::bind(&MUCController::handleUIEvent, this, _1));
// setup handling of MUC bookmark changes
mucBookmarkManagerBookmarkAddedConnection_ = (mucBookmarkManager_->onBookmarkAdded.connect(boost::bind(&MUCController::handleMUCBookmarkAdded, this, _1)));
mucBookmarkManagerBookmarkRemovedConnection_ = (mucBookmarkManager_->onBookmarkRemoved.connect(boost::bind(&MUCController::handleMUCBookmarkRemoved, this, _1)));
@@ -566,64 +566,65 @@ void MUCController::preHandleIncomingMessage(std::shared_ptr<MessageEvent> messa
}
isInitialJoin_ = false;
chatWindow_->setSubject(message->getSubject());
doneGettingHistory_ = true;
subject_ = message->getSubject();
}
if (!doneGettingHistory_ && !message->getPayload<Delay>()) {
doneGettingHistory_ = true;
}
if (!doneGettingHistory_) {
checkDuplicates(message);
messageEvent->conclude();
}
}
void MUCController::addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time) {
if (from.isBare()) {
chatWindow_->addSystemMessage(message, ChatWindow::DefaultDirection);
}
else {
ChatControllerBase::addMessageHandleIncomingMessage(from, message, senderIsSelf, label, time);
}
}
void MUCController::postHandleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent, const ChatWindow::ChatMessage& chatMessage) {
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);
}
}
}
void MUCController::handleOccupantRoleChanged(const std::string& nick, const MUCOccupant& occupant, const MUCOccupant::Role& oldRole) {
clearPresenceQueue();
receivedActivity();
JID jid(nickToJID(nick));
roster_->removeContactFromGroup(jid, roleToGroupName(oldRole));
JID realJID;
if (occupant.getRealJID()) {
realJID = occupant.getRealJID().get();
}
std::string group(roleToGroupName(occupant.getRole()));
roster_->addContact(jid, realJID, nick, group, avatarManager_->getAvatarPath(jid));
roster_->getGroup(group)->setManualSort(roleToSortName(occupant.getRole()));
roster_->applyOnItems(SetMUC(jid, occupant.getRole(), occupant.getAffiliation()));
chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "%1% is now a %2%")) % nick % roleToFriendlyName(occupant.getRole()))), ChatWindow::DefaultDirection);
if (nick == nick_) {
setAvailableRoomActions(occupant.getAffiliation(), occupant.getRole());
}
}
void MUCController::handleOccupantAffiliationChanged(const std::string& nick, const MUCOccupant::Affiliation& affiliation, const MUCOccupant::Affiliation& /*oldAffiliation*/)
{
if (nick == nick_) {
setAvailableRoomActions(affiliation, muc_->getOccupant(nick_).getRole());
}
JID jid(nickToJID(nick));
MUCOccupant occupant = muc_->getOccupant(nick);
@@ -1084,61 +1085,61 @@ void MUCController::addRecentLogs() {
}
}
void MUCController::checkDuplicates(std::shared_ptr<Message> newMessage) {
std::string body = newMessage->getBody().get_value_or("");
JID jid = newMessage->getFrom();
boost::optional<boost::posix_time::ptime> time = newMessage->getTimestamp();
for (const auto& message : boost::adaptors::reverse(joinContext_)) {
boost::posix_time::ptime messageTime = message.getTime() - boost::posix_time::hours(message.getOffset());
if (time && time < messageTime) {
break;
}
if (time && time != messageTime) {
continue;
}
if (message.getFromJID() != jid) {
continue;
}
if (message.getMessage() != body) {
continue;
}
// Mark the message as unreadable
newMessage->setBody("");
}
}
void MUCController::setNick(const std::string& nick) {
nick_ = nick;
- highlighter_->setNick(nick_);
+ chatMessageParser_->setNick(nick);
}
Form::ref MUCController::buildImpromptuRoomConfiguration(Form::ref roomConfigurationForm) {
Form::ref result = std::make_shared<Form>(Form::SubmitType);
std::string impromptuConfigs[] = { "muc#roomconfig_enablelogging", "muc#roomconfig_persistentroom", "muc#roomconfig_publicroom", "muc#roomconfig_whois"};
std::set<std::string> impromptuConfigsMissing(impromptuConfigs, impromptuConfigs + 4);
for (const auto& field : roomConfigurationForm->getFields()) {
std::shared_ptr<FormField> resultField;
if (field->getName() == "muc#roomconfig_enablelogging") {
resultField = std::make_shared<FormField>(FormField::BooleanType, "0");
}
if (field->getName() == "muc#roomconfig_persistentroom") {
resultField = std::make_shared<FormField>(FormField::BooleanType, "0");
}
if (field->getName() == "muc#roomconfig_publicroom") {
resultField = std::make_shared<FormField>(FormField::BooleanType, "0");
}
if (field->getName() == "muc#roomconfig_whois") {
resultField = std::make_shared<FormField>(FormField::ListSingleType, "anyone");
}
if (field->getName() == "FORM_TYPE") {
resultField = std::make_shared<FormField>(FormField::HiddenType, "http://jabber.org/protocol/muc#roomconfig");
}
if (resultField) {
impromptuConfigsMissing.erase(field->getName());
resultField->setName(field->getName());
result->addField(resultField);
}
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,87 +1,87 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#include <map>
#include <memory>
#include <set>
#include <string>
#include <boost/signals2.hpp>
#include <boost/signals2/connection.hpp>
#include <Swiften/Base/Override.h>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/Elements/MUCOccupant.h>
#include <Swiften/Elements/Message.h>
#include <Swiften/JID/JID.h>
#include <Swiften/MUC/MUC.h>
#include <Swiften/Network/Timer.h>
#include <Swift/Controllers/Chat/ChatControllerBase.h>
#include <Swift/Controllers/Roster/RosterItem.h>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
namespace Swift {
class StanzaChannel;
class IQRouter;
class ChatWindowFactory;
class Roster;
class AvatarManager;
class UIEventStream;
class TimerFactory;
class TabComplete;
class XMPPRoster;
class HighlightManager;
class UIEvent;
class VCardManager;
class RosterVCardProvider;
class ClientBlockListManager;
class MUCBookmarkManager;
class MUCBookmark;
enum JoinPart {Join, Part, JoinThenPart, PartThenJoin};
struct NickJoinPart {
NickJoinPart(const std::string& nick, JoinPart type) : nick(nick), type(type) {}
std::string nick;
JoinPart type;
};
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;
boost::signals2::signal<void ()> onImpromptuConfigCompleted;
boost::signals2::signal<void (const std::string&, const std::string& )> onUserNicknameChanged;
virtual void setOnline(bool online) SWIFTEN_OVERRIDE;
virtual void setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info) SWIFTEN_OVERRIDE;
void rejoin();
static void appendToJoinParts(std::vector<NickJoinPart>& joinParts, const NickJoinPart& newEvent);
static std::string generateJoinPartString(const std::vector<NickJoinPart>& joinParts, bool isImpromptu);
static std::string concatenateListOfNames(const std::vector<NickJoinPart>& joinParts);
static std::string generateNicknameChangeString(const std::string& oldNickname, const std::string& newNickname);
bool isJoined();
const std::string& getNick();
const boost::optional<std::string> getPassword() const;
bool isImpromptu() const;
std::map<std::string, JID> getParticipantJIDs() const;
void sendInvites(const std::vector<JID>& jids, const std::string& reason) const;
protected:
virtual void preSendMessageRequest(std::shared_ptr<Message> message) SWIFTEN_OVERRIDE;
virtual bool isIncomingMessageFromMe(std::shared_ptr<Message> message) SWIFTEN_OVERRIDE;
virtual std::string senderHighlightNameFromMessage(const JID& from) SWIFTEN_OVERRIDE;
virtual std::string senderDisplayNameFromMessage(const JID& from) SWIFTEN_OVERRIDE;
virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(std::shared_ptr<Message> message) const SWIFTEN_OVERRIDE;
virtual void preHandleIncomingMessage(std::shared_ptr<MessageEvent>) SWIFTEN_OVERRIDE;
virtual void addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time) SWIFTEN_OVERRIDE;
virtual void postHandleIncomingMessage(std::shared_ptr<MessageEvent>, const ChatWindow::ChatMessage& chatMessage) SWIFTEN_OVERRIDE;
virtual void cancelReplaces() SWIFTEN_OVERRIDE;
virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) SWIFTEN_OVERRIDE;
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,281 +1,292 @@
/*
- * 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_ = ":(";
smile2Path_ = "/blah/smile2.jpg";
emoticons_[smile1_] = smile1Path_;
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_;
std::string smile2_;
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,32 +1,32 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <map>
#include <set>
#include <boost/bind.hpp>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <hippomocks.h>
#include <Swiften/Avatars/AvatarMemoryStorage.h>
#include <Swiften/Avatars/NullAvatarManager.h>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/DummyStanzaChannel.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Crypto/CryptoProvider.h>
#include <Swiften/Crypto/PlatformCryptoProvider.h>
#include <Swiften/Disco/DummyEntityCapsProvider.h>
#include <Swiften/Elements/CarbonsReceived.h>
#include <Swiften/Elements/CarbonsSent.h>
#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Elements/DeliveryReceiptRequest.h>
#include <Swiften/Elements/Forwarded.h>
#include <Swiften/Elements/MUCInvitationPayload.h>
#include <Swiften/Elements/MUCUserPayload.h>
@@ -104,61 +104,61 @@ class ChatsManagerTest : public CppUnit::TestFixture {
public:
void setUp() {
mocks_ = new MockRepository();
jid_ = JID("test@test.com/resource");
stanzaChannel_ = new DummyStanzaChannel();
iqRouter_ = new IQRouter(stanzaChannel_);
eventController_ = new EventController();
chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();
joinMUCWindowFactory_ = mocks_->InterfaceMock<JoinMUCWindowFactory>();
xmppRoster_ = new XMPPRosterImpl();
mucRegistry_ = new MUCRegistry();
nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, nullptr, mucRegistry_);
presenceOracle_ = new PresenceOracle(stanzaChannel_, xmppRoster_);
serverDiscoInfo_ = std::make_shared<DiscoInfo>();
presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_);
directedPresenceSender_ = new DirectedPresenceSender(presenceSender_);
mucManager_ = new MUCManager(stanzaChannel_, iqRouter_, directedPresenceSender_, mucRegistry_);
uiEventStream_ = new UIEventStream();
entityCapsProvider_ = new DummyEntityCapsProvider();
chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>();
mucSearchWindowFactory_ = mocks_->InterfaceMock<MUCSearchWindowFactory>();
settings_ = new DummySettingsProvider();
profileSettings_ = new ProfileSettingsProvider("a", settings_);
chatListWindow_ = new MockChatListWindow();
ftManager_ = new DummyFileTransferManager();
ftOverview_ = new FileTransferOverview(ftManager_);
avatarManager_ = new NullAvatarManager();
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));
crypto_ = PlatformCryptoProvider::create();
vcardStorage_ = new VCardMemoryStorage(crypto_);
vcardManager_ = new VCardManager(jid_, iqRouter_, vcardStorage_);
mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_);
clientBlockListManager_ = new ClientBlockListManager(iqRouter_);
manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, nullptr, mucRegistry_, entityCapsProvider_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, nullptr, wbManager_, highlightManager_, clientBlockListManager_, emoticons_, vcardManager_);
manager_->setAvatarManager(avatarManager_);
}
void tearDown() {
delete highlightManager_;
delete profileSettings_;
delete avatarManager_;
delete manager_;
delete clientBlockListManager_;
delete vcardManager_;
delete vcardStorage_;
delete crypto_;
delete ftOverview_;
delete ftManager_;
delete wbSessionManager_;
delete wbManager_;
delete directedPresenceSender_;
delete presenceSender_;
delete presenceOracle_;
@@ -759,128 +759,115 @@ public:
stanzaChannel_->onIQReceived(infoResponse);
CPPUNIT_ASSERT_EQUAL(true, window->impromptuMUCSupported_);
manager_->setOnline(false);
CPPUNIT_ASSERT_EQUAL(false, window->impromptuMUCSupported_);
}
void testhelperChatControllerPresenceAccessUpdatedOnSubscriptionChangeReceiptsAllowed(RosterItemPayload::Subscription from, RosterItemPayload::Subscription to) {
JID messageJID("testling@test.com/resource1");
xmppRoster_->addContact(messageJID, "foo", std::vector<std::string>(), from);
MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>();
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window);
settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true);
std::shared_ptr<Message> message = makeDeliveryReceiptTestMessage(messageJID, "1");
manager_->handleIncomingMessage(message);
CPPUNIT_ASSERT_EQUAL(st(0), stanzaChannel_->countSentStanzaOfType<Message>());
xmppRoster_->addContact(messageJID, "foo", std::vector<std::string>(), to);
message->setID("2");
manager_->handleIncomingMessage(message);
CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->countSentStanzaOfType<Message>());
Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex<Stanza>(1);
CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload<DeliveryReceipt>() != nullptr);
}
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");
MockChatWindow* window = new MockChatWindow();
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window);
std::shared_ptr<Message> message(new Message());
message->setFrom(messageJID);
std::string body("This message should cause two sounds: Juliet and Romeo.");
message->setBody(body);
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");
MockChatWindow* window = new MockChatWindow();
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window);
std::shared_ptr<Message> message(new Message());
message->setFrom(messageJID);
std::string body("This message should cause one sound, because both actions have the same sound: Juliet and Romeo.");
message->setBody(body);
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() {
JID messageJID("testling@test.com/resource1");
MockChatWindow* window = new MockChatWindow();
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window);
std::shared_ptr<Message> message(new Message());
message->setFrom(messageJID);
std::string body("/me is feeling delighted.");
message->setBody(body);
manager_->handleIncomingMessage(message);
CPPUNIT_ASSERT_EQUAL(std::string("is feeling delighted."), window->bodyFromMessage(window->lastAddedAction_));
}
void testRestartingMUCComponentCrash() {
JID mucJID = JID("teaparty@rooms.wonderland.lit");
JID self = JID("girl@wonderland.lit/rabbithole");
std::string nick = "aLiCe";
MockChatWindow* window;
auto genRemoteMUCPresence = [=]() {
auto presence = Presence::create();
presence->setFrom(mucJID.withResource(nick));
presence->setTo(self);
return presence;
};
@@ -895,66 +882,67 @@ public:
}
{
auto firstPresence = genRemoteMUCPresence();
firstPresence->setType(Presence::Unavailable);
auto userPayload = std::make_shared<MUCUserPayload>();
userPayload->addItem(MUCItem(MUCOccupant::Owner, MUCOccupant::NoRole));
firstPresence->addPayload(userPayload);
stanzaChannel_->onPresenceReceived(firstPresence);
}
CPPUNIT_ASSERT_EQUAL(std::string("Couldn't enter room: Unable to enter this room."), MockChatWindow::bodyFromMessage(window->lastAddedErrorMessage_));
{
auto presence = genRemoteMUCPresence();
presence->setType(Presence::Unavailable);
auto userPayload = std::make_shared<MUCUserPayload>();
userPayload->addStatusCode(303);
auto item = MUCItem(MUCOccupant::Owner, self, MUCOccupant::Moderator);
item.nick = nick;
userPayload->addItem(item);
userPayload->addStatusCode(110);
presence->addPayload(userPayload);
stanzaChannel_->onPresenceReceived(presence);
}
}
void testChatControllerMeMessageHandlingInMUC() {
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);
uiEventStream_->send(std::make_shared<JoinMUCUIEvent>(mucJID, boost::optional<std::string>(), nickname));
auto genRemoteMUCPresence = [=]() {
auto presence = Presence::create();
presence->setFrom(mucJID.withResource(nickname));
presence->setTo(jid_);
return presence;
};
{
auto presence = genRemoteMUCPresence();
auto userPayload = std::make_shared<MUCUserPayload>();
userPayload->addStatusCode(110);
userPayload->addItem(MUCItem(MUCOccupant::Owner, jid_, MUCOccupant::Moderator));
presence->addPayload(userPayload);
stanzaChannel_->onPresenceReceived(presence);
}
{
auto presence = genRemoteMUCPresence();
presence->setFrom(mucJID.withResource("someDifferentNickname"));
auto userPayload = std::make_shared<MUCUserPayload>();
userPayload->addItem(MUCItem(MUCOccupant::Member, JID("foo@bar.com"), MUCOccupant::Moderator));
presence->addPayload(userPayload);
stanzaChannel_->onPresenceReceived(presence);
}
@@ -1114,62 +1102,62 @@ public:
originalMessage->setFrom(messageJID);
originalMessage->setTo(jid2);
originalMessage->setType(Message::Chat);
originalMessage->addPayload(std::make_shared<DeliveryReceipt>("abcdefg123456"));
auto messageWrapper = createCarbonsMessage(std::make_shared<CarbonsReceived>(), originalMessage);
manager_->handleIncomingMessage(messageWrapper);
CPPUNIT_ASSERT_EQUAL(size_t(2), window->receiptChanges_.size());
CPPUNIT_ASSERT_EQUAL(ChatWindow::ReceiptReceived, window->receiptChanges_[1].second);
}
}
private:
std::shared_ptr<Message> makeDeliveryReceiptTestMessage(const JID& from, const std::string& id) {
std::shared_ptr<Message> message = std::make_shared<Message>();
message->setFrom(from);
message->setID(id);
message->setBody("This will cause the window to open");
message->addPayload(std::make_shared<DeliveryReceiptRequest>());
return message;
}
size_t st(int i) {
return static_cast<size_t>(i);
}
void handleHighlightAction(const HighlightAction& action) {
handledHighlightActions_++;
- if (action.playSound()) {
- soundsPlayed_.insert(action.getSoundFile());
+ if (action.getSoundFilePath()) {
+ soundsPlayed_.insert(action.getSoundFilePath().get_value_or(""));
}
}
private:
JID jid_;
ChatsManager* manager_;
DummyStanzaChannel* stanzaChannel_;
IQRouter* iqRouter_;
EventController* eventController_;
ChatWindowFactory* chatWindowFactory_;
JoinMUCWindowFactory* joinMUCWindowFactory_;
NickResolver* nickResolver_;
PresenceOracle* presenceOracle_;
AvatarManager* avatarManager_;
std::shared_ptr<DiscoInfo> serverDiscoInfo_;
XMPPRosterImpl* xmppRoster_;
PresenceSender* presenceSender_;
MockRepository* mocks_;
UIEventStream* uiEventStream_;
ChatListWindowFactory* chatListWindowFactory_;
WhiteboardWindowFactory* whiteboardWindowFactory_;
MUCSearchWindowFactory* mucSearchWindowFactory_;
MUCRegistry* mucRegistry_;
DirectedPresenceSender* directedPresenceSender_;
DummyEntityCapsProvider* entityCapsProvider_;
MUCManager* mucManager_;
DummySettingsProvider* settings_;
ProfileSettingsProvider* profileSettings_;
ChatListWindow* chatListWindow_;
FileTransferOverview* ftOverview_;
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,32 +1,32 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <boost/algorithm/string.hpp>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <hippomocks.h>
#include <Swiften/Avatars/NullAvatarManager.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/DummyStanzaChannel.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Crypto/CryptoProvider.h>
#include <Swiften/Crypto/PlatformCryptoProvider.h>
#include <Swiften/Disco/DummyEntityCapsProvider.h>
#include <Swiften/Elements/MUCUserPayload.h>
#include <Swiften/Elements/Thread.h>
#include <Swiften/MUC/MUCBookmarkManager.h>
#include <Swiften/MUC/UnitTest/MockMUC.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/Presence/DirectedPresenceSender.h>
#include <Swiften/Presence/PresenceOracle.h>
#include <Swiften/Presence/StanzaChannelPresenceSender.h>
#include <Swiften/Queries/DummyIQChannel.h>
#include <Swiften/Roster/XMPPRoster.h>
#include <Swiften/Roster/XMPPRosterImpl.h>
#include <Swiften/VCards/VCardManager.h>
#include <Swiften/VCards/VCardMemoryStorage.h>
@@ -67,72 +67,74 @@ class MUCControllerTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testHandleChangeSubjectRequest);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
crypto_ = std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create());
self_ = JID("girl@wonderland.lit/rabbithole");
nick_ = "aLiCe";
mucJID_ = JID("teaparty@rooms.wonderland.lit");
mocks_ = new MockRepository();
stanzaChannel_ = new DummyStanzaChannel();
iqChannel_ = new DummyIQChannel();
iqRouter_ = new IQRouter(iqChannel_);
eventController_ = new EventController();
chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();
userSearchWindowFactory_ = mocks_->InterfaceMock<UserSearchWindowFactory>();
xmppRoster_ = new XMPPRosterImpl();
presenceOracle_ = new PresenceOracle(stanzaChannel_, xmppRoster_);
presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_);
directedPresenceSender_ = new DirectedPresenceSender(presenceSender_);
uiEventStream_ = new UIEventStream();
avatarManager_ = new NullAvatarManager();
TimerFactory* timerFactory = nullptr;
window_ = new MockChatWindow();
mucRegistry_ = new MUCRegistry();
entityCapsProvider_ = new DummyEntityCapsProvider();
settings_ = new DummySettingsProvider();
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_;
delete settings_;
delete entityCapsProvider_;
delete eventController_;
delete presenceOracle_;
delete xmppRoster_;
delete mocks_;
delete uiEventStream_;
delete stanzaChannel_;
delete presenceSender_;
delete directedPresenceSender_;
delete iqRouter_;
delete iqChannel_;
delete mucRegistry_;
delete avatarManager_;
}
void finishJoin() {
Presence::ref presence(new Presence());
presence->setFrom(JID(muc_->getJID().toString() + "/" + nick_));
MUCUserPayload::ref status(new MUCUserPayload());
MUCUserPayload::StatusCode code;
code.code = 110;
status->addStatusCode(code);
presence->addPayload(status);
stanzaChannel_->onPresenceReceived(presence);
}
@@ -565,53 +567,53 @@ public:
for (auto childItem : child->getChildren()) {
ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(childItem);
CPPUNIT_ASSERT(item);
std::map<std::string, MUCOccupant>::const_iterator occupant = occupants.find(item->getJID().getResource());
CPPUNIT_ASSERT(occupant != occupants.end());
CPPUNIT_ASSERT(item->getMUCRole() == occupant->second.getRole());
CPPUNIT_ASSERT(item->getMUCAffiliation() == occupant->second.getAffiliation());
}
}
}
void testHandleChangeSubjectRequest() {
std::string testStr("New Subject");
CPPUNIT_ASSERT_EQUAL(std::string(""), muc_->newSubjectSet_);
window_->onChangeSubjectRequest(testStr);
CPPUNIT_ASSERT_EQUAL(testStr, muc_->newSubjectSet_);
}
private:
JID self_;
JID mucJID_;
MockMUC::ref muc_;
std::string nick_;
DummyStanzaChannel* stanzaChannel_;
DummyIQChannel* iqChannel_;
IQRouter* iqRouter_;
EventController* eventController_;
ChatWindowFactory* chatWindowFactory_;
UserSearchWindowFactory* userSearchWindowFactory_;
MUCController* controller_;
-// NickResolver* nickResolver_;
+ NickResolver* nickResolver_;
PresenceOracle* presenceOracle_;
AvatarManager* avatarManager_;
StanzaChannelPresenceSender* presenceSender_;
DirectedPresenceSender* directedPresenceSender_;
MockRepository* mocks_;
UIEventStream* uiEventStream_;
MockChatWindow* window_;
MUCRegistry* mucRegistry_;
DummyEntityCapsProvider* entityCapsProvider_;
DummySettingsProvider* settings_;
HighlightManager* highlightManager_;
std::shared_ptr<ChatMessageParser> chatMessageParser_;
std::shared_ptr<CryptoProvider> crypto_;
VCardManager* vcardManager_;
VCardMemoryStorage* vcardStorage_;
ClientBlockListManager* clientBlockListManager_;
MUCBookmarkManager* mucBookmarkManager_;
XMPPRoster* xmppRoster_;
};
CPPUNIT_TEST_SUITE_REGISTRATION(MUCControllerTest);
diff --git a/Swift/Controllers/EventNotifier.cpp b/Swift/Controllers/EventNotifier.cpp
index 6ea2ea5..f22a58c 100644
--- a/Swift/Controllers/EventNotifier.cpp
+++ b/Swift/Controllers/EventNotifier.cpp
@@ -1,80 +1,77 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/EventNotifier.h>
#include <boost/algorithm/string.hpp>
#include <boost/bind.hpp>
#include <Swiften/Avatars/AvatarManager.h>
#include <Swiften/Base/String.h>
#include <Swiften/Base/format.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/JID/JID.h>
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/Settings/SettingsProvider.h>
#include <Swift/Controllers/XMPPEvents/ErrorEvent.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h>
#include <Swift/Controllers/XMPPEvents/MessageEvent.h>
#include <Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h>
#include <SwifTools/Notifier/Notifier.h>
namespace Swift {
EventNotifier::EventNotifier(EventController* eventController, Notifier* notifier, AvatarManager* avatarManager, NickResolver* nickResolver) : eventController(eventController), notifier(notifier), avatarManager(avatarManager), nickResolver(nickResolver) {
eventController->onEventQueueEventAdded.connect(boost::bind(&EventNotifier::handleEventAdded, this, _1));
}
EventNotifier::~EventNotifier() {
notifier->purgeCallbacks();
eventController->onEventQueueEventAdded.disconnect(boost::bind(&EventNotifier::handleEventAdded, this, _1));
}
void EventNotifier::handleEventAdded(std::shared_ptr<StanzaEvent> event) {
if (event->getConcluded()) {
return;
}
if (std::shared_ptr<MessageEvent> messageEvent = std::dynamic_pointer_cast<MessageEvent>(event)) {
JID jid = messageEvent->getStanza()->getFrom();
- std::string title = nickResolver->jidToNick(jid);
if (!messageEvent->getStanza()->isError() && !messageEvent->getStanza()->getBody().get_value_or("").empty()) {
JID activationJID = jid;
if (messageEvent->getStanza()->getType() == Message::Groupchat) {
activationJID = jid.toBare();
}
- std::string messageText = messageEvent->getStanza()->getBody().get_value_or("");
- if (boost::starts_with(messageText, "/me ")) {
- messageText = "*" + String::getSplittedAtFirst(messageText, ' ').second + "*";
+ for (const auto& notification : messageEvent->getNotifications()) {
+ notifier->showMessage(Notifier::IncomingMessage, notification.title, notification.message, avatarManager->getAvatarPath(jid), boost::bind(&EventNotifier::handleNotificationActivated, this, activationJID));
}
- notifier->showMessage(Notifier::IncomingMessage, title, messageText, avatarManager->getAvatarPath(jid), boost::bind(&EventNotifier::handleNotificationActivated, this, activationJID));
}
}
else if(std::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = std::dynamic_pointer_cast<SubscriptionRequestEvent>(event)) {
JID jid = subscriptionEvent->getJID();
std::string title = jid;
std::string message = str(format(QT_TRANSLATE_NOOP("", "%1% wants to add you to his/her contact list")) % nickResolver->jidToNick(jid));
notifier->showMessage(Notifier::SystemMessage, title, message, boost::filesystem::path(), boost::function<void()>());
}
else if(std::shared_ptr<ErrorEvent> errorEvent = std::dynamic_pointer_cast<ErrorEvent>(event)) {
notifier->showMessage(Notifier::SystemMessage, QT_TRANSLATE_NOOP("", "Error"), errorEvent->getText(), boost::filesystem::path(), boost::function<void()>());
}
else if (std::shared_ptr<MUCInviteEvent> mucInviteEvent = std::dynamic_pointer_cast<MUCInviteEvent>(event)) {
std::string title = mucInviteEvent->getInviter();
std::string message = str(format(QT_TRANSLATE_NOOP("", "%1% has invited you to enter the %2% room")) % nickResolver->jidToNick(mucInviteEvent->getInviter()) % mucInviteEvent->getRoomJID());
// FIXME: not show avatar or greyed out avatar for mediated invites
notifier->showMessage(Notifier::SystemMessage, title, message, avatarManager->getAvatarPath(mucInviteEvent->getInviter()), boost::bind(&EventNotifier::handleNotificationActivated, this, mucInviteEvent->getInviter()));
}
}
void EventNotifier::handleNotificationActivated(JID jid) {
onNotificationActivated(jid);
}
}
diff --git a/Swift/Controllers/HighlightAction.cpp b/Swift/Controllers/HighlightAction.cpp
deleted file mode 100644
index 3ea2c86..0000000
--- a/Swift/Controllers/HighlightAction.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2012 Maciej Niedzielski
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2015 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <Swift/Controllers/HighlightAction.h>
-
-namespace Swift {
-
-void HighlightAction::setHighlightWholeMessage(bool highlightText)
-{
- highlightWholeMessage_ = highlightText;
- if (!highlightWholeMessage_) {
- textColor_.clear();
- textBackground_.clear();
- }
-}
-
-void HighlightAction::setPlaySound(bool playSound)
-{
- playSound_ = playSound;
- if (!playSound_) {
- soundFile_.clear();
- }
-}
-
-bool operator ==(HighlightAction const& a, HighlightAction const& b) {
- if (a.highlightWholeMessage() != b.highlightWholeMessage()) {
- return false;
- }
-
- if (a.getTextColor() != b.getTextColor()) {
- return false;
- }
-
- if (a.getTextBackground() != b.getTextBackground()) {
- return false;
- }
-
- if (a.playSound() != b.playSound()) {
- return false;
- }
-
- if (a.getSoundFile() != b.getSoundFile()) {
- return false;
- }
-
- return true;
-}
-
-bool operator !=(HighlightAction const& a, HighlightAction const& b) {
- return !(a == b);
-}
-
-}
diff --git a/Swift/Controllers/HighlightAction.h b/Swift/Controllers/HighlightAction.h
deleted file mode 100644
index b9d4539..0000000
--- a/Swift/Controllers/HighlightAction.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2012 Maciej Niedzielski
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2014-2015 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>
-
-namespace Swift {
-
- class HighlightRule;
-
- class HighlightAction {
- public:
- HighlightAction() : highlightWholeMessage_(false), playSound_(false) {}
-
- /**
- * Gets the flag that indicates the entire message should be highlighted.
- */
- bool highlightWholeMessage() const { return highlightWholeMessage_; }
- void setHighlightWholeMessage(bool highlightText);
-
- /**
- * Gets the foreground highlight color.
- */
- const std::string& getTextColor() const { return textColor_; }
- void setTextColor(const std::string& textColor) { textColor_ = textColor; }
-
- /**
- * Gets the background highlight color.
- */
- const std::string& getTextBackground() const { return textBackground_; }
- void setTextBackground(const std::string& textBackground) { textBackground_ = textBackground; }
-
- bool playSound() const { return playSound_; }
- void setPlaySound(bool playSound);
-
- /**
- * Gets the sound filename. If the string is empty, assume a default sound file.
- */
- const std::string& getSoundFile() const { return soundFile_; }
- void setSoundFile(const std::string& soundFile) { soundFile_ = soundFile; }
-
- bool isEmpty() const { return !highlightWholeMessage_ && !playSound_; }
-
- private:
- friend class boost::serialization::access;
- template<class Archive> void serialize(Archive & ar, const unsigned int version);
-
- bool highlightWholeMessage_;
- std::string textColor_;
- std::string textBackground_;
-
- bool playSound_;
- std::string soundFile_;
- };
-
- bool operator ==(HighlightAction const& a, HighlightAction const& b);
- bool operator !=(HighlightAction const& a, HighlightAction const& b);
-
- template<class Archive>
- void HighlightAction::serialize(Archive& ar, const unsigned int /*version*/)
- {
- ar & highlightWholeMessage_;
- ar & textColor_;
- ar & textBackground_;
- ar & playSound_;
- ar & soundFile_;
- }
-
-}
diff --git a/Swift/Controllers/HighlightManager.cpp b/Swift/Controllers/HighlightManager.cpp
deleted file mode 100644
index 9176301..0000000
--- a/Swift/Controllers/HighlightManager.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (c) 2012 Maciej Niedzielski
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2014-2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <Swift/Controllers/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 <Swift/Controllers/Highlighter.h>
-#include <Swift/Controllers/SettingConstants.h>
-#include <Swift/Controllers/Settings/SettingsProvider.h>
-
-/* How does highlighting work?
- *
- * HighlightManager manages a list of if-then rules used to highlight messages.
- * Rule is represented by HighlightRule. Action ("then" part) is HighlightAction.
- *
- *
- * HighlightManager is also used as a factory for Highlighter objects.
- * Each ChatControllerBase has its own Highlighter.
- * Highligher may be customized by using setNick(), etc.
- *
- * ChatControllerBase passes incoming messages to Highlighter and gets HighlightAction in return
- * (first matching rule is returned).
- * If needed, HighlightAction is then passed back to Highlighter for further handling.
- * This results in HighlightManager emiting onHighlight event,
- * which is handled by SoundController to play sound notification
- */
-
-namespace Swift {
-
-HighlightManager::HighlightManager(SettingsProvider* settings)
- : settings_(settings)
- , storingSettings_(false) {
- rules_ = std::make_shared<HighlightRulesList>();
- 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();
- }
-}
-
-std::string HighlightManager::rulesToString() const {
- std::stringstream stream;
- boost::archive::text_oarchive archive(stream);
- archive & rules_->list_;
- return stream.str();
-}
-
-std::vector<HighlightRule> HighlightManager::getDefaultRules() {
- std::vector<HighlightRule> rules;
-
- HighlightRule chatNotificationRule;
- chatNotificationRule.setMatchChat(true);
- chatNotificationRule.getAction().setPlaySound(true);
- chatNotificationRule.setMatchWholeWords(true);
- rules.push_back(chatNotificationRule);
-
- HighlightRule selfMentionMUCRule;
- selfMentionMUCRule.setMatchMUC(true);
- selfMentionMUCRule.getAction().setPlaySound(true);
- selfMentionMUCRule.setNickIsKeyword(true);
- selfMentionMUCRule.setMatchCase(true);
- selfMentionMUCRule.setMatchWholeWords(true);
- rules.push_back(selfMentionMUCRule);
-
- return rules;
-}
-
-HighlightRule HighlightManager::getRule(int index) const {
- assert(index >= 0 && static_cast<size_t>(index) < rules_->getSize());
- return rules_->getRule(static_cast<size_t>(index));
-}
-
-void HighlightManager::setRule(int index, const HighlightRule& rule) {
- assert(index >= 0 && static_cast<size_t>(index) < rules_->getSize());
- rules_->list_[static_cast<size_t>(index)] = rule;
-}
-
-void HighlightManager::insertRule(int index, const HighlightRule& rule) {
- assert(index >= 0 && boost::numeric_cast<std::vector<std::string>::size_type>(index) <= rules_->getSize());
- rules_->list_.insert(rules_->list_.begin() + index, rule);
-}
-
-void HighlightManager::removeRule(int index) {
- assert(index >= 0 && boost::numeric_cast<std::vector<std::string>::size_type>(index) < rules_->getSize());
- rules_->list_.erase(rules_->list_.begin() + index);
-}
-
-void HighlightManager::swapRules(const size_t first, const size_t second) {
- assert(first < rules_->getSize());
- assert(second < rules_->getSize());
- const HighlightRule swap = rules_->getRule(first);
- rules_->setRule(first, rules_->getRule(second));
- rules_->setRule(second, swap);
-}
-
-void HighlightManager::storeSettings() {
- storingSettings_ = true; // don't reload settings while saving
- settings_->storeSetting(SettingConstants::HIGHLIGHT_RULES, rulesToString());
- storingSettings_ = false;
-}
-
-void HighlightManager::loadSettings() {
- std::string rulesString = settings_->getSetting(SettingConstants::HIGHLIGHT_RULES);
- std::stringstream stream;
- stream << rulesString;
- try {
- boost::archive::text_iarchive archive(stream);
- archive >> rules_->list_;
- } catch (boost::archive::archive_exception&) {
- rules_->list_ = getDefaultRules();
- }
-}
-
-Highlighter* HighlightManager::createHighlighter() {
- return new Highlighter(this);
-}
-
-bool HighlightManager::isDefaultRulesList() const {
- return getDefaultRules() == rules_->list_;
-}
-
-void HighlightManager::resetToDefaultRulesList() {
- rules_->list_ = getDefaultRules();
-}
-
-}
diff --git a/Swift/Controllers/HighlightManager.h b/Swift/Controllers/HighlightManager.h
deleted file mode 100644
index a35e253..0000000
--- a/Swift/Controllers/HighlightManager.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2012 Maciej Niedzielski
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2014-2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include <boost/signals2.hpp>
-
-#include <Swift/Controllers/HighlightRule.h>
-
-namespace Swift {
-
- class SettingsProvider;
- class Highlighter;
-
- class HighlightManager {
- public:
-
- class HighlightRulesList {
- public:
- friend class HighlightManager;
- size_t getSize() const { return list_.size(); }
- const HighlightRule& getRule(const size_t index) const { return list_[index]; }
- void addRule(const HighlightRule& rule) { list_.push_back(rule); }
- void combineRules(const HighlightRulesList& rhs) {
- list_.insert(list_.end(), rhs.list_.begin(), rhs.list_.end());
- }
- void setRule(const size_t index, const HighlightRule& rule) {
- list_[index] = rule;
- }
- private:
- std::vector<HighlightRule> list_;
- };
-
- HighlightManager(SettingsProvider* settings);
-
- Highlighter* createHighlighter();
-
- std::shared_ptr<const HighlightManager::HighlightRulesList> getRules() const { return rules_; }
-
- bool isDefaultRulesList() const;
- void resetToDefaultRulesList();
-
- HighlightRule getRule(int index) const;
- void setRule(int index, const HighlightRule& rule);
- void insertRule(int index, const HighlightRule& rule);
- void removeRule(int index);
- void swapRules(const size_t first, const size_t second);
- void storeSettings();
- void loadSettings();
-
- boost::signals2::signal<void (const HighlightAction&)> onHighlight;
-
- private:
- void handleSettingChanged(const std::string& settingPath);
-
- std::string rulesToString() const;
- static std::vector<HighlightRule> getDefaultRules();
-
- private:
- SettingsProvider* settings_;
- bool storingSettings_;
-
- std::shared_ptr<HighlightManager::HighlightRulesList> rules_;
- boost::signals2::scoped_connection handleSettingChangedConnection_;
- };
-
- typedef std::shared_ptr<const HighlightManager::HighlightRulesList> HighlightRulesListPtr;
-
-}
diff --git a/Swift/Controllers/HighlightRule.cpp b/Swift/Controllers/HighlightRule.cpp
deleted file mode 100644
index a8cb7e4..0000000
--- a/Swift/Controllers/HighlightRule.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (c) 2012 Maciej Niedzielski
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2014-2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <Swift/Controllers/HighlightRule.h>
-
-#include <algorithm>
-
-#include <boost/algorithm/string.hpp>
-#include <boost/lambda/lambda.hpp>
-
-#include <Swiften/Base/Regex.h>
-
-namespace Swift {
-
-HighlightRule::HighlightRule()
- : nickIsKeyword_(false)
- , matchCase_(false)
- , matchWholeWords_(false)
- , matchChat_(false)
- , matchMUC_(false)
-{
-}
-
-boost::regex HighlightRule::regexFromString(const std::string & s) const
-{
- std::string escaped = Regex::escape(s);
- std::string word = matchWholeWords_ ? "\\b" : "";
- boost::regex::flag_type flags = boost::regex::normal;
- if (!matchCase_) {
- flags |= boost::regex::icase;
- }
- return boost::regex(word + escaped + word, flags);
-}
-
-void HighlightRule::updateRegex() const
-{
- keywordRegex_.clear();
- for (const auto& k : keywords_) {
- keywordRegex_.push_back(regexFromString(k));
- }
- senderRegex_.clear();
- for (const auto& s : senders_) {
- senderRegex_.push_back(regexFromString(s));
- }
-}
-
-std::string HighlightRule::boolToString(bool b)
-{
- return b ? "1" : "0";
-}
-
-bool HighlightRule::boolFromString(const std::string& s)
-{
- return s == "1";
-}
-
-bool HighlightRule::isMatch(const std::string& body, const std::string& sender, const std::string& nick, MessageType messageType) const
-{
- if ((messageType == HighlightRule::ChatMessage && matchChat_) || (messageType == HighlightRule::MUCMessage && matchMUC_)) {
-
- bool matchesKeyword = keywords_.empty() && (nick.empty() || !nickIsKeyword_);
- bool matchesSender = senders_.empty();
-
- if (!matchesKeyword) {
- // check if the nickname matches
- if (nickIsKeyword_ && !nick.empty() && boost::regex_search(body, regexFromString(nick))) {
- matchesKeyword = true;
- }
-
- // check if a keyword matches
- if (!matchesKeyword && !keywords_.empty()) {
- for (const auto& keyword : keywordRegex_) {
- if (boost::regex_search(body, keyword)) {
- matchesKeyword = true;
- break;
- }
- }
- }
- }
-
- for (const auto& rx : senderRegex_) {
- if (boost::regex_search(sender, rx)) {
- matchesSender = true;
- break;
- }
- }
-
- if (matchesKeyword && matchesSender) {
- return true;
- }
- }
-
- return false;
-}
-
-void HighlightRule::setSenders(const std::vector<std::string>& senders)
-{
- senders_ = senders;
- updateRegex();
-}
-
-void HighlightRule::setKeywords(const std::vector<std::string>& keywords)
-{
- keywords_ = keywords;
- updateRegex();
-}
-
-std::vector<boost::regex> HighlightRule::getKeywordRegex(const std::string& nick) const {
- if (nickIsKeyword_) {
- std::vector<boost::regex> regex;
- if (!nick.empty()) {
- regex.push_back(regexFromString(nick));
- }
- return regex;
- } else {
- return keywordRegex_;
- }
-}
-
-void HighlightRule::setNickIsKeyword(bool nickIsKeyword)
-{
- nickIsKeyword_ = nickIsKeyword;
- updateRegex();
-}
-
-void HighlightRule::setMatchCase(bool matchCase)
-{
- matchCase_ = matchCase;
- updateRegex();
-}
-
-void HighlightRule::setMatchWholeWords(bool matchWholeWords)
-{
- matchWholeWords_ = matchWholeWords;
- updateRegex();
-}
-
-void HighlightRule::setMatchChat(bool matchChat)
-{
- matchChat_ = matchChat;
- updateRegex();
-}
-
-void HighlightRule::setMatchMUC(bool matchMUC)
-{
- matchMUC_ = matchMUC;
- updateRegex();
-}
-
-bool HighlightRule::isEmpty() const
-{
- return senders_.empty() && keywords_.empty() && !nickIsKeyword_ && !matchChat_ && !matchMUC_ && action_.isEmpty();
-}
-
-bool operator ==(HighlightRule const& a, HighlightRule const& b) {
- if (a.getSenders() != b.getSenders()) {
- return false;
- }
-
- if (a.getKeywords() != b.getKeywords()) {
- return false;
- }
-
- if (a.getNickIsKeyword() != b.getNickIsKeyword()) {
- return false;
- }
-
- if (a.getMatchChat() != b.getMatchChat()) {
- return false;
- }
-
- if (a.getMatchMUC() != b.getMatchMUC()) {
- return false;
- }
-
- if (a.getMatchCase() != b.getMatchCase()) {
- return false;
- }
-
- if (a.getMatchWholeWords() != b.getMatchWholeWords()) {
- return false;
- }
-
- if (a.getAction() != b.getAction()) {
- return false;
- }
-
- return true;
-}
-
-}
diff --git a/Swift/Controllers/HighlightRule.h b/Swift/Controllers/HighlightRule.h
deleted file mode 100644
index bffdc41..0000000
--- a/Swift/Controllers/HighlightRule.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2012 Maciej Niedzielski
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2014-2016 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 <boost/regex.hpp>
-
-#include <Swift/Controllers/HighlightAction.h>
-
-namespace Swift {
-
- class HighlightRule {
- public:
- HighlightRule();
-
- enum MessageType { ChatMessage, MUCMessage };
-
- bool isMatch(const std::string& body, const std::string& sender, const std::string& nick, MessageType) const;
-
- const HighlightAction& getAction() const { return action_; }
- HighlightAction& getAction() { return action_; }
-
- const std::vector<std::string>& getSenders() const { return senders_; }
- void setSenders(const std::vector<std::string>&);
- const std::vector<boost::regex>& getSenderRegex() const { return senderRegex_; }
-
- const std::vector<std::string>& getKeywords() const { return keywords_; }
- void setKeywords(const std::vector<std::string>&);
- std::vector<boost::regex> getKeywordRegex(const std::string& nick) const;
-
- bool getNickIsKeyword() const { return nickIsKeyword_; }
- void setNickIsKeyword(bool);
-
- bool getMatchCase() const { return matchCase_; }
- void setMatchCase(bool);
-
- bool getMatchWholeWords() const { return matchWholeWords_; }
- void setMatchWholeWords(bool);
-
- bool getMatchChat() const { return matchChat_; }
- void setMatchChat(bool);
-
- bool getMatchMUC() const { return matchMUC_; }
- void setMatchMUC(bool);
-
- bool isEmpty() const;
-
- private:
- friend class boost::serialization::access;
- template<class Archive> void serialize(Archive & ar, const unsigned int version);
-
- static std::string boolToString(bool);
- static bool boolFromString(const std::string&);
-
- std::vector<std::string> senders_;
- std::vector<std::string> keywords_;
- bool nickIsKeyword_;
-
- mutable std::vector<boost::regex> senderRegex_;
- mutable std::vector<boost::regex> keywordRegex_;
- void updateRegex() const;
- boost::regex regexFromString(const std::string&) const;
-
- bool matchCase_;
- bool matchWholeWords_;
-
- bool matchChat_;
- bool matchMUC_;
-
- HighlightAction action_;
- };
-
- bool operator ==(HighlightRule const& a, HighlightRule const& b);
-
- template<class Archive>
- void HighlightRule::serialize(Archive& ar, const unsigned int /*version*/)
- {
- ar & senders_;
- ar & keywords_;
- ar & nickIsKeyword_;
- ar & matchChat_;
- ar & matchMUC_;
- ar & matchCase_;
- ar & matchWholeWords_;
- ar & action_;
- updateRegex();
- }
-
-}
diff --git a/Swift/Controllers/Highlighter.cpp b/Swift/Controllers/Highlighter.cpp
deleted file mode 100644
index cea077e..0000000
--- a/Swift/Controllers/Highlighter.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2012 Maciej Niedzielski
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2014-2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <Swift/Controllers/Highlighter.h>
-
-#include <Swift/Controllers/HighlightManager.h>
-
-namespace Swift {
-
-Highlighter::Highlighter(HighlightManager* manager)
- : manager_(manager)
-{
- setMode(ChatMode);
-}
-
-void Highlighter::setMode(Mode mode)
-{
- mode_ = mode;
- messageType_ = mode_ == ChatMode ? HighlightRule::ChatMessage : HighlightRule::MUCMessage;
-}
-
-HighlightAction Highlighter::findFirstFullMessageMatchAction(const std::string& body, const std::string& sender) const
-{
- HighlightAction match;
- HighlightRulesListPtr rules = manager_->getRules();
- for (size_t i = 0; i < rules->getSize(); ++i) {
- const HighlightRule& rule = rules->getRule(i);
- if (rule.isMatch(body, sender, nick_, messageType_) && rule.getAction().highlightWholeMessage()) {
- match = rule.getAction();
- break;
- }
- }
-
- return match;
-}
-
-void Highlighter::handleHighlightAction(const HighlightAction& action)
-{
- manager_->onHighlight(action);
-}
-
-}
diff --git a/Swift/Controllers/Highlighter.h b/Swift/Controllers/Highlighter.h
deleted file mode 100644
index 9ad3339..0000000
--- a/Swift/Controllers/Highlighter.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2012 Maciej Niedzielski
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include <Swift/Controllers/HighlightRule.h>
-
-namespace Swift {
-
- class HighlightManager;
-
- class Highlighter {
- public:
- Highlighter(HighlightManager* manager);
-
- enum Mode { ChatMode, MUCMode };
- void setMode(Mode mode);
-
- void setNick(const std::string& nick) { nick_ = nick; }
- std::string getNick() const { return nick_; }
-
- HighlightAction findFirstFullMessageMatchAction(const std::string& body, const std::string& sender) const;
-
- void handleHighlightAction(const HighlightAction& action);
-
- private:
- HighlightManager* manager_;
- Mode mode_;
- HighlightRule::MessageType messageType_;
- std::string nick_;
- };
-
-}
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/HighlightEditorController.cpp b/Swift/Controllers/Highlighting/HighlightEditorController.cpp
index 1f5f928..50da3dc 100644
--- a/Swift/Controllers/HighlightEditorController.cpp
+++ b/Swift/Controllers/Highlighting/HighlightEditorController.cpp
@@ -1,43 +1,43 @@
/*
* Copyright (c) 2012 Maciej Niedzielski
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
/*
- * Copyright (c) 2014-2016 Isode Limited.
+ * Copyright (c) 2014-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
-#include <Swift/Controllers/HighlightEditorController.h>
+#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_);
diff --git a/Swift/Controllers/HighlightEditorController.h b/Swift/Controllers/Highlighting/HighlightEditorController.h
index a699751..d7608a5 100644
--- a/Swift/Controllers/HighlightEditorController.h
+++ b/Swift/Controllers/Highlighting/HighlightEditorController.h
@@ -1,38 +1,38 @@
/*
* Copyright (c) 2012 Maciej Niedzielski
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
/*
- * Copyright (c) 2014-2016 Isode Limited.
+ * 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:
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_;
+ };
+
+}
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 0d9f1b8..e64b23d 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -1,91 +1,91 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/MainController.h>
#include <cstdlib>
#include <memory>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/String.h>
#include <Swiften/Base/format.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/ClientXMLTracer.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Client/Storages.h>
#include <Swiften/Crypto/CryptoProvider.h>
#include <Swiften/Disco/CapsInfoGenerator.h>
#include <Swiften/Disco/ClientDiscoManager.h>
#include <Swiften/Disco/GetDiscoInfoRequest.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/Elements/VCardUpdate.h>
#include <Swiften/FileTransfer/FileTransferManager.h>
#include <Swiften/Network/NetworkFactories.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/Presence/PresenceSender.h>
#include <Swiften/Queries/Requests/EnableCarbonsRequest.h>
#include <Swiften/StringCodecs/Base64.h>
#include <Swiften/StringCodecs/Hexify.h>
#include <Swiften/VCards/GetVCardRequest.h>
#include <Swiften/VCards/VCardManager.h>
#ifdef SWIFTEN_PLATFORM_WIN32
#include <Swiften/SASL/WindowsAuthentication.h>
#endif
#include <Swift/Controllers/AdHocManager.h>
#include <Swift/Controllers/BlockListController.h>
#include <Swift/Controllers/BuildVersion.h>
#include <Swift/Controllers/Chat/ChatsManager.h>
#include <Swift/Controllers/Chat/MUCController.h>
#include <Swift/Controllers/Chat/UserSearchController.h>
#include <Swift/Controllers/ContactEditController.h>
#include <Swift/Controllers/ContactSuggester.h>
#include <Swift/Controllers/ContactsFromXMPPRoster.h>
#include <Swift/Controllers/EventNotifier.h>
#include <Swift/Controllers/EventWindowController.h>
#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
#include <Swift/Controllers/FileTransferListController.h>
-#include <Swift/Controllers/HighlightEditorController.h>
-#include <Swift/Controllers/HighlightManager.h>
+#include <Swift/Controllers/Highlighting/HighlightEditorController.h>
+#include <Swift/Controllers/Highlighting/HighlightManager.h>
#include <Swift/Controllers/HistoryController.h>
#include <Swift/Controllers/HistoryViewController.h>
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/PresenceNotifier.h>
#include <Swift/Controllers/ProfileController.h>
#include <Swift/Controllers/Roster/RosterController.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/Settings/SettingsProvider.h>
#include <Swift/Controllers/ShowProfileController.h>
#include <Swift/Controllers/SoundEventController.h>
#include <Swift/Controllers/SoundPlayer.h>
#include <Swift/Controllers/StatusTracker.h>
#include <Swift/Controllers/Storages/CertificateStorageFactory.h>
#include <Swift/Controllers/Storages/CertificateStorageTrustChecker.h>
#include <Swift/Controllers/Storages/StoragesFactory.h>
#include <Swift/Controllers/SystemTray.h>
#include <Swift/Controllers/SystemTrayController.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UIInterfaces/LoginWindow.h>
#include <Swift/Controllers/UIInterfaces/LoginWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/MainWindow.h>
#include <Swift/Controllers/UIInterfaces/UIFactory.h>
#include <Swift/Controllers/WhiteboardManager.h>
#include <Swift/Controllers/XMLConsoleController.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/XMPPURIController.h>
#include <SwifTools/Dock/Dock.h>
diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript
index 105b44b..0c3127c 100644
--- a/Swift/Controllers/SConscript
+++ b/Swift/Controllers/SConscript
@@ -1,111 +1,109 @@
Import("env")
import Version
################################################################################
# Flags
################################################################################
if env["SCONS_STAGE"] == "flags" :
env["SWIFT_CONTROLLERS_FLAGS"] = {
"LIBPATH": [Dir(".")],
"LIBS": ["SwiftControllers"]
}
################################################################################
# Build
################################################################################
if env["SCONS_STAGE"] == "build" :
myenv = env.Clone()
myenv.BuildVersion("BuildVersion.h", project = "swift")
myenv.UseFlags(env["SWIFTEN_FLAGS"])
myenv.UseFlags(env["SWIFTEN_DEP_FLAGS"])
myenv.StaticLibrary("SwiftControllers", [
+ "AdHocController.cpp",
+ "AdHocManager.cpp",
+ "BlockListController.cpp",
"Chat/ChatController.cpp",
"Chat/ChatControllerBase.cpp",
+ "Chat/ChatMessageParser.cpp",
"Chat/ChatsManager.cpp",
"Chat/MUCController.cpp",
"Chat/MUCSearchController.cpp",
"Chat/UserSearchController.cpp",
- "Chat/ChatMessageParser.cpp",
- "ContactSuggester.cpp",
- "MainController.cpp",
- "ProfileController.cpp",
- "ShowProfileController.cpp",
+ "ChatMessageSummarizer.cpp",
+ "Contact.cpp",
"ContactEditController.cpp",
+ "ContactProvider.cpp",
+ "ContactSuggester.cpp",
+ "ContactsFromXMPPRoster.cpp",
+ "EventNotifier.cpp",
+ "EventWindowController.cpp",
"FileTransfer/FileTransferController.cpp",
"FileTransfer/FileTransferOverview.cpp",
"FileTransfer/FileTransferProgressInfo.cpp",
- "Roster/RosterController.cpp",
- "Roster/RosterGroupExpandinessPersister.cpp",
+ "FileTransferListController.cpp",
+ "Highlighting/HighlightAction.cpp",
+ "Highlighting/HighlightEditorController.cpp",
+ "Highlighting/HighlightManager.cpp",
+ "Highlighting/Highlighter.cpp",
+ "HistoryController.cpp",
+ "HistoryViewController.cpp",
+ "MainController.cpp",
+ "PresenceNotifier.cpp",
+ "PreviousStatusStore.cpp",
+ "ProfileController.cpp",
+ "ProfileSettingsProvider.cpp",
"Roster/ContactRosterItem.cpp",
"Roster/GroupRosterItem.cpp",
- "Roster/RosterItem.cpp",
"Roster/Roster.cpp",
+ "Roster/RosterController.cpp",
+ "Roster/RosterGroupExpandinessPersister.cpp",
+ "Roster/RosterItem.cpp",
"Roster/RosterVCardProvider.cpp",
"Roster/TableRoster.cpp",
- "EventWindowController.cpp",
- "SoundEventController.cpp",
- "SystemTrayController.cpp",
- "XMLConsoleController.cpp",
- "HistoryViewController.cpp",
- "HistoryController.cpp",
- "FileTransferListController.cpp",
- "BlockListController.cpp",
- "StatusTracker.cpp",
- "PresenceNotifier.cpp",
- "EventNotifier.cpp",
- "AdHocManager.cpp",
- "AdHocController.cpp",
- "XMPPEvents/EventController.cpp",
- "UIEvents/UIEvent.cpp",
- "UIInterfaces/XMLConsoleWidget.cpp",
- "UIInterfaces/ChatListWindow.cpp",
- "UIInterfaces/HighlightEditorWindow.cpp",
- "PreviousStatusStore.cpp",
- "ProfileSettingsProvider.cpp",
+ "SettingConstants.cpp",
"Settings/SettingsProviderHierachy.cpp",
"Settings/XMLSettingsProvider.cpp",
- "Storages/CertificateStorageFactory.cpp",
- "Storages/CertificateStorage.cpp",
+ "ShowProfileController.cpp",
+ "SoundEventController.cpp",
+ "StatusCache.cpp",
+ "StatusTracker.cpp",
+ "StatusUtil.cpp",
+ "Storages/AvatarFileStorage.cpp",
+ "Storages/CapsFileStorage.cpp",
"Storages/CertificateFileStorage.cpp",
"Storages/CertificateMemoryStorage.cpp",
- "Storages/AvatarFileStorage.cpp",
+ "Storages/CertificateStorage.cpp",
+ "Storages/CertificateStorageFactory.cpp",
"Storages/FileStorages.cpp",
"Storages/RosterFileStorage.cpp",
- "Storages/CapsFileStorage.cpp",
"Storages/VCardFileStorage.cpp",
- "StatusUtil.cpp",
+ "SystemTrayController.cpp",
"Translator.cpp",
- "XMPPURIController.cpp",
- "ChatMessageSummarizer.cpp",
- "SettingConstants.cpp",
+ "UIEvents/UIEvent.cpp",
+ "UIInterfaces/ChatListWindow.cpp",
+ "UIInterfaces/HighlightEditorWindow.cpp",
+ "UIInterfaces/XMLConsoleWidget.cpp",
"WhiteboardManager.cpp",
- "StatusCache.cpp",
- "HighlightAction.cpp",
- "HighlightEditorController.cpp",
- "HighlightManager.cpp",
- "HighlightRule.cpp",
- "Highlighter.cpp",
- "ContactsFromXMPPRoster.cpp",
- "ContactProvider.cpp",
- "Contact.cpp"
+ "XMLConsoleController.cpp",
+ "XMPPEvents/EventController.cpp",
+ "XMPPURIController.cpp",
])
env.Append(UNITTEST_SOURCES = [
- File("Roster/UnitTest/RosterControllerTest.cpp"),
- File("Roster/UnitTest/RosterTest.cpp"),
- File("Roster/UnitTest/LeastCommonSubsequenceTest.cpp"),
- File("Roster/UnitTest/TableRosterTest.cpp"),
- File("UnitTest/PreviousStatusStoreTest.cpp"),
- File("UnitTest/PresenceNotifierTest.cpp"),
+ File("Chat/UnitTest/ChatMessageParserTest.cpp"),
File("Chat/UnitTest/ChatsManagerTest.cpp"),
File("Chat/UnitTest/MUCControllerTest.cpp"),
- File("Chat/UnitTest/ChatMessageParserTest.cpp"),
- File("UnitTest/MockChatWindow.cpp"),
- File("UnitTest/ChatMessageSummarizerTest.cpp"),
+ File("Roster/UnitTest/LeastCommonSubsequenceTest.cpp"),
+ File("Roster/UnitTest/RosterControllerTest.cpp"),
+ File("Roster/UnitTest/RosterTest.cpp"),
+ File("Roster/UnitTest/TableRosterTest.cpp"),
File("Settings/UnitTest/SettingsProviderHierachyTest.cpp"),
- File("UnitTest/HighlightRuleTest.cpp"),
- File("UnitTest/ContactSuggesterTest.cpp")
+ File("UnitTest/ChatMessageSummarizerTest.cpp"),
+ File("UnitTest/ContactSuggesterTest.cpp"),
+ File("UnitTest/MockChatWindow.cpp"),
+ File("UnitTest/PresenceNotifierTest.cpp"),
+ File("UnitTest/PreviousStatusStoreTest.cpp"),
])
diff --git a/Swift/Controllers/SettingConstants.cpp b/Swift/Controllers/SettingConstants.cpp
index dedf56b..f0064ba 100644
--- a/Swift/Controllers/SettingConstants.cpp
+++ b/Swift/Controllers/SettingConstants.cpp
@@ -1,27 +1,28 @@
/*
- * Copyright (c) 2012-2016 Isode Limited.
+ * Copyright (c) 2012-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/SettingConstants.h>
namespace Swift {
const SettingsProvider::Setting<bool> SettingConstants::IDLE_GOES_OFFLINE = SettingsProvider::Setting<bool>("idleGoesOffline", false);
const SettingsProvider::Setting<int> SettingConstants::IDLE_TIMEOUT = SettingsProvider::Setting<int>("idleTimeout", 600);
const SettingsProvider::Setting<bool> SettingConstants::SHOW_NOTIFICATIONS = SettingsProvider::Setting<bool>("showNotifications", true);
const SettingsProvider::Setting<bool> SettingConstants::REQUEST_DELIVERYRECEIPTS = SettingsProvider::Setting<bool>("requestDeliveryReceipts", false);
const SettingsProvider::Setting<bool> SettingConstants::FORGET_PASSWORDS = SettingsProvider::Setting<bool>("forgetPasswords", false);
const SettingsProvider::Setting<bool> SettingConstants::REMEMBER_RECENT_CHATS = SettingsProvider::Setting<bool>("rememberRecentChats", true);
const SettingsProvider::Setting<std::string> SettingConstants::LAST_LOGIN_JID = SettingsProvider::Setting<std::string>("lastLoginJID", "");
const SettingsProvider::Setting<bool> SettingConstants::LOGIN_AUTOMATICALLY = SettingsProvider::Setting<bool>("loginAutomatically", false);
const SettingsProvider::Setting<bool> SettingConstants::SHOW_OFFLINE("showOffline", false);
const SettingsProvider::Setting<std::string> SettingConstants::EXPANDED_ROSTER_GROUPS("GroupExpandiness", "");
const SettingsProvider::Setting<bool> SettingConstants::PLAY_SOUNDS("playSounds", true);
const SettingsProvider::Setting<std::string> SettingConstants::HIGHLIGHT_RULES("highlightRules", "@");
+const SettingsProvider::Setting<std::string> SettingConstants::HIGHLIGHT_RULES_V2("highlightRulesV2", "@");
const SettingsProvider::Setting<std::string> SettingConstants::INVITE_AUTO_ACCEPT_MODE("inviteAutoAcceptMode", "presence");
const SettingsProvider::Setting<bool> SettingConstants::DISCONNECT_ON_CARD_REMOVAL("disconnectOnCardRemoval", true);
const SettingsProvider::Setting<bool> SettingConstants::SINGLE_SIGN_ON("singleSignOn", false);
}
diff --git a/Swift/Controllers/SettingConstants.h b/Swift/Controllers/SettingConstants.h
index 3f15c44..fec2d27 100644
--- a/Swift/Controllers/SettingConstants.h
+++ b/Swift/Controllers/SettingConstants.h
@@ -1,90 +1,97 @@
/*
- * Copyright (c) 2012-2016 Isode Limited.
+ * Copyright (c) 2012-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#include <Swift/Controllers/Settings/SettingsProvider.h>
namespace Swift {
/**
* This class contains the major setting keys for Swift.
*/
class SettingConstants {
public:
/**
* The #IDLE_GOES_OFFLINE setting specifies whether to close the XMPP connection when
* the user went idle.
*
* True for automatic close of the XMPP connection and false for only changing the presence on idle.
*/
static const SettingsProvider::Setting<bool> IDLE_GOES_OFFLINE;
/**
* The #IDLE_TIMEOUT setting specifieds the seconds the user has to be inactive at the
* desktop so the user is regarded as idle.
*/
static const SettingsProvider::Setting<int> IDLE_TIMEOUT;
static const SettingsProvider::Setting<bool> SHOW_NOTIFICATIONS;
/**
* The #REQUEST_DELIVERYRECEIPTS settings specifies whether to request delivery receipts
* for messages to contacts that support message receipts.
*/
static const SettingsProvider::Setting<bool> REQUEST_DELIVERYRECEIPTS;
static const SettingsProvider::Setting<bool> FORGET_PASSWORDS;
static const SettingsProvider::Setting<bool> REMEMBER_RECENT_CHATS;
static const SettingsProvider::Setting<std::string> LAST_LOGIN_JID;
static const SettingsProvider::Setting<bool> LOGIN_AUTOMATICALLY;
/**
* The #SHOW_OFFLINE setting specifies whether or not to show offline contacts in the
* roster.
*
* If set true Swift will show offline contacts; else not.
*/
static const SettingsProvider::Setting<bool> SHOW_OFFLINE;
/**
* The #EXPANDED_ROSTER_GROUPS setting specifies the list of groups that are expanded
* in the roster UI.
*
* Its value is a string with group names seperated by newlines.
*/
static const SettingsProvider::Setting<std::string> EXPANDED_ROSTER_GROUPS;
static const SettingsProvider::Setting<bool> PLAY_SOUNDS;
/**
* The #HIGHLIGHT_RULES setting specifies the highlight rules and the associated actions.
*
* Its value is a Boost serialized representation.
*/
static const SettingsProvider::Setting<std::string> HIGHLIGHT_RULES;
/**
+ * The #HIGHLIGHT_RULES_V2 setting specifies the second version of highlight configuration
+ * rules, incompatible to old highlight rules.
+ *
+ * Its value is a Boost serialized representation.
+ */
+ static const SettingsProvider::Setting<std::string> HIGHLIGHT_RULES_V2;
+ /**
* The #INVITE_AUTO_ACCEPT_MODE setting specifies how to handle invites to chat rooms.
*
* Supported values are:
* - "no" : It is up to the user whether to accept the invitation and enter a room or not.
* - "presence" : The invitation is automatically accepted if it is from a contact that is
* already allowed to see the user's presence status.
* - "domain" : The invitation is automatically accepted if it is from a contact that is
* already allowed to see the user's presence status or from a contact of user's domain.
*/
static const SettingsProvider::Setting<std::string> INVITE_AUTO_ACCEPT_MODE;
/**
* The #DISCONNECT_ON_CARD_REMOVAL setting
* specifies whether or not to sign out the user when
* the smartcard is removed.
*
* If set true Swift will sign out the user when the
* smart card is removed; else not.
*/
static const SettingsProvider::Setting<bool> DISCONNECT_ON_CARD_REMOVAL;
/**
* The #SINGLE_SIGN_ON setting
* specifies whether to log in using Single Sign On.
* This is currently supported on Windows.
*
* If set true Swift will use GSSAPI authentication to
* log in the user; else not.
*/
static const SettingsProvider::Setting<bool> SINGLE_SIGN_ON;
};
}
diff --git a/Swift/Controllers/SoundEventController.cpp b/Swift/Controllers/SoundEventController.cpp
index 5c7568f..2bafcca 100644
--- a/Swift/Controllers/SoundEventController.cpp
+++ b/Swift/Controllers/SoundEventController.cpp
@@ -1,56 +1,56 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swift/Controllers/SoundEventController.h>
#include <boost/bind.hpp>
-#include <Swift/Controllers/HighlightManager.h>
+#include <Swift/Controllers/Highlighting/HighlightManager.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/SoundPlayer.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/XMPPEvents/IncomingFileTransferEvent.h>
namespace Swift {
SoundEventController::SoundEventController(EventController* eventController, SoundPlayer* soundPlayer, SettingsProvider* settings, HighlightManager* highlightManager) {
settings_ = settings;
eventController_ = eventController;
soundPlayer_ = soundPlayer;
eventController_->onEventQueueEventAdded.connect(boost::bind(&SoundEventController::handleEventQueueEventAdded, this, _1));
highlightManager_ = highlightManager;
highlightManager_->onHighlight.connect(boost::bind(&SoundEventController::handleHighlight, this, _1));
settings_->onSettingChanged.connect(boost::bind(&SoundEventController::handleSettingChanged, this, _1));
playSounds_ = settings->getSetting(SettingConstants::PLAY_SOUNDS);
}
void SoundEventController::handleEventQueueEventAdded(std::shared_ptr<StanzaEvent> event) {
if (playSounds_ && std::dynamic_pointer_cast<IncomingFileTransferEvent>(event)) {
soundPlayer_->playSound(SoundPlayer::MessageReceived, "");
}
}
void SoundEventController::handleHighlight(const HighlightAction& action) {
- if (playSounds_ && action.playSound()) {
- soundPlayer_->playSound(SoundPlayer::MessageReceived, action.getSoundFile());
+ if (playSounds_ && action.getSoundFilePath()) {
+ soundPlayer_->playSound(SoundPlayer::MessageReceived, action.getSoundFilePath().get_value_or(""));
}
}
void SoundEventController::setPlaySounds(bool playSounds) {
playSounds_ = playSounds;
settings_->storeSetting(SettingConstants::PLAY_SOUNDS, playSounds);
}
void SoundEventController::handleSettingChanged(const std::string& settingPath) {
if (SettingConstants::PLAY_SOUNDS.getKey() == settingPath) {
playSounds_ = settings_->getSetting(SettingConstants::PLAY_SOUNDS);
}
}
}
diff --git a/Swift/Controllers/SoundEventController.h b/Swift/Controllers/SoundEventController.h
index e5b43b4..d612b18 100644
--- a/Swift/Controllers/SoundEventController.h
+++ b/Swift/Controllers/SoundEventController.h
@@ -1,34 +1,37 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#include <memory>
-#include <Swift/Controllers/HighlightAction.h>
+#include <Swift/Controllers/Highlighting/HighlightAction.h>
#include <Swift/Controllers/Settings/SettingsProvider.h>
#include <Swift/Controllers/XMPPEvents/StanzaEvent.h>
namespace Swift {
class EventController;
class SoundPlayer;
class HighlightManager;
class SoundEventController {
public:
SoundEventController(EventController* eventController, SoundPlayer* soundPlayer, SettingsProvider* settings, HighlightManager* highlightManager);
void setPlaySounds(bool playSounds);
bool getSoundEnabled() {return playSounds_;}
+
private:
void handleSettingChanged(const std::string& settingPath);
void handleEventQueueEventAdded(std::shared_ptr<StanzaEvent> event);
void handleHighlight(const HighlightAction& action);
+
+ private:
EventController* eventController_;
SoundPlayer* soundPlayer_;
bool playSounds_;
SettingsProvider* settings_;
HighlightManager* highlightManager_;
};
}
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index 8ee083d..7aaa90e 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -1,113 +1,140 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/optional.hpp>
#include <boost/signals2.hpp>
#include <Swiften/Base/Tristate.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/Elements/Form.h>
#include <Swiften/Elements/MUCOccupant.h>
#include <Swiften/Elements/SecurityLabelsCatalog.h>
#include <Swiften/MUC/MUCBookmark.h>
-#include <Swift/Controllers/HighlightManager.h>
+#include <Swift/Controllers/Highlighting/HighlightManager.h>
namespace Swift {
class AvatarManager;
class TreeWidget;
class Roster;
class TabComplete;
class RosterItem;
class ContactRosterItem;
class FileTransferController;
class UserSearchWindow;
class ChatWindow {
public:
class ChatMessagePart {
public:
virtual ~ChatMessagePart() {}
};
class ChatMessage {
public:
ChatMessage() {}
ChatMessage(const std::string& text) {
append(std::make_shared<ChatTextMessagePart>(text));
}
void append(const std::shared_ptr<ChatMessagePart>& part) {
parts_.push_back(part);
}
const std::vector<std::shared_ptr<ChatMessagePart> >& getParts() const {
return parts_;
}
void setParts(const std::vector<std::shared_ptr<ChatMessagePart> >& parts) {
parts_ = parts;
}
- void setFullMessageHighlightAction(const HighlightAction& action) {
- fullMessageHighlightAction_ = action;
+ void setHighlightActionSender(const HighlightAction& action) {
+ highlightActionSender_ = action;
}
- const HighlightAction& getFullMessageHighlightAction() const {
- return fullMessageHighlightAction_;
+ const HighlightAction& getHighlightActionSender() const {
+ return highlightActionSender_;
+ }
+
+ void setHighlightActionOwnMention(const HighlightAction& action) {
+ highlightActionOwnMention_ = action;
+ }
+
+ const HighlightAction& getHighlightActionOwnMention() const {
+ return highlightActionOwnMention_;
+ }
+
+ void setHighlightActionGroupMessage(const HighlightAction& action) {
+ highlightActionGroupMessage_ = action;
+ }
+
+ const HighlightAction& getHighlightActionGroupMessage() const {
+ return highlightActionGroupMessage_;
+ }
+
+ void setHighlightActonDirectMessage(const HighlightAction& action) {
+ highlightActionDirectMessage_ = action;
+ }
+
+ const HighlightAction& getHighlightActionDirectMessage() const {
+ return highlightActionDirectMessage_;
}
bool isMeCommand() const {
return isMeCommand_;
}
void setIsMeCommand(bool isMeCommand) {
isMeCommand_ = isMeCommand;
}
private:
std::vector<std::shared_ptr<ChatMessagePart> > parts_;
- HighlightAction fullMessageHighlightAction_;
+ HighlightAction highlightActionSender_;
+ HighlightAction highlightActionOwnMention_;
+ HighlightAction highlightActionGroupMessage_;
+ HighlightAction highlightActionDirectMessage_;
bool isMeCommand_ = false;
};
class ChatTextMessagePart : public ChatMessagePart {
public:
ChatTextMessagePart(const std::string& text) : text(text) {}
std::string text;
};
class ChatURIMessagePart : public ChatMessagePart {
public:
ChatURIMessagePart(const std::string& target) : target(target) {}
std::string target;
};
class ChatEmoticonMessagePart : public ChatMessagePart {
public:
std::string imagePath;
std::string alternativeText;
};
class ChatHighlightingMessagePart : public ChatMessagePart {
public:
HighlightAction action;
std::string text;
};
enum AckState {Pending, Received, Failed};
enum ReceiptState {ReceiptRequested, ReceiptReceived, ReceiptFailed};
diff --git a/Swift/Controllers/UnitTest/HighlightRuleTest.cpp b/Swift/Controllers/UnitTest/HighlightRuleTest.cpp
deleted file mode 100644
index 8d49d5d..0000000
--- a/Swift/Controllers/UnitTest/HighlightRuleTest.cpp
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (c) 2012 Maciej Niedzielski
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-/*
- * Copyright (c) 2014-2016 Isode Limited.
- * All rights reserved.
- * See the COPYING file for more information.
- */
-
-#include <string>
-#include <vector>
-
-#include <cppunit/extensions/HelperMacros.h>
-#include <cppunit/extensions/TestFactoryRegistry.h>
-
-#include <Swift/Controllers/HighlightRule.h>
-
-using namespace Swift;
-
-class HighlightRuleTest : public CppUnit::TestFixture {
- CPPUNIT_TEST_SUITE(HighlightRuleTest);
- CPPUNIT_TEST(testEmptyRuleNeverMatches);
- CPPUNIT_TEST(testKeyword);
- CPPUNIT_TEST(testNickKeyword);
- CPPUNIT_TEST(testNickWithoutOtherKeywords);
- CPPUNIT_TEST(testSender);
- CPPUNIT_TEST(testSenderAndKeyword);
- CPPUNIT_TEST(testWholeWords);
- CPPUNIT_TEST(testCase);
- CPPUNIT_TEST(testWholeWordsAndCase);
- CPPUNIT_TEST(testMUC);
- CPPUNIT_TEST_SUITE_END();
-
- public:
- void setUp() {
- std::vector<std::string> keywords;
- keywords.push_back("keyword1");
- keywords.push_back("KEYWORD2");
-
- std::vector<std::string> senders;
- senders.push_back("sender1");
- senders.push_back("SENDER2");
-
- emptyRule = new HighlightRule();
-
- keywordRule = new HighlightRule();
- keywordRule->setKeywords(keywords);
-
- keywordChatRule = new HighlightRule();
- keywordChatRule->setKeywords(keywords);
- keywordChatRule->setMatchChat(true);
-
- keywordNickChatRule = new HighlightRule();
- keywordNickChatRule->setKeywords(keywords);
- keywordNickChatRule->setNickIsKeyword(true);
- keywordNickChatRule->setMatchChat(true);
-
- nickChatRule = new HighlightRule();
- nickChatRule->setNickIsKeyword(true);
- nickChatRule->setMatchChat(true);
-
- nickRule = new HighlightRule();
- nickRule->setNickIsKeyword(true);
-
- senderRule = new HighlightRule();
- senderRule->setSenders(senders);
-
- senderChatRule = new HighlightRule();
- senderChatRule->setSenders(senders);
- senderChatRule->setMatchChat(true);
-
- senderKeywordChatRule = new HighlightRule();
- senderKeywordChatRule->setSenders(senders);
- senderKeywordChatRule->setKeywords(keywords);
- senderKeywordChatRule->setMatchChat(true);
-
- senderKeywordNickChatRule = new HighlightRule();
- senderKeywordNickChatRule->setSenders(senders);
- senderKeywordNickChatRule->setKeywords(keywords);
- senderKeywordNickChatRule->setNickIsKeyword(true);
- senderKeywordNickChatRule->setMatchChat(true);
-
- senderKeywordNickWordChatRule = new HighlightRule();
- senderKeywordNickWordChatRule->setSenders(senders);
- senderKeywordNickWordChatRule->setKeywords(keywords);
- senderKeywordNickWordChatRule->setNickIsKeyword(true);
- senderKeywordNickWordChatRule->setMatchWholeWords(true);
- senderKeywordNickWordChatRule->setMatchChat(true);
-
- senderKeywordNickCaseChatRule = new HighlightRule();
- senderKeywordNickCaseChatRule->setSenders(senders);
- senderKeywordNickCaseChatRule->setKeywords(keywords);
- senderKeywordNickCaseChatRule->setNickIsKeyword(true);
- senderKeywordNickCaseChatRule->setMatchCase(true);
- senderKeywordNickCaseChatRule->setMatchChat(true);
-
- senderKeywordNickCaseWordChatRule = new HighlightRule();
- senderKeywordNickCaseWordChatRule->setSenders(senders);
- senderKeywordNickCaseWordChatRule->setKeywords(keywords);
- senderKeywordNickCaseWordChatRule->setNickIsKeyword(true);
- senderKeywordNickCaseWordChatRule->setMatchCase(true);
- senderKeywordNickCaseWordChatRule->setMatchWholeWords(true);
- senderKeywordNickCaseWordChatRule->setMatchChat(true);
-
- senderKeywordNickMUCRule = new HighlightRule();
- senderKeywordNickMUCRule->setSenders(senders);
- senderKeywordNickMUCRule->setKeywords(keywords);
- senderKeywordNickMUCRule->setNickIsKeyword(true);
- senderKeywordNickMUCRule->setMatchMUC(true);
- }
-
- void tearDown() {
- delete emptyRule;
-
- delete keywordRule;
- delete keywordChatRule;
- delete keywordNickChatRule;
- delete nickChatRule;
- delete nickRule;
-
- delete senderRule;
- delete senderChatRule;
- delete senderKeywordChatRule;
- delete senderKeywordNickChatRule;
-
- delete senderKeywordNickWordChatRule;
- delete senderKeywordNickCaseChatRule;
- delete senderKeywordNickCaseWordChatRule;
-
- delete senderKeywordNickMUCRule;
- }
-
- void testEmptyRuleNeverMatches() {
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "from", "nick", HighlightRule::MUCMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "from", "", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "from", "", HighlightRule::MUCMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "", "nick", HighlightRule::MUCMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "from", "nick", HighlightRule::MUCMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "", "", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "", "", HighlightRule::MUCMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "from", "", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "from", "", HighlightRule::MUCMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "", "nick", HighlightRule::MUCMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "", "", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "", "", HighlightRule::MUCMessage), false);
- }
-
- void testKeyword() {
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("body", "from", "nick", HighlightRule::MUCMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("keyword1", "from", "nick", HighlightRule::MUCMessage), false);
- CPPUNIT_ASSERT_EQUAL(keywordRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("body", "sender contains keyword1", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("abc keyword1 xyz", "from", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("abckeyword1xyz", "from", "nick", HighlightRule::ChatMessage), true);
-
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("KEYword1", "from", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("abc KEYword1 xyz", "from", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("abcKEYword1xyz", "from", "nick", HighlightRule::ChatMessage), true);
-
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("keyword2", "from", "nick", HighlightRule::ChatMessage), true);
- }
-
- void testNickKeyword() {
- CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("body contains nick", "from", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("body contains nick", "from", "nick", HighlightRule::MUCMessage), false);
- CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("body contains nick", "from", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), true);
-
- CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("body", "sender contains nick", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("body contains mixed-case NiCk", "sender", "nick", HighlightRule::ChatMessage), true);
-
- CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("nickname", "from", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("NIckNAME", "from", "nick", HighlightRule::ChatMessage), true);
-
- CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("body", "from", "", HighlightRule::ChatMessage), false);
- }
-
- void testNickWithoutOtherKeywords() {
- CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("body contains nick", "from", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("body contains nick", "from", "nick", HighlightRule::MUCMessage), false);
- CPPUNIT_ASSERT_EQUAL(nickRule->isMatch("body contains nick", "from", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("body", "sender contains nick but it does't matter", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("body contains mixed-case NiCk", "from", "nick", HighlightRule::ChatMessage), true);
-
- CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("nickname", "from", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("NIckNAME", "from", "nick", HighlightRule::ChatMessage), true);
-
- // there are no keywords in this rule and empty nick is not treated as a keyword, so we don't check for keywords to get a match
- CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("body", "from", "", HighlightRule::ChatMessage), true);
- }
-
- void testSender() {
- CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "from", "nick", HighlightRule::MUCMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "sender1", "nick", HighlightRule::MUCMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderRule->isMatch("body contains sender1", "from", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "abc sender1 xyz", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "abcsender1xyz", "nick", HighlightRule::ChatMessage), true);
-
- CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "SENDer1", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "abc SENDer1 xyz", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "abcSENDer1xyz", "nick", HighlightRule::ChatMessage), true);
-
- CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "sender2", "nick", HighlightRule::ChatMessage), true);
- }
-
- void testSenderAndKeyword() {
- CPPUNIT_ASSERT_EQUAL(senderKeywordChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordChatRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordChatRule->isMatch("keyword1", "sender1", "nick", HighlightRule::ChatMessage), true);
- }
-
- void testWholeWords() {
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("keyword1", "sender1", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("xkeyword1", "sender1", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("keyword1", "xsender1", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("body contains nick", "sender1", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("body contains xnick", "sender1", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("KEYword1", "SENDer1", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("body contains NiCk", "sender1", "nick", HighlightRule::ChatMessage), true);
- }
-
- void testCase() {
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("keyword1", "sender1", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("xkeyword1", "xsender1", "nick", HighlightRule::ChatMessage), true);
-
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("body contains nick", "sender1", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("body contains xnick", "sender1", "nick", HighlightRule::ChatMessage), true);
-
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("KEYword1", "SENDer1", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("keyword1", "SENDer1", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("KEYword1", "sender1", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("body contains NiCk", "sender1", "nick", HighlightRule::ChatMessage), false);
- }
-
- void testWholeWordsAndCase() {
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("keyword1", "sender1", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("xkeyword1", "sender1", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("keyword1", "xsender1", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("body contains nick", "sender1", "nick", HighlightRule::ChatMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("body contains xnick", "sender1", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("KEYword1", "SENDer1", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("keyword1", "SENDer1", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("KEYword1", "sender1", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("body contains NiCk", "sender1", "nick", HighlightRule::ChatMessage), false);
- }
-
- void testMUC() {
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickMUCRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false);
-
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickMUCRule->isMatch("keyword1", "sender1", "nick", HighlightRule::ChatMessage), false);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickMUCRule->isMatch("keyword1", "sender1", "nick", HighlightRule::MUCMessage), true);
- CPPUNIT_ASSERT_EQUAL(senderKeywordNickMUCRule->isMatch("body contains nick", "sender1", "nick", HighlightRule::MUCMessage), true);
- }
-
- private:
- HighlightRule* emptyRule;
-
- HighlightRule* keywordRule;
- HighlightRule* keywordChatRule;
- HighlightRule* keywordNickChatRule;
- HighlightRule* nickChatRule;
- HighlightRule* nickRule;
-
- HighlightRule* senderRule;
- HighlightRule* senderChatRule;
- HighlightRule* senderKeywordChatRule;
- HighlightRule* senderKeywordNickChatRule;
-
- HighlightRule* senderKeywordNickWordChatRule;
- HighlightRule* senderKeywordNickCaseChatRule;
- HighlightRule* senderKeywordNickCaseWordChatRule;
-
- HighlightRule* senderKeywordNickMUCRule;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION(HighlightRuleTest);
diff --git a/Swift/Controllers/XMPPEvents/MessageEvent.h b/Swift/Controllers/XMPPEvents/MessageEvent.h
index 7af2be6..12f4c48 100644
--- a/Swift/Controllers/XMPPEvents/MessageEvent.h
+++ b/Swift/Controllers/XMPPEvents/MessageEvent.h
@@ -1,47 +1,67 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
#include <cassert>
#include <memory>
#include <Swiften/Elements/Message.h>
#include <Swift/Controllers/XMPPEvents/StanzaEvent.h>
namespace Swift {
class MessageEvent : public StanzaEvent {
public:
+ class SystemNotification {
+ public:
+ SystemNotification(const std::string& title, const std::string& message) : title(title), message(message) {
+ }
+
+ public:
+ std::string title;
+ std::string message;
+ };
+
+ public:
typedef std::shared_ptr<MessageEvent> ref;
MessageEvent(std::shared_ptr<Message> stanza) : stanza_(stanza), targetsMe_(true) {}
std::shared_ptr<Message> getStanza() {return stanza_;}
bool isReadable() {
return getStanza()->isError() || !getStanza()->getBody().get_value_or("").empty();
}
+ void addNotification(const std::string& title, const std::string& message) {
+ systemNotifications_.push_back(SystemNotification(title, message));
+ }
+
+ const std::vector<SystemNotification>& getNotifications() const {
+ return systemNotifications_;
+ }
+
void read() {
assert (isReadable());
conclude();
}
void setTargetsMe(bool targetsMe) {
targetsMe_ = targetsMe;
}
bool targetsMe() const {
return targetsMe_;
}
private:
std::shared_ptr<Message> stanza_;
+ std::vector<SystemNotification> systemNotifications_;
bool targetsMe_;
};
}