summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Maudsley <richard.maudsley@isode.com>2014-07-16 12:37:25 (GMT)
committerSwift Review <review@swift.im>2014-07-29 08:36:54 (GMT)
commit9c5c731845881996f45b32ea6de12e0647f4634d (patch)
tree70331a822814ade469f07231ff0bf6dfbfa1fcde
parent690cb7e85ff9dadbfca3e3bc91826161011712f1 (diff)
downloadswift-contrib-9c5c731845881996f45b32ea6de12e0647f4634d.zip
swift-contrib-9c5c731845881996f45b32ea6de12e0647f4634d.tar.bz2
Prevent nick highlight rule highlighting the entire message and remove default highlight colours
Test-Information: Add a nick highlight rule. Verify that it is triggered correctly in MUCs and that only the nick text is highlighted. Added unit tests. Change-Id: I9af1c900f4767383745afd36a5eadbe08f606432
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp4
-rw-r--r--Swift/Controllers/Chat/ChatMessageParser.cpp9
-rw-r--r--Swift/Controllers/Chat/ChatMessageParser.h4
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp36
-rw-r--r--Swift/Controllers/HighlightAction.cpp2
-rw-r--r--Swift/Controllers/HighlightAction.h11
-rw-r--r--Swift/Controllers/HighlightRule.cpp12
-rw-r--r--Swift/Controllers/HighlightRule.h2
-rw-r--r--Swift/Controllers/Highlighter.h1
-rw-r--r--Swift/QtUI/QtHighlightEditor.cpp45
-rw-r--r--Swift/QtUI/QtHighlightEditor.ui7
-rw-r--r--Swift/QtUI/QtWebKitChatView.cpp8
12 files changed, 88 insertions, 53 deletions
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index 24341e6..519deda 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -173,79 +173,79 @@ void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool
#endif
}
void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::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();
}
std::string ChatControllerBase::addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, const boost::shared_ptr<SecurityLabel> label, const boost::filesystem::path& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) {
if (boost::starts_with(message, "/me ")) {
return chatWindow_->addAction(chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight);
} else {
- return chatWindow_->addMessage(chatMessageParser_->parseMessageBody(message,senderIsSelf), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight);
+ return chatWindow_->addMessage(chatMessageParser_->parseMessageBody(message,highlighter_->getNick(),senderIsSelf), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight);
}
}
void ChatControllerBase::replaceMessage(const std::string& message, const std::string& id, bool senderIsSelf, const boost::posix_time::ptime& time, const HighlightAction& highlight) {
if (boost::starts_with(message, "/me ")) {
chatWindow_->replaceWithAction(chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second), id, time, highlight);
} else {
- chatWindow_->replaceMessage(chatMessageParser_->parseMessageBody(message,senderIsSelf), id, time, highlight);
+ chatWindow_->replaceMessage(chatMessageParser_->parseMessageBody(message,highlighter_->getNick(),senderIsSelf), id, time, highlight);
}
}
bool ChatControllerBase::isFromContact(const JID& from) {
return from.toBare() == toJID_.toBare();
}
void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) {
preHandleIncomingMessage(messageEvent);
if (messageEvent->isReadable() && !messageEvent->getConcluded()) {
unreadMessages_.push_back(messageEvent);
if (messageEvent->targetsMe()) {
targetedUnreadMessages_.push_back(messageEvent);
}
}
boost::shared_ptr<Message> message = messageEvent->getStanza();
std::string body = message->getBody();
HighlightAction highlight;
if (message->isError()) {
if (!message->getTo().getResource().empty()) {
std::string errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't send message: %1%")) % getErrorMessage(message->getPayload<ErrorPayload>()));
chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage));
}
}
else if (messageEvent->getStanza()->getPayload<MUCInvitationPayload>()) {
handleMUCInvitation(messageEvent->getStanza());
return;
}
else if (messageEvent->getStanza()->getPayload<MUCUserPayload>() && messageEvent->getStanza()->getPayload<MUCUserPayload>()->getInvite()) {
handleMediatedMUCInvitation(messageEvent->getStanza());
return;
}
else {
if (!messageEvent->isReadable()) {
return;
diff --git a/Swift/Controllers/Chat/ChatMessageParser.cpp b/Swift/Controllers/Chat/ChatMessageParser.cpp
index 09d93ac..5a608db 100644
--- a/Swift/Controllers/Chat/ChatMessageParser.cpp
+++ b/Swift/Controllers/Chat/ChatMessageParser.cpp
@@ -1,95 +1,95 @@
/*
* Copyright (c) 2013-2014 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <Swift/Controllers/Chat/ChatMessageParser.h>
#include <vector>
#include <utility>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/algorithm/string.hpp>
#include <Swiften/Base/Regex.h>
#include <Swiften/Base/foreach.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) {
}
typedef std::pair<std::string, std::string> StringPair;
- ChatWindow::ChatMessage ChatMessageParser::parseMessageBody(const std::string& body, bool senderIsSelf) {
+ ChatWindow::ChatMessage ChatMessageParser::parseMessageBody(const std::string& body, const std::string& nick, bool senderIsSelf) {
ChatWindow::ChatMessage parsedMessage;
std::string remaining = body;
/* Parse one, URLs */
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(boost::make_shared<ChatWindow::ChatURIMessagePart>(part));
}
else {
parsedMessage.append(boost::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);
+ parsedMessage = splitHighlight(parsedMessage, nick);
}
return parsedMessage;
}
ChatWindow::ChatMessage ChatMessageParser::emoticonHighlight(const ChatWindow::ChatMessage& message)
{
ChatWindow::ChatMessage parsedMessage = message;
std::string regexString;
/* Parse two, emoticons */
foreach (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;
foreach (boost::shared_ptr<ChatWindow::ChatMessagePart> part, parsedMessage.getParts()) {
boost::shared_ptr<ChatWindow::ChatTextMessagePart> textPart;
if ((textPart = boost::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();
@@ -106,82 +106,83 @@ namespace Swift {
if (start != matchStart) {
/* If we're skipping over plain text since the previous emoticon, record it as plain text */
newMessage.append(boost::make_shared<ChatWindow::ChatTextMessagePart>(std::string(start, matchStart)));
}
boost::shared_ptr<ChatWindow::ChatEmoticonMessagePart> emoticonPart = boost::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(boost::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 = newMessage;
}
return parsedMessage;
}
- ChatWindow::ChatMessage ChatMessageParser::splitHighlight(const ChatWindow::ChatMessage& message)
+ ChatWindow::ChatMessage ChatMessageParser::splitHighlight(const ChatWindow::ChatMessage& message, const std::string& nick)
{
ChatWindow::ChatMessage parsedMessage = message;
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 */
}
- foreach(const boost::regex &regex, rule.getKeywordRegex()) {
+ const std::vector<boost::regex> keywordRegex = rule.getKeywordRegex(nick);
+ foreach(const boost::regex& regex, keywordRegex) {
ChatWindow::ChatMessage newMessage;
foreach (boost::shared_ptr<ChatWindow::ChatMessagePart> part, parsedMessage.getParts()) {
boost::shared_ptr<ChatWindow::ChatTextMessagePart> textPart;
if ((textPart = boost::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(boost::make_shared<ChatWindow::ChatTextMessagePart>(std::string(start, matchStart)));
}
boost::shared_ptr<ChatWindow::ChatHighlightingMessagePart> highlightPart = boost::make_shared<ChatWindow::ChatHighlightingMessagePart>();
highlightPart->text = match.str();
highlightPart->foregroundColor = rule.getAction().getTextColor();
highlightPart->backgroundColor = rule.getAction().getTextBackground();
newMessage.append(highlightPart);
start = matchEnd;
}
if (start != text.end()) {
/* If there's plain text after the last emoticon, record it */
newMessage.append(boost::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);
}
}
diff --git a/Swift/Controllers/Chat/ChatMessageParser.h b/Swift/Controllers/Chat/ChatMessageParser.h
index cff4ffa..2f5c171 100644
--- a/Swift/Controllers/Chat/ChatMessageParser.h
+++ b/Swift/Controllers/Chat/ChatMessageParser.h
@@ -1,26 +1,26 @@
/*
* Copyright (c) 2013-2014 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <string>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
namespace Swift {
class ChatMessageParser {
public:
ChatMessageParser(const std::map<std::string, std::string>& emoticons, HighlightRulesListPtr highlightRules, bool mucMode = false);
- ChatWindow::ChatMessage parseMessageBody(const std::string& body, bool senderIsSelf = false);
+ ChatWindow::ChatMessage parseMessageBody(const std::string& body, const std::string& nick = "", bool senderIsSelf = false);
private:
ChatWindow::ChatMessage emoticonHighlight(const ChatWindow::ChatMessage& parsedMessage);
- ChatWindow::ChatMessage splitHighlight(const ChatWindow::ChatMessage& parsedMessage);
+ ChatWindow::ChatMessage splitHighlight(const ChatWindow::ChatMessage& parsedMessage, const std::string& nick);
std::map<std::string, std::string> emoticons_;
HighlightRulesListPtr highlightRules_;
bool mucMode_;
};
}
diff --git a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
index 5dca63a..2a07654 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
@@ -53,70 +53,82 @@ public:
CPPUNIT_ASSERT_EQUAL(text, part->text);
}
void assertURL(const ChatWindow::ChatMessage& result, size_t index, const std::string& text) {
boost::shared_ptr<ChatWindow::ChatURIMessagePart> part = boost::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);
return rule;
}
static const HighlightRulesListPtr ruleListFromKeyword(const std::string& keyword, bool matchCase, bool matchWholeWord)
{
boost::shared_ptr<HighlightManager::HighlightRulesList> list = boost::make_shared<HighlightManager::HighlightRulesList>();
list->addRule(ruleFromKeyword(keyword, matchCase, matchWholeWord));
return list;
}
static const HighlightRulesListPtr ruleListFromKeywords(const HighlightRule &rule1, const HighlightRule &rule2)
{
boost::shared_ptr<HighlightManager::HighlightRulesList> list = boost::make_shared<HighlightManager::HighlightRulesList>();
list->addRule(rule1);
list->addRule(rule2);
return list;
}
+ static HighlightRulesListPtr ruleListWithNickHighlight()
+ {
+ HighlightRule rule;
+ rule.setMatchChat(true);
+ rule.setNickIsKeyword(true);
+ rule.setMatchCase(true);
+ rule.setMatchWholeWords(true);
+ boost::shared_ptr<HighlightManager::HighlightRulesList> list = boost::make_shared<HighlightManager::HighlightRulesList>();
+ list->addRule(rule);
+ return list;
+ }
+
void testFullBody() {
const std::string no_special_message = "a message with no special content";
ChatMessageParser testling(emoticons_, boost::make_shared<HighlightManager::HighlightRulesList>());
ChatWindow::ChatMessage result = testling.parseMessageBody(no_special_message);
assertText(result, 0, no_special_message);
testling = ChatMessageParser(emoticons_, ruleListFromKeyword("trigger", false, false));
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");
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");
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");
@@ -142,70 +154,94 @@ public:
assertText(result, 0, "zero ");
assertHighlight(result, 1, "oNe");
assertText(result, 2, " two ");
assertHighlight(result, 3, "tHrEe");
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");
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");
assertText(result, 2, "two");
assertHighlight(result, 3, "three");
testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", true, false), ruleFromKeyword("three", false, false)));
result = testling.parseMessageBody("zeroOnEtwoThReE");
assertText(result, 0, "zeroOnEtwo");
assertHighlight(result, 1, "ThReE");
testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, true), ruleFromKeyword("three", false, false)));
result = testling.parseMessageBody("zeroonetwothree");
assertText(result, 0, "zeroonetwo");
assertHighlight(result, 1, "three");
testling = ChatMessageParser(emoticons_, ruleListFromKeywords(ruleFromKeyword("one", false, true), ruleFromKeyword("three", false, true)));
result = testling.parseMessageBody("zeroonetwothree");
assertText(result, 0, "zeroonetwothree");
+
+ testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
+ result = testling.parseMessageBody("Alice", "Alice");
+ assertHighlight(result, 0, "Alice");
+
+ testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
+ result = testling.parseMessageBody("TextAliceText", "Alice");
+ assertText(result, 0, "TextAliceText");
+
+ testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
+ result = testling.parseMessageBody("Text Alice Text", "Alice");
+ assertText(result, 0, "Text ");
+ assertHighlight(result, 1, "Alice");
+ assertText(result, 2, " Text");
+
+ testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
+ result = testling.parseMessageBody("Alice Text", "Alice");
+ assertHighlight(result, 0, "Alice");
+ assertText(result, 1, " Text");
+
+ testling = ChatMessageParser(emoticons_, ruleListWithNickHighlight());
+ result = testling.parseMessageBody("Text Alice", "Alice");
+ assertText(result, 0, "Text ");
+ assertHighlight(result, 1, "Alice");
}
void testOneEmoticon() {
ChatMessageParser testling(emoticons_, boost::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_, boost::make_shared<HighlightManager::HighlightRulesList>());
ChatWindow::ChatMessage result = testling.parseMessageBody(":)");
assertEmoticon(result, 0, smile1_, smile1Path_);
}
void testHiddenEmoticon() {
ChatMessageParser testling(emoticons_, boost::make_shared<HighlightManager::HighlightRulesList>());
ChatWindow::ChatMessage result = testling.parseMessageBody("b:)a");
assertText(result, 0, "b:)a");
}
void testEndlineEmoticon() {
ChatMessageParser testling(emoticons_, boost::make_shared<HighlightManager::HighlightRulesList>());
ChatWindow::ChatMessage result = testling.parseMessageBody("Lazy:)");
assertText(result, 0, "Lazy");
assertEmoticon(result, 1, smile1_, smile1Path_);
}
void testBoundedEmoticons() {
ChatMessageParser testling(emoticons_, boost::make_shared<HighlightManager::HighlightRulesList>());
ChatWindow::ChatMessage result = testling.parseMessageBody(":)Lazy:(");
assertEmoticon(result, 0, smile1_, smile1Path_);
assertText(result, 1, "Lazy");
diff --git a/Swift/Controllers/HighlightAction.cpp b/Swift/Controllers/HighlightAction.cpp
index d4d199d..492d4d2 100644
--- a/Swift/Controllers/HighlightAction.cpp
+++ b/Swift/Controllers/HighlightAction.cpp
@@ -1,28 +1,28 @@
/*
* Copyright (c) 2012 Maciej Niedzielski
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
#include <Swift/Controllers/HighlightAction.h>
namespace Swift {
-void HighlightAction::setHighlightText(bool highlightText)
+void HighlightAction::setHighlightAllText(bool highlightText)
{
highlightText_ = highlightText;
if (!highlightText_) {
textColor_.clear();
textBackground_.clear();
}
}
void HighlightAction::setPlaySound(bool playSound)
{
playSound_ = playSound;
if (!playSound_) {
soundFile_.clear();
}
}
}
diff --git a/Swift/Controllers/HighlightAction.h b/Swift/Controllers/HighlightAction.h
index de1f201..90768a7 100644
--- a/Swift/Controllers/HighlightAction.h
+++ b/Swift/Controllers/HighlightAction.h
@@ -1,73 +1,76 @@
/*
* Copyright (c) 2012 Maciej Niedzielski
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
/*
* Copyright (c) 2014 Kevin Smith and Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <string>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
namespace Swift {
class HighlightRule;
class HighlightAction {
public:
HighlightAction() : highlightText_(false), playSound_(false) {}
- bool highlightText() const { return highlightText_; }
- void setHighlightText(bool highlightText);
+ /**
+ * Gets the flag that indicates the entire message should be highlighted.
+ */
+ bool highlightAllText() const { return highlightText_; }
+ void setHighlightAllText(bool highlightText);
/**
- * Gets the foreground highlight color. If the string is empty, assume a default color.
+ * Gets the foreground highlight color.
*/
const std::string& getTextColor() const { return textColor_; }
void setTextColor(const std::string& textColor) { textColor_ = textColor; }
/**
- * Gets the background highlight color. If the string is empty, assume a default color.
+ * Gets the background highlight color.
*/
const std::string& getTextBackground() const { return textBackground_; }
void setTextBackground(const std::string& textBackground) { textBackground_ = textBackground; }
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 !highlightText_ && !playSound_; }
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive & ar, const unsigned int version);
bool highlightText_;
std::string textColor_;
std::string textBackground_;
bool playSound_;
std::string soundFile_;
};
template<class Archive>
void HighlightAction::serialize(Archive& ar, const unsigned int /*version*/)
{
ar & highlightText_;
ar & textColor_;
ar & textBackground_;
ar & playSound_;
ar & soundFile_;
diff --git a/Swift/Controllers/HighlightRule.cpp b/Swift/Controllers/HighlightRule.cpp
index f1a5235..3251067 100644
--- a/Swift/Controllers/HighlightRule.cpp
+++ b/Swift/Controllers/HighlightRule.cpp
@@ -70,70 +70,82 @@ bool HighlightRule::isMatch(const std::string& body, const std::string& sender,
bool matchesSender = senders_.empty();
if (!matchesKeyword && nickIsKeyword_ && !nick.empty()) {
if (boost::regex_search(body, regexFromString(nick))) {
matchesKeyword = true;
}
}
foreach (const boost::regex & 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();
}
diff --git a/Swift/Controllers/HighlightRule.h b/Swift/Controllers/HighlightRule.h
index ae1a3d3..c0226bc 100644
--- a/Swift/Controllers/HighlightRule.h
+++ b/Swift/Controllers/HighlightRule.h
@@ -8,71 +8,71 @@
* Copyright (c) 2014 Kevin Smith and Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <vector>
#include <string>
#include <boost/regex.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.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>&);
- const std::vector<boost::regex>& getKeywordRegex() const { return keywordRegex_; }
+ std::vector<boost::regex> getKeywordRegex(const std::string& nick) const;
bool getNickIsKeyword() const { return nickIsKeyword_; }
void setNickIsKeyword(bool);
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_;
diff --git a/Swift/Controllers/Highlighter.h b/Swift/Controllers/Highlighter.h
index d026f29..d5d846b 100644
--- a/Swift/Controllers/Highlighter.h
+++ b/Swift/Controllers/Highlighter.h
@@ -1,37 +1,38 @@
/*
* Copyright (c) 2012 Maciej Niedzielski
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
#pragma once
#include <string>
#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 findAction(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/QtUI/QtHighlightEditor.cpp b/Swift/QtUI/QtHighlightEditor.cpp
index ce07003..8488d7d 100644
--- a/Swift/QtUI/QtHighlightEditor.cpp
+++ b/Swift/QtUI/QtHighlightEditor.cpp
@@ -8,99 +8,97 @@
* Copyright (c) 2014 Kevin Smith and Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <cassert>
#include <boost/lexical_cast.hpp>
#include <Swift/QtUI/UserSearch/QtSuggestingJIDInput.h>
#include <Swift/Controllers/HighlightManager.cpp>
#include <Swift/QtUI/QtHighlightEditor.h>
#include <Swift/QtUI/QtSwiftUtil.h>
#include <Swift/QtUI/QtSettingsProvider.h>
#include <QTreeWidgetItem>
#include <QFileDialog>
namespace Swift {
QtHighlightEditor::QtHighlightEditor(QtSettingsProvider* settings, QWidget* parent)
: QWidget(parent), settings_(settings), previousRow_(-1)
{
ui_.setupUi(this);
connect(ui_.listWidget, SIGNAL(currentRowChanged(int)), SLOT(onCurrentRowChanged(int)));
connect(ui_.newButton, SIGNAL(clicked()), SLOT(onNewButtonClicked()));
connect(ui_.deleteButton, SIGNAL(clicked()), SLOT(onDeleteButtonClicked()));
connect(ui_.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(onApplyButtonClick()));
connect(ui_.buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), SLOT(onCancelButtonClick()));
connect(ui_.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(onOkButtonClick()));
connect(ui_.noColorRadio, SIGNAL(clicked()), SLOT(colorOtherSelect()));
- connect(ui_.defaultColorRadio, SIGNAL(clicked()), SLOT(colorOtherSelect()));
connect(ui_.customColorRadio, SIGNAL(clicked()), SLOT(colorCustomSelect()));
connect(ui_.noSoundRadio, SIGNAL(clicked()), SLOT(soundOtherSelect()));
connect(ui_.defaultSoundRadio, SIGNAL(clicked()), SLOT(soundOtherSelect()));
connect(ui_.customSoundRadio, SIGNAL(clicked()), SLOT(soundCustomSelect()));
/* replace the static line-edit control with the roster autocompleter */
ui_.dummySenderName->setVisible(false);
jid_ = new QtSuggestingJIDInput(this, settings);
ui_.senderName->addWidget(jid_);
jid_->onUserSelected.connect(boost::bind(&QtHighlightEditor::handleOnUserSelected, this, _1));
/* handle autocomplete */
connect(jid_, SIGNAL(textEdited(QString)), SLOT(handleContactSuggestionRequested(QString)));
/* we need to be notified if any of the state changes so that we can update our textual rule description */
connect(ui_.chatRadio, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.roomRadio, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.nickIsKeyword, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.allMsgRadio, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.senderRadio, SIGNAL(clicked()), SLOT(widgetClick()));
connect(jid_, SIGNAL(textChanged(const QString&)), SLOT(widgetClick()));
connect(ui_.keywordRadio, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.keyword, SIGNAL(textChanged(const QString&)), SLOT(widgetClick()));
connect(ui_.matchPartialWords, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.matchCase, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.noColorRadio, SIGNAL(clicked()), SLOT(widgetClick()));
- connect(ui_.defaultColorRadio, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.customColorRadio, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.noSoundRadio, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.defaultSoundRadio, SIGNAL(clicked()), SLOT(widgetClick()));
connect(ui_.customSoundRadio, SIGNAL(clicked()), SLOT(widgetClick()));
/* allow selection of a custom sound file */
connect(ui_.soundFileButton, SIGNAL(clicked()), SLOT(selectSoundFile()));
/* if these are not needed, then they should be removed */
ui_.moveUpButton->setVisible(false);
ui_.moveDownButton->setVisible(false);
setWindowTitle(tr("Highlight Rules"));
}
QtHighlightEditor::~QtHighlightEditor()
{
}
std::string formatShortDescription(const HighlightRule &rule)
{
const std::string chatOrRoom = (rule.getMatchChat() ? "chat" : "room");
std::vector<std::string> senders = rule.getSenders();
std::vector<std::string> keywords = rule.getKeywords();
if (senders.empty() && keywords.empty() && !rule.getNickIsKeyword()) {
return std::string("All ") + chatOrRoom + " messages.";
}
if (rule.getNickIsKeyword()) {
return std::string("All ") + chatOrRoom + " messages that mention my name.";
}
if (!senders.empty()) {
@@ -273,71 +271,70 @@ void QtHighlightEditor::setChildWidgetStates()
ui_.matchPartialWords->setEnabled(false);
ui_.matchCase->setEnabled(false);
}
if (ui_.chatRadio->isChecked()) {
ui_.allMsgRadio->setText(tr("Apply to all chat messages"));
} else {
ui_.allMsgRadio->setText(tr("Apply to all room messages"));
}
}
void QtHighlightEditor::widgetClick()
{
setChildWidgetStates();
HighlightRule rule = ruleFromDialog();
if (ui_.listWidget->currentItem()) {
ui_.listWidget->currentItem()->setText(P2QSTRING(formatShortDescription(rule)));
}
}
void QtHighlightEditor::disableDialog()
{
ui_.chatRadio->setEnabled(false);
ui_.roomRadio->setEnabled(false);
ui_.allMsgRadio->setEnabled(false);
ui_.nickIsKeyword->setEnabled(false);
ui_.senderRadio->setEnabled(false);
ui_.dummySenderName->setEnabled(false);
ui_.keywordRadio->setEnabled(false);
ui_.keyword->setEnabled(false);
ui_.matchPartialWords->setEnabled(false);
ui_.matchCase->setEnabled(false);
ui_.noColorRadio->setEnabled(false);
- ui_.defaultColorRadio->setEnabled(false);
ui_.customColorRadio->setEnabled(false);
ui_.foregroundColor->setEnabled(false);
ui_.backgroundColor->setEnabled(false);
ui_.noSoundRadio->setEnabled(false);
ui_.defaultSoundRadio->setEnabled(false);
ui_.customSoundRadio->setEnabled(false);
ui_.soundFile->setEnabled(false);
ui_.soundFileButton->setEnabled(false);
}
void QtHighlightEditor::handleContactSuggestionRequested(const QString& text)
{
std::string stdText = Q2PSTRING(text);
onContactSuggestionsRequested(stdText);
}
void QtHighlightEditor::selectSoundFile()
{
QString path = QFileDialog::getOpenFileName(this, tr("Select sound file..."), QString(), "Sounds (*.wav)");
ui_.soundFile->setText(path);
}
void QtHighlightEditor::handleOnUserSelected(const JID& jid) {
/* this might seem like it should be standard behaviour for the suggesting input box, but is not desirable in all cases */
jid_->setText(P2QSTRING(jid.toString()));
}
void QtHighlightEditor::populateList()
{
previousRow_ = -1;
ui_.listWidget->clear();
HighlightRulesListPtr rules = highlightManager_->getRules();
for (size_t i = 0; i < rules->getSize(); ++i) {
const HighlightRule& rule = rules->getRule(i);
QListWidgetItem *item = new QListWidgetItem();
@@ -367,158 +364,150 @@ int QtHighlightEditor::getSelectedRow() const
}
}
return -1;
}
HighlightRule QtHighlightEditor::ruleFromDialog()
{
HighlightRule rule;
if (ui_.chatRadio->isChecked()) {
rule.setMatchChat(true);
rule.setMatchMUC(false);
} else {
rule.setMatchChat(false);
rule.setMatchMUC(true);
}
if (ui_.senderRadio->isChecked()) {
QString senderName = jid_->text();
if (!senderName.isEmpty()) {
std::vector<std::string> senders;
senders.push_back(Q2PSTRING(senderName));
rule.setSenders(senders);
}
}
if (ui_.keywordRadio->isChecked()) {
QString keywordString = ui_.keyword->text();
if (!keywordString.isEmpty()) {
std::vector<std::string> keywords;
keywords.push_back(Q2PSTRING(keywordString));
rule.setKeywords(keywords);
}
}
- rule.setNickIsKeyword(ui_.nickIsKeyword->isChecked());
- rule.setMatchWholeWords(!ui_.matchPartialWords->isChecked());
- rule.setMatchCase(ui_.matchCase->isChecked());
+ if (ui_.nickIsKeyword->isChecked()) {
+ rule.setNickIsKeyword(true);
+ rule.setMatchWholeWords(true);
+ rule.setMatchCase(true);
+ } else {
+ rule.setMatchWholeWords(!ui_.matchPartialWords->isChecked());
+ rule.setMatchCase(ui_.matchCase->isChecked());
+ }
HighlightAction& action = rule.getAction();
if (ui_.noColorRadio->isChecked()) {
- action.setHighlightText(false);
- action.setTextColor("");
- action.setTextBackground("");
- } else if (ui_.defaultColorRadio->isChecked()) {
- action.setHighlightText(true);
action.setTextColor("");
action.setTextBackground("");
} else {
- action.setHighlightText(true);
action.setTextColor(Q2PSTRING(ui_.foregroundColor->getColor().name()));
action.setTextBackground(Q2PSTRING(ui_.backgroundColor->getColor().name()));
}
if (ui_.noSoundRadio->isChecked()) {
action.setPlaySound(false);
} else if (ui_.defaultSoundRadio->isChecked()) {
action.setPlaySound(true);
action.setSoundFile("");
} else {
action.setPlaySound(true);
action.setSoundFile(Q2PSTRING(ui_.soundFile->text()));
}
return rule;
}
void QtHighlightEditor::ruleToDialog(const HighlightRule& rule)
{
ui_.chatRadio->setEnabled(true);
ui_.roomRadio->setEnabled(true);
if (rule.getMatchMUC()) {
ui_.chatRadio->setChecked(false);
ui_.roomRadio->setChecked(true);
} else {
ui_.chatRadio->setChecked(true);
ui_.roomRadio->setChecked(false);
}
ui_.allMsgRadio->setEnabled(true);
ui_.allMsgRadio->setChecked(true); /* this is the default radio button */
jid_->setText("");
ui_.keyword->setText("");
ui_.matchPartialWords->setChecked(false);
ui_.matchCase->setChecked(false);
ui_.nickIsKeyword->setEnabled(true);
if (rule.getNickIsKeyword()) {
ui_.nickIsKeyword->setChecked(true);
}
ui_.senderRadio->setEnabled(true);
std::vector<std::string> senders = rule.getSenders();
if (!senders.empty()) {
ui_.senderRadio->setChecked(true);
jid_->setText(P2QSTRING(senders[0]));
}
ui_.keywordRadio->setEnabled(true);
std::vector<std::string> keywords = rule.getKeywords();
if (!keywords.empty()) {
ui_.keywordRadio->setChecked(true);
ui_.keyword->setText(P2QSTRING(keywords[0]));
ui_.matchPartialWords->setChecked(!rule.getMatchWholeWords());
ui_.matchCase->setChecked(rule.getMatchCase());
}
const HighlightAction& action = rule.getAction();
ui_.noColorRadio->setEnabled(true);
- ui_.defaultColorRadio->setEnabled(true);
ui_.customColorRadio->setEnabled(true);
- if (action.highlightText()) {
- if (action.getTextColor().empty() && action.getTextBackground().empty()) {
- ui_.defaultColorRadio->setChecked(true);
- ui_.foregroundColor->setEnabled(false);
- ui_.backgroundColor->setEnabled(false);
- } else {
- ui_.foregroundColor->setEnabled(true);
- ui_.backgroundColor->setEnabled(true);
- QColor foregroundColor(P2QSTRING(action.getTextColor()));
- ui_.foregroundColor->setColor(foregroundColor);
- QColor backgroundColor(P2QSTRING(action.getTextBackground()));
- ui_.backgroundColor->setColor(backgroundColor);
- ui_.customColorRadio->setChecked(true);
- }
- } else {
+ if (action.getTextColor().empty() && action.getTextBackground().empty()) {
ui_.noColorRadio->setChecked(true);
ui_.foregroundColor->setEnabled(false);
ui_.backgroundColor->setEnabled(false);
+ } else {
+ ui_.foregroundColor->setEnabled(true);
+ ui_.backgroundColor->setEnabled(true);
+ QColor foregroundColor(P2QSTRING(action.getTextColor()));
+ ui_.foregroundColor->setColor(foregroundColor);
+ QColor backgroundColor(P2QSTRING(action.getTextBackground()));
+ ui_.backgroundColor->setColor(backgroundColor);
+ ui_.customColorRadio->setChecked(true);
}
ui_.noSoundRadio->setEnabled(true);
ui_.defaultSoundRadio->setEnabled(true);
ui_.customSoundRadio->setEnabled(true);
ui_.soundFile->setText("");
ui_.soundFile->setEnabled(false);
ui_.soundFileButton->setEnabled(false);
if (action.playSound()) {
if (action.getSoundFile().empty()) {
ui_.defaultSoundRadio->setChecked(true);
} else {
ui_.customSoundRadio->setChecked(true);
ui_.soundFile->setText(P2QSTRING(action.getSoundFile()));
ui_.soundFile->setEnabled(true);
ui_.soundFileButton->setEnabled(true);
}
} else {
ui_.noSoundRadio->setChecked(true);
}
/* set radio button child option states */
setChildWidgetStates();
}
}
diff --git a/Swift/QtUI/QtHighlightEditor.ui b/Swift/QtUI/QtHighlightEditor.ui
index be2e99b..775771f 100644
--- a/Swift/QtUI/QtHighlightEditor.ui
+++ b/Swift/QtUI/QtHighlightEditor.ui
@@ -245,77 +245,70 @@
<item>
<widget class="QCheckBox" name="matchCase">
<property name="text">
<string>Keyword is case sensitive</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Highlight Action</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QRadioButton" name="noColorRadio">
<property name="text">
<string>No Highlight</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
- <widget class="QRadioButton" name="defaultColorRadio">
- <property name="text">
- <string>Default Color</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="QRadioButton" name="customColorRadio">
<property name="text">
<string>Custom Color</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
diff --git a/Swift/QtUI/QtWebKitChatView.cpp b/Swift/QtUI/QtWebKitChatView.cpp
index 1486293..a510e34 100644
--- a/Swift/QtUI/QtWebKitChatView.cpp
+++ b/Swift/QtUI/QtWebKitChatView.cpp
@@ -576,72 +576,72 @@ QString QtWebKitChatView::chatMessageToHTML(const ChatWindow::ChatMessage& messa
QString imageStyle = showEmoticons_ ? "" : "style='display:none'";
result += "<span class='swift_emoticon_image' " + imageStyle + "><img src='" + P2QSTRING(emoticonPart->imagePath) + "'/></span><span class='swift_emoticon_text' " + textStyle + ">" + QtUtilities::htmlEscape(P2QSTRING(emoticonPart->alternativeText)) + "</span>";
continue;
}
if ((highlightPart = boost::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(part))) {
QString spanStart = getHighlightSpanStart(highlightPart->foregroundColor, highlightPart->backgroundColor);
result += spanStart + QtUtilities::htmlEscape(P2QSTRING(highlightPart->text)) + "</span>";
continue;
}
}
return result;
}
std::string QtWebKitChatView::addMessage(
const QString& message,
const std::string& senderName,
bool senderIsSelf,
boost::shared_ptr<SecurityLabel> label,
const std::string& avatarPath,
const QString& style,
const boost::posix_time::ptime& time,
const HighlightAction& highlight,
ChatSnippet::Direction direction) {
QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str());
QString htmlString;
if (label) {
htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \" class='swift_label'>").arg(QtUtilities::htmlEscape(P2QSTRING(label->getForegroundColor()))).arg(QtUtilities::htmlEscape(P2QSTRING(label->getBackgroundColor())));
htmlString += QString("%1</span> ").arg(QtUtilities::htmlEscape(P2QSTRING(label->getDisplayMarking())));
}
QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
QString styleSpanEnd = style == "" ? "" : "</span>";
- QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : "";
- QString highlightSpanEnd = highlight.highlightText() ? "</span>" : "";
+ QString highlightSpanStart = highlight.highlightAllText() ? getHighlightSpanStart(highlight) : "";
+ QString highlightSpanEnd = highlight.highlightAllText() ? "</span>" : "";
htmlString += "<span class='swift_inner_message'>" + styleSpanStart + highlightSpanStart + message + highlightSpanEnd + styleSpanEnd + "</span>" ;
bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf);
QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();
std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), direction));
previousMessageWasSelf_ = senderIsSelf;
previousSenderName_ = P2QSTRING(senderName);
previousMessageKind_ = PreviousMessageWasMessage;
return id;
}
std::string QtWebKitChatView::addAction(const ChatWindow::ChatMessage& message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) {
return addMessage(" *" + chatMessageToHTML(message) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time, highlight, ChatSnippet::getDirection(message));
}
static QString encodeButtonArgument(const QString& str) {
return QtUtilities::htmlEscape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str)))));
}
static QString decodeButtonArgument(const QString& str) {
return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str))));
}
QString QtWebKitChatView::buildChatWindowButton(const QString& name, const QString& id, const QString& arg1, const QString& arg2, const QString& arg3, const QString& arg4, const QString& arg5) {
QRegExp regex("[A-Za-z][A-Za-z0-9\\-\\_]+");
Q_ASSERT(regex.exactMatch(id));
QString html = QString("<input id='%2' type='submit' value='%1' onclick='chatwindow.buttonClicked(\"%2\", \"%3\", \"%4\", \"%5\", \"%6\", \"%7\");' />").arg(name).arg(id).arg(encodeButtonArgument(arg1)).arg(encodeButtonArgument(arg2)).arg(encodeButtonArgument(arg3)).arg(encodeButtonArgument(arg4)).arg(encodeButtonArgument(arg5));
return html;
}
std::string QtWebKitChatView::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) {
SWIFT_LOG(debug) << "addFileTransfer" << std::endl;
@@ -794,72 +794,72 @@ void QtWebKitChatView::addErrorMessage(const ChatWindow::ChatMessage& errorMessa
addMessageBottom(boost::make_shared<SystemMessageSnippet>("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_, ChatSnippet::getDirection(errorMessage)));
previousMessageWasSelf_ = false;
previousMessageKind_ = PreviousMessageWasSystem;
}
void QtWebKitChatView::addSystemMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction direction) {
if (window_->isWidgetSelected()) {
window_->onAllMessagesRead();
}
QString messageHTML = chatMessageToHTML(message);
addMessageBottom(boost::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, getActualDirection(message, direction)));
previousMessageKind_ = PreviousMessageWasSystem;
}
void QtWebKitChatView::replaceWithAction(const ChatWindow::ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) {
replaceMessage(" *" + chatMessageToHTML(message) + "*", id, time, "font-style:italic ", highlight);
}
void QtWebKitChatView::replaceMessage(const ChatWindow::ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) {
replaceMessage(chatMessageToHTML(message), id, time, "", highlight);
}
void QtWebKitChatView::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style, const HighlightAction& highlight) {
if (!id.empty()) {
if (window_->isWidgetSelected()) {
window_->onAllMessagesRead();
}
QString messageHTML(message);
QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
QString styleSpanEnd = style == "" ? "" : "</span>";
- QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : "";
- QString highlightSpanEnd = highlight.highlightText() ? "</span>" : "";
+ QString highlightSpanStart = highlight.highlightAllText() ? getHighlightSpanStart(highlight) : "";
+ QString highlightSpanEnd = highlight.highlightAllText() ? "</span>" : "";
messageHTML = styleSpanStart + highlightSpanStart + messageHTML + highlightSpanEnd + styleSpanEnd;
replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time));
}
else {
std::cerr << "Trying to replace a message with no id";
}
}
void QtWebKitChatView::addPresenceMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction direction) {
if (window_->isWidgetSelected()) {
window_->onAllMessagesRead();
}
QString messageHTML = chatMessageToHTML(message);
addMessageBottom(boost::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, getActualDirection(message, direction)));
previousMessageKind_ = PreviousMessageWasPresence;
}
void QtWebKitChatView::replaceLastMessage(const ChatWindow::ChatMessage& message, const ChatWindow::TimestampBehaviour timestampBehaviour) {
replaceLastMessage(chatMessageToHTML(message), timestampBehaviour);
}
void QtWebKitChatView::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct, bool isImpromptu, bool isContinuation) {
if (window_->isWidgetSelected()) {
window_->onAllMessagesRead();
}
QString message;
if (isImpromptu) {
message = QObject::tr("You've been invited to join a chat.") + "\n";
} else {
message = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + "\n";
}