diff options
Diffstat (limited to 'Swift/QtUI/QtChatWindow.cpp')
| -rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 268 | 
1 files changed, 192 insertions, 76 deletions
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 28549f8..d81de61 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010-2012 Kevin Smith + * Copyright (c) 2010-2013 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -25,14 +25,17 @@  #include <Swift/Controllers/UIEvents/SendFileUIEvent.h>  #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>  #include "QtChatWindowJSBridge.h" +#include "QtUtilities.h"  #include <boost/cstdint.hpp>  #include <boost/format.hpp> +#include <boost/smart_ptr/make_shared.hpp>  #include <boost/lexical_cast.hpp>  #include <QLabel>  #include <qdebug.h>  #include <QMessageBox> +#include <QMimeData>  #include <QInputDialog>  #include <QApplication>  #include <QBoxLayout> @@ -45,6 +48,7 @@  #include <QTextEdit>  #include <QTime>  #include <QUrl> +#include <QToolButton>  #include <QPushButton>  #include <QFileDialog>  #include <QMenu> @@ -64,7 +68,7 @@ const QString QtChatWindow::ButtonFileTransferAcceptRequest = QString("filetrans  const QString QtChatWindow::ButtonMUCInvite = QString("mucinvite"); -QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), emoticons_(emoticons) { +QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), blockingState_(BlockingUnsupported) {  	settings_ = settings;  	unreadCount_ = 0;  	idCounter_ = 0; @@ -103,21 +107,18 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	alertLabel_->setStyleSheet(alertStyleSheet_);  	alertWidget_->hide(); -	QBoxLayout* subjectLayout = new QBoxLayout(QBoxLayout::LeftToRight); +	subjectLayout_ = new QBoxLayout(QBoxLayout::LeftToRight);  	subject_ = new QLineEdit(this); -	subjectLayout->addWidget(subject_); +	subjectLayout_->addWidget(subject_);  	setSubject("");  	subject_->setReadOnly(true); -	actionButton_ = new QPushButton(this); +	QPushButton* actionButton_ = new QPushButton(this);  	actionButton_->setIcon(QIcon(":/icons/actions.png"));  	connect(actionButton_, SIGNAL(clicked()), this, SLOT(handleActionButtonClicked())); -	subjectLayout->addWidget(actionButton_); -  	subject_->hide(); -	actionButton_->hide(); -	layout->addLayout(subjectLayout); +	layout->addLayout(subjectLayout_);  	logRosterSplitter_ = new QSplitter(this);  	logRosterSplitter_->setAutoFillBackground(true); @@ -150,13 +151,19 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	QHBoxLayout* inputBarLayout = new QHBoxLayout();  	inputBarLayout->setContentsMargins(0,0,0,0);  	inputBarLayout->setSpacing(2); -	input_ = new QtTextEdit(this); +	input_ = new QtTextEdit(settings_, this);  	input_->setAcceptRichText(false);  	inputBarLayout->addWidget(midBar_);  	inputBarLayout->addWidget(input_);  	correctingLabel_ = new QLabel(tr("Correcting"), this);  	inputBarLayout->addWidget(correctingLabel_);  	correctingLabel_->hide(); + +	// using an extra layout to work around Qt margin glitches on OS X +	QHBoxLayout* actionLayout = new QHBoxLayout(); +	actionLayout->addWidget(actionButton_); + +	inputBarLayout->addLayout(actionLayout);  	layout->addLayout(inputBarLayout);  	inputClearing_ = false; @@ -215,6 +222,15 @@ bool QtChatWindow::appendToPreviousCheck(QtChatWindow::PreviousMessageKind messa  	return previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_&& previousSenderName_ == P2QSTRING(senderName)));  } +ChatSnippet::Direction QtChatWindow::getActualDirection(const ChatMessage& message, Direction direction) { +	if (direction == DefaultDirection) { +		return QCoreApplication::translate("QApplication", "QT_LAYOUT_DIRECTION") == "RTL" ? ChatSnippet::RTL : ChatSnippet::LTR; +	} +	else { +		return ChatSnippet::getDirection(message); +	} +} +  void QtChatWindow::handleFontResized(int fontSizeSteps) {  	messageLog_->resizeFont(fontSizeSteps);  } @@ -413,7 +429,6 @@ void QtChatWindow::convertToMUC() {  	setAcceptDrops(false);  	treeWidget_->show();  	subject_->show(); -	actionButton_->show();  }  void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) { @@ -481,28 +496,95 @@ void QtChatWindow::updateTitleWithUnreadCount() {  	emit titleUpdated();  } -std::string QtChatWindow::addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) { -	return addMessage(linkimoticonify(P2QSTRING(message)), senderName, senderIsSelf, label, avatarPath, "", time); +std::string QtChatWindow::addMessage( +		const 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, "", time, highlight, ChatSnippet::getDirection(message)); +} + +QString QtChatWindow::chatMessageToHTML(const ChatMessage& message) { +	QString result; +	foreach (boost::shared_ptr<ChatMessagePart> part, message.getParts()) { +		boost::shared_ptr<ChatTextMessagePart> textPart; +		boost::shared_ptr<ChatURIMessagePart> uriPart; +		boost::shared_ptr<ChatEmoticonMessagePart> emoticonPart; +		boost::shared_ptr<ChatHighlightingMessagePart> highlightPart; + +		if ((textPart = boost::dynamic_pointer_cast<ChatTextMessagePart>(part))) { +			QString text = QtUtilities::htmlEscape(P2QSTRING(textPart->text)); +			text.replace("\n","<br/>"); +			result += text; +			continue; +		} +		if ((uriPart = boost::dynamic_pointer_cast<ChatURIMessagePart>(part))) { +			QString uri = QtUtilities::htmlEscape(P2QSTRING(uriPart->target)); +			result += "<a href='" + uri + "' >" + uri + "</a>"; +			continue; +		} +		if ((emoticonPart = boost::dynamic_pointer_cast<ChatEmoticonMessagePart>(part))) { +			QString textStyle = showEmoticons_ ? "style='display:none'" : ""; +			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<ChatHighlightingMessagePart>(part))) { +			//FIXME: Maybe do something here. Anything, really. +			continue; +		} + +	} +	return result; +} + +/*QString QtChatWindow::linkimoticonify(const std::string& message) const { +	return linkimoticonify(P2QSTRING(message));  }  QString QtChatWindow::linkimoticonify(const QString& message) const {  	QString messageHTML(message); -	messageHTML = Qt::escape(messageHTML); +	messageHTML = QtUtilities::htmlEscape(messageHTML);  	QMapIterator<QString, QString> it(emoticons_); -	QString textStyle = showEmoticons_ ? "style='display:none'" : ""; -	QString imageStyle = showEmoticons_ ? "" : "style='display:none'"; +	  	if (messageHTML.length() < 500) {  		while (it.hasNext()) {  			it.next(); -			messageHTML.replace(it.key(), "<span class='swift_emoticon_image' " + imageStyle + "><img src='" + it.value() + "'/></span><span class='swift_emoticon_text' " + textStyle + ">"+it.key() + "</span>"); +			messageHTML.replace(it.key(), );  		}  		messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML)));  	}  	messageHTML.replace("\n","<br/>");  	return messageHTML; +}*/ + +QString QtChatWindow::getHighlightSpanStart(const HighlightAction& highlight) { +	QString color = QtUtilities::htmlEscape(P2QSTRING(highlight.getTextColor())); +	QString background = QtUtilities::htmlEscape(P2QSTRING(highlight.getTextBackground())); +	if (color.isEmpty()) { +		color = "black"; +	} +	if (background.isEmpty()) { +		background = "yellow"; +	} + +	return QString("<span style=\"color: %1; background: %2\">").arg(color).arg(background);  } -std::string QtChatWindow::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) { +std::string QtChatWindow::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) { +  	if (isWidgetSelected()) {  		onAllMessagesRead();  	} @@ -510,13 +592,15 @@ std::string QtChatWindow::addMessage(const QString &message, const std::string &  	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(Qt::escape(P2QSTRING(label->getForegroundColor()))).arg(Qt::escape(P2QSTRING(label->getBackgroundColor()))); -		htmlString += QString("%1</span> ").arg(Qt::escape(P2QSTRING(label->getDisplayMarking()))); +		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 messageHTML(message); +  	QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";  	QString styleSpanEnd = style == "" ? "" : "</span>"; -	htmlString += "<span class='swift_inner_message'>" + styleSpanStart + messageHTML + styleSpanEnd + "</span>" ; +	QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : ""; +	QString highlightSpanEnd = highlight.highlightText() ? "</span>" : ""; +	htmlString += "<span class='swift_inner_message'>" + styleSpanStart + highlightSpanStart + message + highlightSpanEnd + styleSpanEnd + "</span>" ;  	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf);  	if (lastLineTracker_.getShouldMoveLastLine()) { @@ -527,7 +611,7 @@ std::string QtChatWindow::addMessage(const QString &message, const std::string &  	}  	QString qAvatarPath =  scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();  	std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++); -	messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); +	messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), direction));  	previousMessageWasSelf_ = senderIsSelf;  	previousSenderName_ = P2QSTRING(senderName); @@ -572,10 +656,11 @@ int QtChatWindow::getCount() {  	return unreadCount_;  } -std::string QtChatWindow::addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) { -	return addMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time); +std::string QtChatWindow::addAction(const 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));  } +// FIXME: Move this to a different file  std::string formatSize(const boost::uintmax_t bytes) {  	static const char *siPrefix[] = {"k", "M", "G", "T", "P", "E", "Z", "Y", NULL};  	int power = 0; @@ -587,11 +672,11 @@ std::string formatSize(const boost::uintmax_t bytes) {  	return str( boost::format("%.1lf %sB") % engBytes % (power > 0 ? siPrefix[power-1] : "") );  } -QString encodeButtonArgument(const QString& str) { -	return Qt::escape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str))))); +static QString encodeButtonArgument(const QString& str) { +	return QtUtilities::htmlEscape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str)))));  } -QString decodeButtonArgument(const QString& str) { +static QString decodeButtonArgument(const QString& str) {  	return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str))));  } @@ -606,11 +691,13 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se  	SWIFT_LOG(debug) << "addFileTransfer" << std::endl;  	QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); +	QString actionText;  	QString htmlString;  	QString formattedFileSize = P2QSTRING(formatSize(sizeInBytes));  	if (senderIsSelf) {  		// outgoing -		htmlString = tr("Send file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" + +		actionText = tr("Send file"); +		htmlString = actionText + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" +  			"<div id='" + ft_id + "'>" +  				buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) +  				buildChatWindowButton(tr("Set Description"), ButtonFileTransferSetDescription, ft_id) + @@ -618,7 +705,8 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se  			"</div>";  	} else {  		// incoming -		htmlString = tr("Receiving file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize  + ") <br/>" + +		actionText = tr("Receiving file"); +		htmlString = actionText + ": " + P2QSTRING(filename) + " ( " + formattedFileSize  + ") <br/>" +  			"<div id='" + ft_id + "'>" +  				buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) +  				buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) + @@ -636,7 +724,7 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se  	}  	QString qAvatarPath = "qrc:/icons/avatar.png";  	std::string id = "ftmessage" + boost::lexical_cast<std::string>(idCounter_++); -	messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); +	messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), ChatSnippet::getDirection(actionText)));  	previousMessageWasSelf_ = senderIsSelf;  	previousSenderName_ = P2QSTRING(senderName); @@ -645,22 +733,25 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se  }  void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) { -	messageLog_->setFileTransferProgress(QString::fromStdString(id), percentageDone); +	messageLog_->setFileTransferProgress(P2QSTRING(id), percentageDone);  }  void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg) { -	messageLog_->setFileTransferStatus(QString::fromStdString(id), state, QString::fromStdString(msg)); +	messageLog_->setFileTransferStatus(P2QSTRING(id), state, P2QSTRING(msg));  }  std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) {  	QString wb_id = QString("wb%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));  	QString htmlString; +	QString actionText;  	if (senderIsSelf) { -		htmlString = "<div id='" + wb_id + "'>" + tr("Starting whiteboard chat") + "<br />"+ +		actionText = tr("Starting whiteboard chat"); +		htmlString = "<div id='" + wb_id + "'>" + actionText + "<br />"+  				buildChatWindowButton(tr("Cancel"), ButtonWhiteboardSessionCancel, wb_id) +  			"</div>";  	} else { -		htmlString = "<div id='" + wb_id + "'>" + tr("%1 would like to start a whiteboard chat").arg(Qt::escape(contact_)) + ": <br/>" + +		actionText = tr("%1 would like to start a whiteboard chat"); +		htmlString = "<div id='" + wb_id + "'>" + actionText.arg(QtUtilities::htmlEscape(contact_)) + ": <br/>" +  				buildChatWindowButton(tr("Cancel"), ButtonWhiteboardSessionCancel, wb_id) +  				buildChatWindowButton(tr("Accept"), ButtonWhiteboardSessionAcceptRequest, wb_id) +  			"</div>"; @@ -674,7 +765,7 @@ std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) {  	}  	QString qAvatarPath = "qrc:/icons/avatar.png";  	std::string id = "wbmessage" + boost::lexical_cast<std::string>(idCounter_++); -	messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(contact_), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, false, theme_, P2QSTRING(id)))); +	messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(contact_), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, false, theme_, P2QSTRING(id), ChatSnippet::getDirection(actionText)));  	previousMessageWasSelf_ = false;  	previousSenderName_ = contact_; @@ -682,7 +773,7 @@ std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) {  }  void QtChatWindow::setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state) { -	messageLog_->setWhiteboardSessionStatus(QString::fromStdString(id), state); +	messageLog_->setWhiteboardSessionStatus(P2QSTRING(id), state);  }  void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3) { @@ -719,12 +810,12 @@ void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1,  	}  	else if (id.startsWith(ButtonWhiteboardSessionAcceptRequest)) {  		QString id = arg1; -		messageLog_->setWhiteboardSessionStatus(QString::fromStdString(Q2PSTRING(id)), ChatWindow::WhiteboardAccepted); +		messageLog_->setWhiteboardSessionStatus(id, ChatWindow::WhiteboardAccepted);  		onWhiteboardSessionAccept();  	}  	else if (id.startsWith(ButtonWhiteboardSessionCancel)) {  		QString id = arg1; -		messageLog_->setWhiteboardSessionStatus(QString::fromStdString(Q2PSTRING(id)), ChatWindow::WhiteboardTerminated); +		messageLog_->setWhiteboardSessionStatus(id, ChatWindow::WhiteboardTerminated);  		onWhiteboardSessionCancel();  	}  	else if (id.startsWith(ButtonWhiteboardShowWindow)) { @@ -744,40 +835,39 @@ void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1,  	}  } -void QtChatWindow::addErrorMessage(const std::string& errorMessage) { +void QtChatWindow::addErrorMessage(const ChatMessage& errorMessage) {  	if (isWidgetSelected()) {  		onAllMessagesRead();  	} -	QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage))); -	errorMessageHTML.replace("\n","<br/>"); -	messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_))); +	QString errorMessageHTML(chatMessageToHTML(errorMessage)); +	 +	messageLog_->addMessageBottom(boost::make_shared<SystemMessageSnippet>("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_, ChatSnippet::getDirection(errorMessage)));  	previousMessageWasSelf_ = false;  	previousMessageKind_ = PreviousMessageWasSystem;  } -void QtChatWindow::addSystemMessage(const std::string& message) { +void QtChatWindow::addSystemMessage(const ChatMessage& message, Direction direction) {  	if (isWidgetSelected()) {  		onAllMessagesRead();  	} -	QString messageHTML(P2QSTRING(message)); -	messageHTML = linkimoticonify(messageHTML); -	messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); +	QString messageHTML = chatMessageToHTML(message); +	messageLog_->addMessageBottom(boost::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, getActualDirection(message, direction)));  	previousMessageKind_ = PreviousMessageWasSystem;  } -void QtChatWindow::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { -	replaceMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", id, time, "font-style:italic "); +void QtChatWindow::replaceWithAction(const 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 QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { -	replaceMessage(linkimoticonify(P2QSTRING(message)), id, time, ""); +void QtChatWindow::replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) { +	replaceMessage(chatMessageToHTML(message), id, time, "", highlight);  } -void QtChatWindow::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style) { +void QtChatWindow::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style, const HighlightAction& highlight) {  	if (!id.empty()) {  		if (isWidgetSelected()) {  			onAllMessagesRead(); @@ -787,7 +877,9 @@ void QtChatWindow::replaceMessage(const QString& message, const std::string& id,  		QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";  		QString styleSpanEnd = style == "" ? "" : "</span>"; -		messageHTML = styleSpanStart + messageHTML + styleSpanEnd; +		QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : ""; +		QString highlightSpanEnd = highlight.highlightText() ? "</span>" : ""; +		messageHTML = styleSpanStart + highlightSpanStart + messageHTML + highlightSpanEnd + styleSpanEnd;  		messageLog_->replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time));  	} @@ -796,14 +888,13 @@ void QtChatWindow::replaceMessage(const QString& message, const std::string& id,  	}  } -void QtChatWindow::addPresenceMessage(const std::string& message) { +void QtChatWindow::addPresenceMessage(const ChatMessage& message, Direction direction) {  	if (isWidgetSelected()) {  		onAllMessagesRead();  	} -	QString messageHTML(P2QSTRING(message)); -	messageHTML = linkimoticonify(messageHTML); -	messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); +	QString messageHTML = chatMessageToHTML(message); +	messageLog_->addMessageBottom(boost::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, getActualDirection(message, direction)));  	previousMessageKind_ = PreviousMessageWasPresence;  } @@ -874,12 +965,15 @@ void QtChatWindow::dropEvent(QDropEvent *event) {  	if (event->mimeData()->urls().size() == 1) {  		onSendFileRequest(Q2PSTRING(event->mimeData()->urls().at(0).toLocalFile()));  	} else { -		addSystemMessage("Sending of multiple files at once isn't supported at this time."); +		std::string messageText(Q2PSTRING(tr("Sending of multiple files at once isn't supported at this time."))); +		ChatMessage message; +		message.append(boost::make_shared<ChatTextMessagePart>(messageText)); +		addSystemMessage(message, DefaultDirection);  	}  } -void QtChatWindow::replaceLastMessage(const std::string& message) { -	messageLog_->replaceLastMessage(linkimoticonify(P2QSTRING(message))); +void QtChatWindow::replaceLastMessage(const ChatMessage& message) { +	messageLog_->replaceLastMessage(chatMessageToHTML(message));  }  void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) { @@ -901,15 +995,26 @@ void QtChatWindow::handleActionButtonClicked() {  	QAction* destroy = NULL;  	QAction* invite = NULL; -	foreach(ChatWindow::RoomAction availableAction, availableRoomActions_) -	{ -		switch(availableAction) +	QAction* block = NULL; +	QAction* unblock = NULL; + +	if (availableRoomActions_.empty()) { +		if (blockingState_ == IsBlocked) { +			unblock = contextMenu.addAction(tr("Unblock")); +		} else if (blockingState_ == IsUnblocked) { +			block = contextMenu.addAction(tr("Block")); +		} +	} else { +		foreach(ChatWindow::RoomAction availableAction, availableRoomActions_)  		{ -			case ChatWindow::ChangeSubject: changeSubject = contextMenu.addAction(tr("Change subject…")); break; -			case ChatWindow::Configure: configure = contextMenu.addAction(tr("Configure room…")); break; -			case ChatWindow::Affiliations: affiliations = contextMenu.addAction(tr("Edit affiliations…")); break; -			case ChatWindow::Destroy: destroy = contextMenu.addAction(tr("Destroy room")); break; -			case ChatWindow::Invite: invite = contextMenu.addAction(tr("Invite person to this room…")); break; +			switch(availableAction) +			{ +				case ChatWindow::ChangeSubject: changeSubject = contextMenu.addAction(tr("Change subject…")); break; +				case ChatWindow::Configure: configure = contextMenu.addAction(tr("Configure room…")); break; +				case ChatWindow::Affiliations: affiliations = contextMenu.addAction(tr("Edit affiliations…")); break; +				case ChatWindow::Destroy: destroy = contextMenu.addAction(tr("Destroy room")); break; +				case ChatWindow::Invite: invite = contextMenu.addAction(tr("Invite person to this room…")); break; +			}  		}  	} @@ -949,6 +1054,12 @@ void QtChatWindow::handleActionButtonClicked() {  	else if (result == invite) {  		onInvitePersonToThisMUCRequest();  	} +	else if (result == block) { +		onBlockUserRequest(); +	} +	else if (result == unblock) { +		onUnblockUserRequest(); +	}  }  void QtChatWindow::handleAffiliationEditorAccepted() { @@ -960,11 +1071,14 @@ void QtChatWindow::setAffiliations(MUCOccupant::Affiliation affiliation, const s  	affiliationEditor_->setAffiliations(affiliation, jids);  } -void QtChatWindow::setAvailableRoomActions(const std::vector<RoomAction> &actions) -{ +void QtChatWindow::setAvailableRoomActions(const std::vector<RoomAction>& actions) {  	availableRoomActions_ = actions;  } +void QtChatWindow::setBlockingState(BlockingState state) { +	blockingState_ = state; +} +  void QtChatWindow::showRoomConfigurationForm(Form::ref form) {  	if (mucConfigurationWindow_) {  		delete mucConfigurationWindow_.data(); @@ -979,18 +1093,20 @@ void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& ji  		onAllMessagesRead();  	} -	QString htmlString = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + " <br/>"; +	QString message = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + "\n"; +	QString htmlString = message;  	if (!reason.empty()) { -		htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "<br/>"; +		htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "\n";  	}  	if (!direct) { -		htmlString += QObject::tr("This person may not have really sent this invitation!") + "<br/>"; +		htmlString += QObject::tr("This person may not have really sent this invitation!") + "\n";  	} +	htmlString = chatMessageToHTML(ChatMessage(Q2PSTRING(htmlString))); -	QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); +	QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));  	htmlString += "<div id='" + id + "'>" + -			buildChatWindowButton(tr("Accept Invite"), ButtonMUCInvite, Qt::escape(P2QSTRING(jid.toString())), Qt::escape(P2QSTRING(password)), id) + +			buildChatWindowButton(chatMessageToHTML(ChatMessage(Q2PSTRING((tr("Accept Invite"))))), ButtonMUCInvite, QtUtilities::htmlEscape(P2QSTRING(jid.toString())), QtUtilities::htmlEscape(P2QSTRING(password)), id) +  		"</div>";  	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMUCInvite, senderName, false); @@ -1002,7 +1118,7 @@ void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& ji  	}  	QString qAvatarPath = "qrc:/icons/avatar.png"; -	messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id))); +	messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id, ChatSnippet::getDirection(message)));  	previousMessageWasSelf_ = false;  	previousSenderName_ = P2QSTRING(senderName);  	previousMessageKind_ = PreviousMessageWasMUCInvite;  | 
 Swift