diff options
Diffstat (limited to 'Swift/QtUI/QtChatWindow.cpp')
| -rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 783 | 
1 files changed, 373 insertions, 410 deletions
| diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 1a82a66..68104b4 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -1,66 +1,64 @@  /* - * Copyright (c) 2010-2012 Kevin Smith + * Copyright (c) 2010-2014 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "QtChatWindow.h" -#include "QtSwiftUtil.h" -#include "Swift/Controllers/Roster/Roster.h" -#include "Swift/Controllers/Roster/RosterItem.h" -#include "Swift/Controllers/Roster/ContactRosterItem.h" -#include "Roster/QtOccupantListWidget.h" -#include "SwifTools/Linkify.h" -#include "QtChatView.h" -#include "MessageSnippet.h" -#include "SystemMessageSnippet.h" -#include "QtTextEdit.h" -#include "QtSettingsProvider.h" -#include "QtScaledAvatarCache.h" - -#include <Swiften/StringCodecs/Base64.h> -#include "SwifTools/TabComplete.h" -#include <Swift/Controllers/UIEvents/UIEventStream.h> -#include <Swift/Controllers/UIEvents/SendFileUIEvent.h> -#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> -#include "QtChatWindowJSBridge.h" +#include <Swift/QtUI/QtChatWindow.h>  #include <boost/cstdint.hpp> -#include <boost/format.hpp>  #include <boost/lexical_cast.hpp> +#include <boost/smart_ptr/make_shared.hpp> -#include <QLabel> -#include <QMessageBox> -#include <QInputDialog> +#include <qdebug.h>  #include <QApplication>  #include <QBoxLayout>  #include <QCloseEvent>  #include <QComboBox> +#include <QFileDialog>  #include <QFileInfo> +#include <QInputDialog> +#include <QLabel>  #include <QLineEdit> +#include <QMenu> +#include <QMessageBox> +#include <QMimeData> +#include <QPushButton>  #include <QSplitter>  #include <QString> +#include <QTextDocument>  #include <QTextEdit>  #include <QTime> +#include <QToolButton>  #include <QUrl> -#include <QPushButton> -#include <QFileDialog> -#include <QMenu> -#include <QTextDocument> -#include <Swift/Controllers/Settings/SettingsProvider.h> +#include <QMimeData> +  #include <Swiften/Base/Log.h> -namespace Swift { +#include <Swift/Controllers/Roster/ContactRosterItem.h> +#include <Swift/Controllers/Roster/Roster.h> +#include <Swift/Controllers/Roster/RosterItem.h> +#include <Swift/Controllers/Settings/SettingsProvider.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIEvents/SendFileUIEvent.h> +#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> + +#include <SwifTools/TabComplete.h> -const QString QtChatWindow::ButtonFileTransferCancel = QString("filetransfer-cancel"); -const QString QtChatWindow::ButtonFileTransferSetDescription = QString("filetransfer-setdescription"); -const QString QtChatWindow::ButtonFileTransferSendRequest = QString("filetransfer-sendrequest"); -const QString QtChatWindow::ButtonFileTransferAcceptRequest = QString("filetransfer-acceptrequest"); -const QString QtChatWindow::ButtonMUCInvite = QString("mucinvite"); +#include <Swift/QtUI/Roster/QtOccupantListWidget.h> +#include <Swift/QtUI/QtAddBookmarkWindow.h> +#include <Swift/QtUI/QtPlainChatView.h> +#include <Swift/QtUI/QtSettingsProvider.h> +#include <Swift/QtUI/QtScaledAvatarCache.h> +#include <Swift/QtUI/QtTextEdit.h> +#include <Swift/QtUI/QtUISettingConstants.h> +#include <Swift/QtUI/QtUtilities.h> +#include <Swift/QtUI/QtWebKitChatView.h> -QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream) { +namespace Swift { + +QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), nextAlertId_(0), eventStream_(eventStream), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false) {  	settings_ = settings;  	unreadCount_ = 0; -	idCounter_ = 0;  	inputEnabled_ = true;  	completer_ = NULL; @@ -68,5 +66,7 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	theme_ = theme;  	isCorrection_ = false; +	labelModel_ = NULL;  	correctionEnabled_ = Maybe; +	fileTransferEnabled_ = Maybe;  	updateTitleWithUnreadCount(); @@ -81,42 +81,32 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	layout->setSpacing(2); -	alertWidget_ = new QWidget(this); -	QHBoxLayout* alertLayout = new QHBoxLayout(alertWidget_); -	layout->addWidget(alertWidget_); -	alertLabel_ = new QLabel(this); -	alertLayout->addWidget(alertLabel_); -	alertButton_ = new QPushButton(this); -	connect (alertButton_, SIGNAL(clicked()), this, SLOT(handleAlertButtonClicked())); -	alertLayout->addWidget(alertButton_); -	QPalette palette = alertWidget_->palette(); -	palette.setColor(QPalette::Window, QColor(Qt::yellow)); -	palette.setColor(QPalette::WindowText, QColor(Qt::black)); -	alertWidget_->setStyleSheet(alertStyleSheet_); -	alertLabel_->setStyleSheet(alertStyleSheet_); -	alertWidget_->hide(); +	alertLayout_ = new QVBoxLayout(); +	layout->addLayout(alertLayout_); -	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);  	layout->addWidget(logRosterSplitter_); -	messageLog_ = new QtChatView(theme, this); +	if (settings_->getSetting(QtUISettingConstants::USE_PLAIN_CHATS) || settings_->getSetting(QtUISettingConstants::USE_SCREENREADER)) { +		messageLog_ = new QtPlainChatView(this, eventStream_); +	} +	else { +		messageLog_ = new QtWebKitChatView(this, eventStream_, theme, this); // I accept that passing the ChatWindow in so that the view can call the signals is somewhat inelegant, but it saves a lot of boilerplate. This patch is unpleasant enough already. So let's fix this soon (it at least needs fixing by the time history is sorted), but not now. +	}  	logRosterSplitter_->addWidget(messageLog_); -	treeWidget_ = new QtOccupantListWidget(eventStream_, settings_, this); +	treeWidget_ = new QtOccupantListWidget(eventStream_, settings_, QtTreeWidget::MessageDefaultJID, this);  	treeWidget_->hide();  	logRosterSplitter_->addWidget(treeWidget_); @@ -124,11 +114,11 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	connect(logRosterSplitter_, SIGNAL(splitterMoved(int, int)), this, SLOT(handleSplitterMoved(int, int))); -	QWidget* midBar = new QWidget(this); -	layout->addWidget(midBar); -	midBar->setAutoFillBackground(true); -	QHBoxLayout *midBarLayout = new QHBoxLayout(midBar); +	midBar_ = new QWidget(this); +	//layout->addWidget(midBar); +	midBar_->setAutoFillBackground(true); +	QHBoxLayout *midBarLayout = new QHBoxLayout(midBar_);  	midBarLayout->setContentsMargins(0,0,0,0);  	midBarLayout->setSpacing(2); -	midBarLayout->addStretch(); +	//midBarLayout->addStretch();  	labelsWidget_ = new QComboBox(this); @@ -137,14 +127,23 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	labelsWidget_->setSizeAdjustPolicy(QComboBox::AdjustToContents);  	midBarLayout->addWidget(labelsWidget_,0); +	connect(labelsWidget_, SIGNAL(currentIndexChanged(int)), this, SLOT(handleCurrentLabelChanged(int))); +	defaultLabelsPalette_ = labelsWidget_->palette();  	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); @@ -159,5 +158,5 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	setFocusProxy(input_);  	logRosterSplitter_->setFocusProxy(input_); -	midBar->setFocusProxy(input_); +	midBar_->setFocusProxy(input_);  	messageLog_->setFocusProxy(input_);  	connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(qAppFocusChanged(QWidget*, QWidget*))); @@ -170,11 +169,10 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2)); -	jsBridge = new QtChatWindowJSBridge(); -	messageLog_->addToJSEnvironment("chatwindow", jsBridge); -	connect(jsBridge, SIGNAL(buttonClicked(QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString))); +	settings_->onSettingChanged.connect(boost::bind(&QtChatWindow::handleSettingChanged, this, _1)); +	messageLog_->showEmoticons(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS)); +  }  QtChatWindow::~QtChatWindow() { -	delete jsBridge;  	if (mucConfigurationWindow_) {  		delete mucConfigurationWindow_.data(); @@ -182,4 +180,11 @@ QtChatWindow::~QtChatWindow() {  } +void QtChatWindow::handleSettingChanged(const std::string& setting) { +	if (setting == QtUISettingConstants::SHOW_EMOTICONS.getKey()) { +		bool showEmoticons = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS); +		messageLog_->showEmoticons(showEmoticons); +	} +} +  void QtChatWindow::handleLogCleared() {  	onLogCleared(); @@ -190,8 +195,4 @@ void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) {  } -bool QtChatWindow::appendToPreviousCheck(QtChatWindow::PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const { -	return previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_&& previousSenderName_ == P2QSTRING(senderName))); -} -  void QtChatWindow::handleFontResized(int fontSizeSteps) {  	messageLog_->resizeFont(fontSizeSteps); @@ -199,20 +200,48 @@ void QtChatWindow::handleFontResized(int fontSizeSteps) {  void QtChatWindow::handleAlertButtonClicked() { -	onAlertButtonClicked(); +	const QObject* alertWidget = QObject::sender()->parent(); +	std::map<AlertID, QWidget*>::const_iterator i = alertWidgets_.begin(); +	for ( ; i != alertWidgets_.end(); ++i) { +		if (i->second == alertWidget) { +			removeAlert(i->first); +			break;  		} - -void QtChatWindow::setAlert(const std::string& alertText, const std::string& buttonText) { -	alertLabel_->setText(alertText.c_str()); -	if (buttonText.empty()) { -		alertButton_->hide(); -	} else { -		alertButton_->setText(buttonText.c_str()); -		alertButton_->show();  	} -	alertWidget_->show();  } -void QtChatWindow::cancelAlert() { -	alertWidget_->hide(); +QtChatWindow::AlertID QtChatWindow::addAlert(const std::string& alertText) { +	QWidget* alertWidget = new QWidget(this); +	QHBoxLayout* alertLayout = new QHBoxLayout(alertWidget); +	alertLayout_->addWidget(alertWidget); +	QLabel* alertLabel = new QLabel(this); +	alertLabel->setText(alertText.c_str()); +	alertLayout->addWidget(alertLabel); + +	QToolButton* closeButton = new QToolButton(alertWidget); +	closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton)); +	closeButton->setIconSize(QSize(16,16)); +	closeButton->setCursor(Qt::ArrowCursor); +	closeButton->setStyleSheet("QToolButton { border: none; padding: 0px; }"); +	connect (closeButton, SIGNAL(clicked()), this, SLOT(handleAlertButtonClicked())); + +	alertLayout->addWidget(closeButton); +	QPalette palette = alertWidget->palette(); +	palette.setColor(QPalette::Window, QColor(Qt::yellow)); +	palette.setColor(QPalette::WindowText, QColor(Qt::black)); +	alertWidget->setStyleSheet(alertStyleSheet_); +	alertLabel->setStyleSheet(alertStyleSheet_); + +	AlertID id = nextAlertId_++; +	alertWidgets_[id] = alertWidget; +	return id; +} + +void QtChatWindow::removeAlert(const AlertID id) { +	std::map<AlertID, QWidget*>::iterator i = alertWidgets_.find(id); +	if (i != alertWidgets_.end()) { +		alertLayout_->removeWidget(i->second); +		delete i->second; +		alertWidgets_.erase(i); +	}  } @@ -223,5 +252,4 @@ void QtChatWindow::setTabComplete(TabComplete* completer) {  void QtChatWindow::handleKeyPressEvent(QKeyEvent* event) {  	event->ignore(); -	QtTabbable::handleKeyPressEvent(event);  	if (event->isAccepted()) {  		return; @@ -232,11 +260,15 @@ void QtChatWindow::handleKeyPressEvent(QKeyEvent* event) {  	if (key == Qt::Key_Tab) {  		tabComplete(); -	} else if ((key == Qt::Key_Up) && input_->toPlainText().isEmpty() && !(lastSentMessage_.isEmpty())) { +	} +	else if ((key == Qt::Key_Up) && input_->toPlainText().isEmpty() && !(lastSentMessage_.isEmpty())) {  		beginCorrection(); -	} else if (key == Qt::Key_Down && isCorrection_ && input_->textCursor().atBlockEnd()) { +	} +	else if (key == Qt::Key_Down && isCorrection_ && input_->textCursor().atBlockEnd()) {  		cancelCorrection(); -	} else if (key == Qt::Key_Down || key == Qt::Key_Up) { +	} +	else if (key == Qt::Key_Down || key == Qt::Key_Up) {  		/* Drop */ -	} else { +	} +	else {  		messageLog_->handleKeyPressEvent(event);  	} @@ -244,9 +276,19 @@ void QtChatWindow::handleKeyPressEvent(QKeyEvent* event) {  void QtChatWindow::beginCorrection() { +	boost::optional<AlertID> newCorrectingAlert;  	if (correctionEnabled_ == ChatWindow::Maybe) { -		setAlert(Q2PSTRING(tr("This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message"))); -	} else if (correctionEnabled_ == ChatWindow::No) { -		setAlert(Q2PSTRING(tr("This chat does not support message correction.  If you send a correction anyway, it will appear as a duplicate message"))); +		newCorrectingAlert = addAlert(Q2PSTRING(tr("This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message"))); +	} +	else if (correctionEnabled_ == ChatWindow::No) { +		newCorrectingAlert = addAlert(Q2PSTRING(tr("This chat does not support message correction.  If you send a correction anyway, it will appear as a duplicate message")));  	} + +	if (newCorrectingAlert) { +		if (correctingAlert_) { +			removeAlert(*correctingAlert_); +		} +		correctingAlert_ = newCorrectingAlert; +	} +  	QTextCursor cursor = input_->textCursor();  	cursor.select(QTextCursor::Document); @@ -257,8 +299,12 @@ void QtChatWindow::beginCorrection() {  	correctingLabel_->show();  	input_->setStyleSheet(alertStyleSheet_); +	labelsWidget_->setEnabled(false);  }  void QtChatWindow::cancelCorrection() { -	cancelAlert(); +	if (correctingAlert_) { +		removeAlert(*correctingAlert_); +		correctingAlert_.reset(); +	}  	QTextCursor cursor = input_->textCursor();  	cursor.select(QTextCursor::Document); @@ -267,4 +313,5 @@ void QtChatWindow::cancelCorrection() {  	correctingLabel_->hide();  	input_->setStyleSheet(qApp->styleSheet()); +	labelsWidget_->setEnabled(true);  } @@ -289,7 +336,8 @@ void QtChatWindow::tabComplete() {  	if (tabCompleteCursor_.hasSelection()) {  		cursor = tabCompleteCursor_; -	} else { +	} +	else {  		cursor = input_->textCursor(); -		cursor.select(QTextCursor::WordUnderCursor); +		while(cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor) && cursor.document()->characterAt(cursor.position() - 1) != ' ') { }  	}  	QString root = cursor.selectedText(); @@ -319,15 +367,14 @@ void QtChatWindow::setRosterModel(Roster* roster) {  void QtChatWindow::setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) { -	availableLabels_ = labels; -	labelsWidget_->clear(); +	delete labelModel_; +	labelModel_ = new LabelModel(); +	labelModel_->availableLabels_ = labels;  	int i = 0;  	int defaultIndex = 0; +	labelsWidget_->setModel(labelModel_);  	foreach (SecurityLabelsCatalog::Item label, labels) { -		std::string selector = label.getSelector(); -		std::string displayMarking = label.getLabel() ? label.getLabel()->getDisplayMarking() : ""; -		QString labelName = selector.empty() ? displayMarking.c_str() : selector.c_str(); -		labelsWidget_->addItem(labelName, QVariant(i));  		if (label.getIsDefault()) {  			defaultIndex = i; +			break;  		}  		i++; @@ -336,4 +383,25 @@ void QtChatWindow::setAvailableSecurityLabels(const std::vector<SecurityLabelsCa  } +void QtChatWindow::handleCurrentLabelChanged(int index) { +	if (static_cast<size_t>(index) >= labelModel_->availableLabels_.size()) { +		qDebug() << "User selected a label that doesn't exist"; +		return; +	} +	const SecurityLabelsCatalog::Item& label = labelModel_->availableLabels_[index]; +	if (label.getLabel()) { +		QPalette palette = labelsWidget_->palette(); +		//palette.setColor(QPalette::Base, P2QSTRING(label.getLabel()->getBackgroundColor())); +		palette.setColor(labelsWidget_->backgroundRole(), P2QSTRING(label.getLabel()->getBackgroundColor())); +		palette.setColor(labelsWidget_->foregroundRole(), P2QSTRING(label.getLabel()->getForegroundColor())); +		labelsWidget_->setPalette(palette); +		midBar_->setPalette(palette); +		labelsWidget_->setAutoFillBackground(true); +	} +	else { +		labelsWidget_->setAutoFillBackground(false); +		labelsWidget_->setPalette(defaultLabelsPalette_); +		midBar_->setPalette(defaultLabelsPalette_); +	} +}  void QtChatWindow::setSecurityLabelsError() { @@ -345,5 +413,6 @@ void QtChatWindow::setSecurityLabelsEnabled(bool enabled) {  		labelsWidget_->setEnabled(true);  		labelsWidget_->show(); -	} else { +	} +	else {  		labelsWidget_->hide();  	} @@ -354,8 +423,12 @@ void QtChatWindow::setCorrectionEnabled(Tristate enabled) {  } +void QtChatWindow::setFileTransferEnabled(Tristate enabled) { +	fileTransferEnabled_ = enabled; +} +  SecurityLabelsCatalog::Item QtChatWindow::getSelectedSecurityLabel() {  	assert(labelsWidget_->isEnabled()); -	assert(labelsWidget_->currentIndex() >= 0 && static_cast<size_t>(labelsWidget_->currentIndex()) < availableLabels_.size()); -	return availableLabels_[labelsWidget_->currentIndex()]; +	assert(labelsWidget_->currentIndex() >= 0 && static_cast<size_t>(labelsWidget_->currentIndex()) < labelModel_->availableLabels_.size()); +	return labelModel_->availableLabels_[labelsWidget_->currentIndex()];  } @@ -366,9 +439,10 @@ void QtChatWindow::closeEvent(QCloseEvent* event) {  } -void QtChatWindow::convertToMUC() { -	setAcceptDrops(false); +void QtChatWindow::convertToMUC(MUCType mucType) { +	impromptu_ = (mucType == ImpromptuMUC); +	treeWidget_->setMessageTarget(impromptu_ ? QtTreeWidget::MessageDisplayJID : QtTreeWidget::MessageDefaultJID); +	isMUC_ = true;  	treeWidget_->show(); -	subject_->show(); -	actionButton_->show(); +	subject_->setVisible(!impromptu_);  } @@ -432,5 +506,6 @@ void QtChatWindow::updateTitleWithUnreadCount() {  	if (isWindow()) {  		setWindowTitle(unreadCount_ > 0 ? QString("(%1) %2").arg(unreadCount_).arg(contact_) : contact_); -	} else { +	} +	else {  		setWindowTitle(contact_);  	} @@ -438,42 +513,5 @@ void QtChatWindow::updateTitleWithUnreadCount() {  } -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(message, senderName, senderIsSelf, label, avatarPath, "", time); -} -std::string QtChatWindow::addMessage(const std::string &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) { -	if (isWidgetSelected()) { -		onAllMessagesRead(); -	} -	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; \">").arg(Qt::escape(P2QSTRING(label->getForegroundColor()))).arg(Qt::escape(P2QSTRING(label->getBackgroundColor()))); -		htmlString += QString("%3</span> ").arg(Qt::escape(P2QSTRING(label->getDisplayMarking()))); -	} -	QString messageHTML(Qt::escape(P2QSTRING(message))); -	messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); -	messageHTML.replace("\n","<br/>"); -	QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">"; -	QString styleSpanEnd = style == "" ? "" : "</span>"; -	htmlString += styleSpanStart + messageHTML + styleSpanEnd; - -	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf); -	if (lastLineTracker_.getShouldMoveLastLine()) { -		/* should this be queued? */ -		messageLog_->addLastSeenLine(); -		/* if the line is added we should break the snippet */ -		appendToPrevious = false; -	} -	QString qAvatarPath =  scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded(); -	std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++); -	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); - -	previousMessageWasSelf_ = senderIsSelf; -	previousSenderName_ = P2QSTRING(senderName); -	previousMessageKind_ = PreviousMessageWasMessage; -	return id; -}  void QtChatWindow::flash() { @@ -481,230 +519,8 @@ void QtChatWindow::flash() {  } -void QtChatWindow::setAckState(std::string const& id, ChatWindow::AckState state) { -	QString xml; -	switch (state) { -		case ChatWindow::Pending: -			xml = "<img src='qrc:/icons/throbber.gif' title='" + tr("This message has not been received by your server yet.") + "'/>"; -			messageLog_->displayReceiptInfo(P2QSTRING(id), false); -			break; -		case ChatWindow::Received: -			xml = ""; -			messageLog_->displayReceiptInfo(P2QSTRING(id), true); -			break; -		case ChatWindow::Failed: xml = "<img src='qrc:/icons/error.png' title='" + tr("This message may not have been transmitted.") + "'/>"; break; -	} -	messageLog_->setAckXML(P2QSTRING(id), xml); -} - -void QtChatWindow::setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) { -	QString xml; -	switch (state) { -		case ChatWindow::ReceiptReceived: -			xml = "<img src='qrc:/icons/check.png' title='" + tr("The receipt for this message has been received.") + "'/>"; -			break; -		case ChatWindow::ReceiptRequested: -			xml = "<img src='qrc:/icons/warn.png' title='" + tr("The receipt for this message has not yet been received. The recipient(s) might not have received this message.") + "'/>"; -			break; -	} -	messageLog_->setReceiptXML(P2QSTRING(id), xml); -} -  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(" *" + message + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time); -} - -std::string formatSize(const boost::uintmax_t bytes) { -	static const char *siPrefix[] = {"k", "M", "G", "T", "P", "E", "Z", "Y", NULL}; -	int power = 0; -	double engBytes = bytes; -	while (engBytes >= 1000) { -		++power; -		engBytes = engBytes / 1000.0; -	} -	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))))); -} - -QString decodeButtonArgument(const QString& str) { -	return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str)))); -} - -QString QtChatWindow::buildChatWindowButton(const QString& name, const QString& id, const QString& arg1, const QString& arg2, const QString& arg3) { -	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\");' />").arg(name).arg(id).arg(encodeButtonArgument(arg1)).arg(encodeButtonArgument(arg2)).arg(encodeButtonArgument(arg3)); -	return html; -} - -std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) { -	SWIFT_LOG(debug) << "addFileTransfer" << std::endl; -	QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); -	 -	QString htmlString; -	QString formattedFileSize = P2QSTRING(formatSize(sizeInBytes)); -	if (senderIsSelf) { -		// outgoing -		htmlString = tr("Send file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" + -			"<div id='" + ft_id + "'>" + -				buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + -				buildChatWindowButton(tr("Set Description"), ButtonFileTransferSetDescription, ft_id) + -				buildChatWindowButton(tr("Send"), ButtonFileTransferSendRequest, ft_id) + -			"</div>"; -	} else { -		// incoming -		htmlString = tr("Receiving file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize  + ") <br/>" + -			"<div id='" + ft_id + "'>" + -				buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + -				buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) + -			"</div>"; -	} - -	//addMessage(message, senderName, senderIsSelf, boost::shared_ptr<SecurityLabel>(), "", boost::posix_time::second_clock::local_time()); - -	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasFileTransfer, senderName, senderIsSelf); -	if (lastLineTracker_.getShouldMoveLastLine()) { -		/* should this be queued? */ -		messageLog_->addLastSeenLine(); -		/* if the line is added we should break the snippet */ -		appendToPrevious = false; -	} -	QString qAvatarPath = "qrc:/icons/avatar.png"; -	std::string id = "ftmessage" + boost::lexical_cast<std::string>(idCounter_++); -	messageLog_->addMessage(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)))); - -	previousMessageWasSelf_ = senderIsSelf; -	previousSenderName_ = P2QSTRING(senderName); -	previousMessageKind_ = PreviousMessageWasFileTransfer; -	return Q2PSTRING(ft_id); -} - -void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) { -	messageLog_->setFileTransferProgress(QString::fromStdString(id), percentageDone); -} - -void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg) { -	messageLog_->setFileTransferStatus(QString::fromStdString(id), state, QString::fromStdString(msg)); -} - -void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3) { -	QString arg1 = decodeButtonArgument(encodedArgument1); -	QString arg2 = decodeButtonArgument(encodedArgument2); -	QString arg3 = decodeButtonArgument(encodedArgument3); - -	if (id.startsWith(ButtonFileTransferCancel)) { -		QString ft_id = arg1; -		onFileTransferCancel(Q2PSTRING(ft_id)); -	} -	else if (id.startsWith(ButtonFileTransferSetDescription)) { -		QString ft_id = arg1; -		bool ok = false; -		QString text = QInputDialog::getText(this, tr("File transfer description"), -			tr("Description:"), QLineEdit::Normal, "", &ok); -		if (ok) { -			descriptions[ft_id] = text; -		} -	} -	else if (id.startsWith(ButtonFileTransferSendRequest)) { -		QString ft_id = arg1; -		QString text = descriptions.find(ft_id) == descriptions.end() ? QString() : descriptions[ft_id]; -		onFileTransferStart(Q2PSTRING(ft_id), Q2PSTRING(text)); -	} -	else if (id.startsWith(ButtonFileTransferAcceptRequest)) { -		QString ft_id = arg1; -		QString filename = arg2; - -		QString path = QFileDialog::getSaveFileName(this, tr("Save File"), filename); -		if (!path.isEmpty()) { -			onFileTransferAccept(Q2PSTRING(ft_id), Q2PSTRING(path)); -		} -	} -	else if (id.startsWith(ButtonMUCInvite)) { -		QString roomJID = arg1; -		QString password = arg2; -		QString elementID = arg3; - -		eventStream_->send(boost::make_shared<JoinMUCUIEvent>(Q2PSTRING(roomJID), Q2PSTRING(password))); -		messageLog_->setMUCInvitationJoined(elementID); -	} -	else { -		SWIFT_LOG(debug) << "Unknown HTML button! ( " << Q2PSTRING(id) << " )" << std::endl; -	} -} - -void QtChatWindow::addErrorMessage(const std::string& errorMessage) { -	if (isWidgetSelected()) { -		onAllMessagesRead(); -	} - -	QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage))); -	errorMessageHTML.replace("\n","<br/>"); -	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_))); - -	previousMessageWasSelf_ = false; -	previousMessageKind_ = PreviousMessageWasSystem; -} - -void QtChatWindow::addSystemMessage(const std::string& message) { -	if (isWidgetSelected()) { -		onAllMessagesRead(); -	} - -	QString messageHTML(Qt::escape(P2QSTRING(message))); -	messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); -	messageHTML.replace("\n","<br/>"); -	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); - -	previousMessageKind_ = PreviousMessageWasSystem; -} - -void QtChatWindow::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { -	replaceMessage(" *" + message + "*", id, time, "font-style:italic "); -} - -void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { -	replaceMessage(message, id, time, ""); -} - -void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style) { -	if (!id.empty()) { -		if (isWidgetSelected()) { -			onAllMessagesRead(); -		} - -		QString messageHTML(Qt::escape(P2QSTRING(message))); -		messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); -		messageHTML.replace("\n","<br/>"); - -		QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">"; -		QString styleSpanEnd = style == "" ? "" : "</span>"; -		messageHTML = styleSpanStart + messageHTML + styleSpanEnd; - -		messageLog_->replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time)); -	} -	else { -		std::cerr << "Trying to replace a message with no id"; -	} -} - -void QtChatWindow::addPresenceMessage(const std::string& message) { -	if (isWidgetSelected()) { -		onAllMessagesRead(); -	} - -	QString messageHTML(Qt::escape(P2QSTRING(message))); -	messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); -	messageHTML.replace("\n","<br/>"); -	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); - -	previousMessageKind_ = PreviousMessageWasPresence; -} -  void QtChatWindow::returnPressed() { @@ -727,5 +543,6 @@ void QtChatWindow::handleInputChanged() {  	if (input_->toPlainText().isEmpty()) {  		onUserCancelsTyping(); -	} else { +	} +	else {  		onUserTyping();  	} @@ -746,4 +563,8 @@ void QtChatWindow::show() {  } +bool QtChatWindow::isVisible() const { +	return QWidget::isVisible(); +} +  void QtChatWindow::activate() {  	if (isWindow()) { @@ -763,20 +584,42 @@ void QtChatWindow::moveEvent(QMoveEvent*) {  void QtChatWindow::dragEnterEvent(QDragEnterEvent *event) { +	if (inputEnabled_) {  		if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) {  			// TODO: check whether contact actually supports file transfer +			if (!isMUC_) { +				event->acceptProposedAction(); +			} +		} +		else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid-list")) { +			if (isMUC_ || supportsImpromptuChat_) {  				event->acceptProposedAction();  			}  		} +	} +}  void QtChatWindow::dropEvent(QDropEvent *event) { +	if (fileTransferEnabled_ == ChatWindow::Yes && event->mimeData()->hasUrls()) {  		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.");  		} +		else { +			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); +		} +	} +	else if (event->mimeData()->hasFormat("application/vnd.swift.contact-jid-list")) { +		QByteArray dataBytes = event->mimeData()->data("application/vnd.swift.contact-jid-list"); +		QDataStream dataStream(&dataBytes, QIODevice::ReadOnly); +		std::vector<JID> invites; +		while (!dataStream.atEnd()) { +			QString jidString; +			dataStream >> jidString; +			invites.push_back(Q2PSTRING(jidString)); +		} +		onInviteToChat(invites);  	} - -void QtChatWindow::replaceLastMessage(const std::string& message) { -	messageLog_->replaceLastMessage(P2QSTRING(Linkify::linkify(message)));  } @@ -788,4 +631,6 @@ void QtChatWindow::setSubject(const std::string& subject) {  	//subject_->setVisible(!subject.empty());  	subject_->setText(P2QSTRING(subject)); +	subject_->setToolTip(P2QSTRING(subject)); +	subject_->setCursorPosition(0);  } @@ -798,16 +643,64 @@ void QtChatWindow::handleActionButtonClicked() {  	QAction* invite = NULL; +	QAction* block = NULL; +	QAction* unblock = NULL; + +	if (availableRoomActions_.empty()) { +		if (blockingState_ == IsBlocked) { +			unblock = contextMenu.addAction(tr("Unblock")); +			unblock->setEnabled(inputEnabled_); +		} +		else if (blockingState_ == IsUnblocked) { +			block = contextMenu.addAction(tr("Block")); +			block->setEnabled(inputEnabled_); +		} + +		if (supportsImpromptuChat_) { +			invite = contextMenu.addAction(tr("Invite person to this chat…")); +			invite->setEnabled(inputEnabled_); +		} + +	} +	else {  		foreach(ChatWindow::RoomAction availableAction, availableRoomActions_)  		{ +			if (impromptu_) { +				// hide options we don't need in impromptu chats +				if (availableAction == ChatWindow::ChangeSubject || +					availableAction == ChatWindow::Configure || +					availableAction == ChatWindow::Affiliations || +					availableAction == ChatWindow::Destroy) { +					continue; +				} +			}  			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; +				case ChatWindow::ChangeSubject: +					changeSubject = contextMenu.addAction(tr("Change subject…")); +					changeSubject->setEnabled(inputEnabled_); +					break; +				case ChatWindow::Configure: +					configure = contextMenu.addAction(tr("Configure room…")); +					configure->setEnabled(inputEnabled_); +					break; +				case ChatWindow::Affiliations: +					affiliations = contextMenu.addAction(tr("Edit affiliations…")); +					affiliations->setEnabled(inputEnabled_); +					break; +				case ChatWindow::Destroy: +					destroy = contextMenu.addAction(tr("Destroy room")); +					destroy->setEnabled(inputEnabled_); +					break; +				case ChatWindow::Invite: +					invite = contextMenu.addAction(tr("Invite person to this room…")); +					invite->setEnabled(inputEnabled_); +					break; +			}  		}  	} +	QAction* bookmark = contextMenu.addAction(tr("Add boomark...")); +	bookmark->setEnabled(inputEnabled_); +  	QAction* result = contextMenu.exec(QCursor::pos());  	if (result == NULL) { @@ -844,9 +737,14 @@ void QtChatWindow::handleActionButtonClicked() {  	}  	else if (result == invite) { -		bool ok; -		QString jid = QInputDialog::getText(this, tr("Enter person's address"), tr("Address:"), QLineEdit::Normal, "", &ok); -		if (ok) { -			onInvitePersonToThisMUCRequest(JID(Q2PSTRING(jid)), ""); +		onInviteToChat(std::vector<JID>()); +	} +	else if (result == block) { +		onBlockUserRequest();  	} +	else if (result == unblock) { +		onUnblockUserRequest(); +	} +	else if (result == bookmark) { +		onBookmarkRequest();  	}  } @@ -861,9 +759,21 @@ void QtChatWindow::setAffiliations(MUCOccupant::Affiliation affiliation, const s  } -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::setCanInitiateImpromptuChats(bool supportsImpromptu) { +	supportsImpromptuChat_ = supportsImpromptu; +} + +void QtChatWindow::showBookmarkWindow(const MUCBookmark& bookmark) { +	QtAddBookmarkWindow* window = new QtAddBookmarkWindow(eventStream_, bookmark); +	window->show(); +} +  void QtChatWindow::showRoomConfigurationForm(Form::ref form) {  	if (mucConfigurationWindow_) { @@ -875,36 +785,89 @@ void QtChatWindow::showRoomConfigurationForm(Form::ref form) {  } -void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) { +void QtChatWindow::handleAppendedToLog() { +	if (lastLineTracker_.getShouldMoveLastLine()) { +		/* should this be queued? */ +		messageLog_->addLastSeenLine(); +	}  	if (isWidgetSelected()) {  		onAllMessagesRead();  	} +} -	QString htmlString = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + " <br/>"; -	if (!reason.empty()) { -		htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "<br/>"; +void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct, bool isImpromptu, bool isContinuation) { +	handleAppendedToLog(); +	messageLog_->addMUCInvitation(senderName, jid, reason, password, direct, isImpromptu, isContinuation);  } -	if (!direct) { -		htmlString += QObject::tr("This person may not have really sent this invitation!") + "<br/>"; + +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) { +	handleAppendedToLog(); +	return messageLog_->addMessage(message, senderName, senderIsSelf, label, avatarPath, time, highlight);  } -	QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); +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) { +	handleAppendedToLog(); +	return messageLog_->addAction(message, senderName, senderIsSelf, label, avatarPath, time, highlight); +} -	htmlString += "<div id='" + id + "'>" + -			buildChatWindowButton(tr("Accept Invite"), ButtonMUCInvite, Qt::escape(P2QSTRING(jid.toString())), Qt::escape(P2QSTRING(password)), id) + -		"</div>"; -	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMUCInvite, senderName, false); -	if (lastLineTracker_.getShouldMoveLastLine()) { -		/* should this be queued? */ -		messageLog_->addLastSeenLine(); -		/* if the line is added we should break the snippet */ -		appendToPrevious = false; +void QtChatWindow::addSystemMessage(const ChatMessage& message, Direction direction) { +	handleAppendedToLog(); +	messageLog_->addSystemMessage(message, direction); +} + +void QtChatWindow::addPresenceMessage(const ChatMessage& message, Direction direction) { +	handleAppendedToLog(); +	messageLog_->addPresenceMessage(message, direction); +} + +void QtChatWindow::addErrorMessage(const ChatMessage& message) { +	handleAppendedToLog(); +	messageLog_->addErrorMessage(message); +} + + +void QtChatWindow::replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) { +	handleAppendedToLog(); +	messageLog_->replaceMessage(message, id, time, highlight); +} + +void QtChatWindow::replaceWithAction(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) { +	handleAppendedToLog(); +	messageLog_->replaceWithAction(message, id, time, highlight); +} + +std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) { +	handleAppendedToLog(); +	return messageLog_->addFileTransfer(senderName, senderIsSelf, filename, sizeInBytes);  } -	QString qAvatarPath = "qrc:/icons/avatar.png"; -	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id))); -	previousMessageWasSelf_ = false; -	previousSenderName_ = P2QSTRING(senderName); -	previousMessageKind_ = PreviousMessageWasMUCInvite; +void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) { +	messageLog_->setFileTransferProgress(id, percentageDone); +} + +void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg) { +	messageLog_->setFileTransferStatus(id, state, msg); +} + + +std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) { +	handleAppendedToLog(); +	return messageLog_->addWhiteboardRequest(contact_, senderIsSelf); +} + +void QtChatWindow::setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state) { +	messageLog_->setWhiteboardSessionStatus(id, state); +} + +void QtChatWindow::replaceLastMessage(const ChatMessage& message, const TimestampBehaviour timestampBehaviour) { +	messageLog_->replaceLastMessage(message, timestampBehaviour); +} + +void QtChatWindow::setAckState(const std::string& id, AckState state) { +	messageLog_->setAckState(id, state); +} + +void QtChatWindow::setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) { +	messageLog_->setMessageReceiptState(id, state);  } | 
 Swift
 Swift