From 2dcdee8e9f657c7f5c5d5c507373fd328beaa568 Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Wed, 28 Mar 2012 01:01:17 +0200
Subject: Refactoring incoming MUC invites UI.

Making MUC invites non-modal by moving them into the chat view.
Adding event classes for invites so they turn up in 'Notices'-tab and generate notifications.

License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php

diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index f590ffd..8523591 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -29,6 +29,7 @@
 #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
 #include <Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h>
 #include <Swiften/Avatars/AvatarManager.h>
+#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h>
 
 namespace Swift {
 
@@ -91,8 +92,8 @@ void ChatControllerBase::setAvailableServerFeatures(boost::shared_ptr<DiscoInfo>
 
 void ChatControllerBase::handleAllMessagesRead() {
 	if (!unreadMessages_.empty()) {
-		foreach (boost::shared_ptr<MessageEvent> messageEvent, unreadMessages_) {
-			messageEvent->read();
+		foreach (boost::shared_ptr<StanzaEvent> stanzaEvent, unreadMessages_) {
+			stanzaEvent->conclude();
 		}
 		unreadMessages_.clear();
 		chatWindow_->setUnreadMessageCount(0);
@@ -267,9 +268,20 @@ std::string ChatControllerBase::getErrorMessage(boost::shared_ptr<ErrorPayload>
 	return defaultMessage;
 }
 
+void ChatControllerBase::handleGeneralMUCInvitation(MUCInviteEvent::ref event) {
+	unreadMessages_.push_back(event);
+	chatWindow_->show();
+	chatWindow_->setUnreadMessageCount(unreadMessages_.size());
+	onUnreadCountChanged();
+	chatWindow_->addMUCInvitation(senderDisplayNameFromMessage(event->getInviter()), event->getRoomJID(), event->getReason(), event->getPassword(), event->getDirect());
+	eventController_->handleIncomingEvent(event);
+}
+
 void ChatControllerBase::handleMUCInvitation(Message::ref message) {
 	MUCInvitationPayload::ref invite = message->getPayload<MUCInvitationPayload>();
-	chatWindow_->addMUCInvitation(invite->getJID(), invite->getReason(), invite->getPassword());
+
+	MUCInviteEvent::ref inviteEvent = boost::make_shared<MUCInviteEvent>(toJID_, invite->getJID(), invite->getReason(), invite->getPassword(), true);
+	handleGeneralMUCInvitation(inviteEvent);
 }
 
 void ChatControllerBase::handleMediatedMUCInvitation(Message::ref message) {
@@ -283,7 +295,9 @@ void ChatControllerBase::handleMediatedMUCInvitation(Message::ref message) {
 	if (message->getPayload<MUCUserPayload>()->getPassword()) {
 		password = *message->getPayload<MUCUserPayload>()->getPassword();
 	}
-	chatWindow_->addMUCInvitation(from, reason, password, false);
+
+	MUCInviteEvent::ref inviteEvent = boost::make_shared<MUCInviteEvent>(invite.from, from, reason, password, false);
+	handleGeneralMUCInvitation(inviteEvent);
 }
 
 
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index e1034d6..a4d23c0 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -20,6 +20,7 @@
 #include <string>
 #include "Swiften/Elements/DiscoInfo.h"
 #include "Swift/Controllers/XMPPEvents/MessageEvent.h"
+#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h>
 #include "Swiften/JID/JID.h"
 #include "Swiften/Elements/SecurityLabelsCatalog.h"
 #include "Swiften/Elements/ErrorPayload.h"
@@ -84,10 +85,11 @@ namespace Swift {
 			void handleDayChangeTick();
 			void handleMUCInvitation(Message::ref message);
 			void handleMediatedMUCInvitation(Message::ref message);
+			void handleGeneralMUCInvitation(MUCInviteEvent::ref event);
 
 		protected:
 			JID selfJID_;
-			std::vector<boost::shared_ptr<MessageEvent> > unreadMessages_;
+			std::vector<boost::shared_ptr<StanzaEvent> > unreadMessages_;
 			StanzaChannel* stanzaChannel_;
 			IQRouter* iqRouter_;
 			ChatWindowFactory* chatWindowFactory_;
diff --git a/Swift/Controllers/EventNotifier.cpp b/Swift/Controllers/EventNotifier.cpp
index e643ab3..a4edabf 100644
--- a/Swift/Controllers/EventNotifier.cpp
+++ b/Swift/Controllers/EventNotifier.cpp
@@ -18,6 +18,7 @@
 #include "Swift/Controllers/XMPPEvents/MessageEvent.h"
 #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h"
 #include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
+#include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h"
 #include "Swift/Controllers/Settings/SettingsProvider.h"
 
 namespace Swift {
@@ -55,6 +56,12 @@ void EventNotifier::handleEventAdded(boost::shared_ptr<StanzaEvent> event) {
 	else if(boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event)) {
 		notifier->showMessage(Notifier::SystemMessage, QT_TRANSLATE_NOOP("", "Error"), errorEvent->getText(), boost::filesystem::path(), boost::function<void()>());
 	}
+	else if (boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event)) {
+		std::string title = mucInviteEvent->getInviter();
+		std::string message = str(format(QT_TRANSLATE_NOOP("", "%1% has invited you to enter the %2% room")) % nickResolver->jidToNick(mucInviteEvent->getInviter()) % mucInviteEvent->getRoomJID());
+		// FIXME: not show avatar or greyed out avatar for mediated invites
+		notifier->showMessage(Notifier::SystemMessage, title, message, avatarManager->getAvatarPath(mucInviteEvent->getInviter()), boost::bind(&EventNotifier::handleNotificationActivated, this, mucInviteEvent->getInviter()));
+	}
 }
 
 
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index e7246c6..9305314 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -57,7 +57,7 @@ namespace Swift {
 			virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0;
 			virtual void setFileTransferProgress(std::string, const int percentageDone) = 0;
 			virtual void setFileTransferStatus(std::string, const FileTransferState state, const std::string& msg = "") = 0;
-			virtual void addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password, bool direct = true) = 0;
+			virtual void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true) = 0;
 
 			// message receipts
 			virtual void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) = 0;
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index dab1e90..f92bd8b 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -50,7 +50,7 @@ namespace Swift {
 			void setAvailableOccupantActions(const std::vector<OccupantAction>&/* actions*/) {}
 			void setSubject(const std::string& /*subject*/) {}
 			virtual void showRoomConfigurationForm(Form::ref) {}
-			virtual void addMUCInvitation(const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool = true) {};
+			virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool = true) {};
 			virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) {}
 			virtual void setAvailableRoomActions(const std::vector<RoomAction> &) {};
 
diff --git a/Swift/Controllers/XMPPEvents/EventController.cpp b/Swift/Controllers/XMPPEvents/EventController.cpp
index 98fd634..0808aa0 100644
--- a/Swift/Controllers/XMPPEvents/EventController.cpp
+++ b/Swift/Controllers/XMPPEvents/EventController.cpp
@@ -13,6 +13,7 @@
 #include "Swift/Controllers/XMPPEvents/MessageEvent.h"
 #include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
 #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h"
+#include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h"
 
 namespace Swift {
 
@@ -29,7 +30,8 @@ void EventController::handleIncomingEvent(boost::shared_ptr<StanzaEvent> sourceE
 	boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(sourceEvent);
 	boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(sourceEvent);
 	boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(sourceEvent);
-	if ((messageEvent && messageEvent->isReadable()) || subscriptionEvent || errorEvent) {
+	boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(sourceEvent);
+	if ((messageEvent && messageEvent->isReadable()) || subscriptionEvent || errorEvent || mucInviteEvent) {
 		events_.push_back(sourceEvent);
 		sourceEvent->onConclusion.connect(boost::bind(&EventController::handleEventConcluded, this, sourceEvent));
 		onEventQueueLengthChange(events_.size());
diff --git a/Swift/Controllers/XMPPEvents/MUCInviteEvent.h b/Swift/Controllers/XMPPEvents/MUCInviteEvent.h
new file mode 100644
index 0000000..0b430cd
--- /dev/null
+++ b/Swift/Controllers/XMPPEvents/MUCInviteEvent.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+namespace Swift {
+
+	class MUCInviteEvent : public StanzaEvent {
+	public:
+		typedef boost::shared_ptr<MUCInviteEvent> ref;
+
+	public:
+		MUCInviteEvent(const JID& inviter, const JID& roomJID, const std::string& reason, const std::string& password, bool direct) : inviter_(inviter), roomJID_(roomJID), reason_(reason), password_(password), direct_(direct) {}
+
+		const JID& getInviter() const { return inviter_; }
+		const JID& getRoomJID() const { return roomJID_; }
+		const std::string& getReason() const { return reason_; }
+		const std::string& getPassword() const { return password_; }
+		bool getDirect() const { return direct_; }
+
+	private:
+		JID inviter_;
+		JID roomJID_;
+		std::string reason_;
+		std::string password_;
+		bool direct_;
+	};
+}
diff --git a/Swift/QtUI/EventViewer/EventDelegate.cpp b/Swift/QtUI/EventViewer/EventDelegate.cpp
index 79b8854..9ecdd34 100644
--- a/Swift/QtUI/EventViewer/EventDelegate.cpp
+++ b/Swift/QtUI/EventViewer/EventDelegate.cpp
@@ -11,10 +11,11 @@
 #include "Swift/Controllers/XMPPEvents/MessageEvent.h"
 #include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
 #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h"
+#include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h"
 
 namespace Swift {
 
-EventDelegate::EventDelegate() : QStyledItemDelegate(), messageDelegate_(QtEvent::SenderRole, Qt::DisplayRole, false), subscriptionDelegate_(QtEvent::SenderRole, Qt::DisplayRole, true), errorDelegate_(QtEvent::SenderRole, Qt::DisplayRole, true) {
+EventDelegate::EventDelegate() : QStyledItemDelegate(), messageDelegate_(QtEvent::SenderRole, Qt::DisplayRole, false), subscriptionDelegate_(QtEvent::SenderRole, Qt::DisplayRole, true), errorDelegate_(QtEvent::SenderRole, Qt::DisplayRole, true), mucInviteDelegate_(QtEvent::SenderRole, Qt::DisplayRole, false) {
 
 }
 
@@ -27,6 +28,7 @@ QSize EventDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIn
 	case MessageEventType: return messageDelegate_.sizeHint(option, item);
 	case SubscriptionEventType: return subscriptionDelegate_.sizeHint(option, item);
 	case ErrorEventType: return errorDelegate_.sizeHint(option, item);
+	case MUCInviteEventType: return mucInviteDelegate_.sizeHint(option, item);
 	default: return QStyledItemDelegate::sizeHint(option, index);
 	}
 }
@@ -41,6 +43,7 @@ void EventDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
 	case MessageEventType: messageDelegate_.paint(painter, option, item);break;
 	case SubscriptionEventType: subscriptionDelegate_.paint(painter, option, item);break;
 	case ErrorEventType: errorDelegate_.paint(painter, option, item);break;
+	case MUCInviteEventType: mucInviteDelegate_.paint(painter, option, item);break;
 	default: QStyledItemDelegate::paint(painter, option, index);
 	}
 }
@@ -52,6 +55,8 @@ EventType EventDelegate::getEventType(boost::shared_ptr<StanzaEvent> event) cons
 	if (subscriptionEvent) return SubscriptionEventType;
 	boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event);
 	if (errorEvent) return ErrorEventType;
+	boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event);
+	if (mucInviteEvent) return MUCInviteEventType;
 	//I don't know what this is.
 	assert(false);
 	return MessageEventType;
diff --git a/Swift/QtUI/EventViewer/EventDelegate.h b/Swift/QtUI/EventViewer/EventDelegate.h
index 2ad741c..1ebaff2 100644
--- a/Swift/QtUI/EventViewer/EventDelegate.h
+++ b/Swift/QtUI/EventViewer/EventDelegate.h
@@ -12,7 +12,7 @@
 #include "Swift/QtUI/EventViewer/TwoLineDelegate.h"
 
 namespace Swift {
-	enum EventType {MessageEventType, SubscriptionEventType, ErrorEventType};
+	enum EventType {MessageEventType, SubscriptionEventType, ErrorEventType, MUCInviteEventType};
 	class EventDelegate : public QStyledItemDelegate {
 		Q_OBJECT
 		public:
@@ -25,6 +25,7 @@ namespace Swift {
 			TwoLineDelegate messageDelegate_;
 			TwoLineDelegate subscriptionDelegate_;
 			TwoLineDelegate errorDelegate_;
+			TwoLineDelegate mucInviteDelegate_;
 	};
 }
 
diff --git a/Swift/QtUI/EventViewer/QtEvent.cpp b/Swift/QtUI/EventViewer/QtEvent.cpp
index e7ea473..3c6f16c 100644
--- a/Swift/QtUI/EventViewer/QtEvent.cpp
+++ b/Swift/QtUI/EventViewer/QtEvent.cpp
@@ -11,6 +11,7 @@
 #include "Swift/Controllers/XMPPEvents/MessageEvent.h"
 #include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
 #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h"
+#include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h"
 
 #include "Swift/QtUI/QtSwiftUtil.h"
 
@@ -47,6 +48,10 @@ QString QtEvent::sender() {
 	if (errorEvent) {
 		return P2QSTRING(errorEvent->getJID().toBare().toString());
 	}
+	boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event_);
+	if (mucInviteEvent) {
+		return P2QSTRING(mucInviteEvent->getInviter().toString());
+	}
 	return "";
 }
 
@@ -71,6 +76,11 @@ QString QtEvent::text() {
 	if (errorEvent) {
 		return P2QSTRING(errorEvent->getText());
 	}
+	boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event_);
+	if (mucInviteEvent) {
+		QString message = QString(QObject::tr("%1 has invited you to enter the %2 room.")).arg(P2QSTRING(mucInviteEvent->getInviter().toBare().toString())).arg(P2QSTRING(mucInviteEvent->getRoomJID().toString()));
+		return message;
+	}
 	return "";
 }
 
diff --git a/Swift/QtUI/EventViewer/QtEventWindow.cpp b/Swift/QtUI/EventViewer/QtEventWindow.cpp
index fdc0194..35473b6 100644
--- a/Swift/QtUI/EventViewer/QtEventWindow.cpp
+++ b/Swift/QtUI/EventViewer/QtEventWindow.cpp
@@ -16,6 +16,7 @@
 #include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
 #include "Swift/QtUI/QtSubscriptionRequestWindow.h"
 #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h"
+#include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h"
 #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
 #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
 
@@ -75,8 +76,9 @@ void QtEventWindow::handleItemActivated(const QModelIndex& item) {
 	QtEvent* event = model_->getItem(item.row());
 	boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event->getEvent());
 	boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event->getEvent());
+	boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event->getEvent());
 	boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event->getEvent());
-	
+
 	if (messageEvent) {
 		if (messageEvent->getStanza()->getType() == Message::Groupchat) {
 			eventStream_->send(boost::shared_ptr<UIEvent>(new JoinMUCUIEvent(messageEvent->getStanza()->getFrom().toBare(), messageEvent->getStanza()->getTo().getResource())));
@@ -86,6 +88,9 @@ void QtEventWindow::handleItemActivated(const QModelIndex& item) {
 	} else if (subscriptionEvent) {
 		QtSubscriptionRequestWindow* window = QtSubscriptionRequestWindow::getWindow(subscriptionEvent, this);
 		window->show();
+	} else if (mucInviteEvent) {
+		eventStream_->send(boost::shared_ptr<UIEvent>(new RequestChatUIEvent(mucInviteEvent->getInviter())));
+		mucInviteEvent->conclude();
 	} else {
 		if (errorEvent) {
 			errorEvent->conclude();
diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp
index db00ba0..b0c4e09 100644
--- a/Swift/QtUI/QtChatView.cpp
+++ b/Swift/QtUI/QtChatView.cpp
@@ -22,6 +22,7 @@
 
 #include "QtWebView.h"
 #include "QtChatTheme.h"
+#include "QtChatWindow.h"
 #include "QtSwiftUtil.h"
 
 
@@ -326,13 +327,13 @@ void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransfe
 
 	QString newInnerHTML = "";
 	if (state == ChatWindow::WaitingForAccept) {
-		newInnerHTML =	"Waiting for other side to accept the transfer.<br/>"
-						"<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">";
+		newInnerHTML =	"Waiting for other side to accept the transfer.<br/>" +
+			QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id);
 	}
 	if (state == ChatWindow::Negotiating) {
 		// replace with text "Negotiaging" + Cancel button
-		newInnerHTML =	"Negotiating...<br/>"
-						"<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">";
+		newInnerHTML =	"Negotiating...<br/>" +
+			QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id);
 	}
 	else if (state == ChatWindow::Transferring) {
 		// progress bar + Cancel Button
@@ -342,8 +343,8 @@ void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransfe
 									"0%"
 								"</div>"
 							"</div>"
-						"</div>"
-						"<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">";
+						"</div>" +
+						QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id);
 	}
 	else if (state == ChatWindow::Canceled) {
 		newInnerHTML = "Transfer has been canceled!";
@@ -359,4 +360,12 @@ void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransfe
 	ftElement.setInnerXml(newInnerHTML);
 }
 
+void QtChatView::setMUCInvitationJoined(QString id) {
+	QWebElement divElement = findDivElementWithID(document_, id);
+	QWebElement buttonElement = divElement.findFirst("input#mucinvite");
+	if (!buttonElement.isNull()) {
+		buttonElement.setAttribute("value", tr("Return to room"));
+	}
+}
+
 }
diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h
index 0cc521a..6b40c05 100644
--- a/Swift/QtUI/QtChatView.h
+++ b/Swift/QtUI/QtChatView.h
@@ -42,6 +42,7 @@ namespace Swift {
 			void addToJSEnvironment(const QString&, QObject*);
 			void setFileTransferProgress(QString id, const int percentageDone);
 			void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg);
+			void setMUCInvitationJoined(QString id);
 
 		signals:
 			void gotFocus();
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index c6519ba..cf520ee 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -22,7 +22,7 @@
 #include <Swift/Controllers/UIEvents/UIEventStream.h>
 #include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
 #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
-#include "QtFileTransferJSBridge.h"
+#include "QtChatWindowJSBridge.h"
 
 #include <boost/cstdint.hpp>
 #include <boost/format.hpp>
@@ -50,7 +50,14 @@
 #include <QDebug>
 
 namespace Swift {
-QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageWasSystem_(false), previousMessageWasPresence_(false), previousMessageWasFileTransfer_(false), eventStream_(eventStream) {
+
+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");
+
+QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream) {
 	settings_ = settings;
 	unreadCount_ = 0;
 	idCounter_ = 0;
@@ -158,16 +165,13 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
 	treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1));
 	treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2));
 
-	fileTransferJS = new QtFileTransferJSBridge();
-	messageLog_->addToJSEnvironment("filetransfer", fileTransferJS);
-	connect(fileTransferJS, SIGNAL(setDescription(QString)), this, SLOT(handleFileTransferSetDescription(QString)));
-	connect(fileTransferJS, SIGNAL(sendRequest(QString)), this, SLOT(handleFileTransferStart(QString)));
-	connect(fileTransferJS, SIGNAL(acceptRequest(QString, QString)), this, SLOT(handleFileTransferAccept(QString, QString)));
-	connect(fileTransferJS, SIGNAL(cancel(QString)), this, SLOT(handleFileTransferCancel(QString)));
+	jsBridge = new QtChatWindowJSBridge();
+	messageLog_->addToJSEnvironment("chatwindow", jsBridge);
+	connect(jsBridge, SIGNAL(buttonClicked(QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString)));
 }
 
 QtChatWindow::~QtChatWindow() {
-	delete fileTransferJS;
+	delete jsBridge;
 	if (mucConfigurationWindow_) {
 		delete mucConfigurationWindow_.data();
 	}
@@ -178,6 +182,10 @@ void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) {
 	onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(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);
 }
@@ -448,7 +456,7 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri
 	QString styleSpanEnd = style == "" ? "" : "</span>";
 	htmlString += styleSpanStart + messageHTML + styleSpanEnd;
 
-	bool appendToPrevious = !previousMessageWasFileTransfer_ && !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
+	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf);
 	if (lastLineTracker_.getShouldMoveLastLine()) {
 		/* should this be queued? */
 		messageLog_->addLastSeenLine();
@@ -461,9 +469,7 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri
 
 	previousMessageWasSelf_ = senderIsSelf;
 	previousSenderName_ = P2QSTRING(senderName);
-	previousMessageWasSystem_ = false;
-	previousMessageWasPresence_ = false;
-	previousMessageWasFileTransfer_ = false;
+	previousMessageKind_ = PreviousMessageWasMessage;
 	return id;
 }
 
@@ -519,31 +525,39 @@ std::string formatSize(const boost::uintmax_t bytes) {
 	return str( boost::format("%.1lf %sB") % engBytes % (power > 0 ? siPrefix[power-1] : "") );
 }
 
+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(arg1).arg(arg2).arg(arg3);
+	return html;
+}
+
 std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) {
 	qDebug() << "addFileTransfer";
-	std::string ft_id = "ft" + boost::lexical_cast<std::string>(idCounter_++);
+	QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));
 	
-	std::string htmlString;
+	QString htmlString;
+	QString formattedFileSize = P2QSTRING(formatSize(sizeInBytes));
 	if (senderIsSelf) {
 		// outgoing
-		htmlString = "Send file: " + filename + " ( " + formatSize(sizeInBytes) + ") </br>" +
+		htmlString = tr("Send file)") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") </br>" +
 			"<div id='" + ft_id + "'>" +
-			"<input id='discard' type='submit' value='Cancel' onclick='filetransfer.cancel(\"" + ft_id + "\");' />" +
-			"<input id='description' type='submit' value='Set Description' onclick='filetransfer.setDescription(\"" + ft_id + "\");' />" +
-			"<input id='send' type='submit' value='Send' onclick='filetransfer.sendRequest(\"" + 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 = "Receiving file: " + filename + " ( " + formatSize(sizeInBytes)  + ") </br>" +
+		htmlString = "Receiving file: " + P2QSTRING(filename) + " ( " + formattedFileSize  + ") </br>" +
 			"<div id='" + ft_id + "'>" +
-			"<input id='discard' type='submit' value='Cancel' onclick='filetransfer.cancel(\"" + ft_id + "\");' />" +
-			"<input id='accept' type='submit' value='Accept' onclick='filetransfer.acceptRequest(\"" + ft_id + "\", \"" + filename + "\");' />" +
+				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 = !previousMessageWasFileTransfer_ && !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
+	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasFileTransfer, senderName, senderIsSelf);
 	if (lastLineTracker_.getShouldMoveLastLine()) {
 		/* should this be queued? */
 		messageLog_->addLastSeenLine();
@@ -552,10 +566,12 @@ 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_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(QString::fromStdString(htmlString), Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
-
+	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))));
 
-	return ft_id;
+	previousMessageWasSelf_ = senderIsSelf;
+	previousSenderName_ = P2QSTRING(senderName);
+	previousMessageKind_ = PreviousMessageWasFileTransfer;
+	return Q2PSTRING(ft_id);
 }
 
 void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) {
@@ -566,33 +582,44 @@ void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState
 	messageLog_->setFileTransferStatus(QString::fromStdString(id), state, QString::fromStdString(msg));
 }
 
-void QtChatWindow::handleFileTransferCancel(QString id) {
-	qDebug() << "QtChatWindow::handleFileTransferCancel(" << id << ")";
-	onFileTransferCancel(Q2PSTRING(id));
-}
-
-void QtChatWindow::handleFileTransferSetDescription(QString id) {
-	bool ok = false;
-	QString text = QInputDialog::getText(this, tr("File transfer description"),
-		tr("Description:"), QLineEdit::Normal, "", &ok);
-	if (ok) {
-		descriptions[id] = text;
+void QtChatWindow::handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3) {
+	if (id.startsWith(ButtonFileTransferCancel)) {
+		QString ft_id = arg1;
+		onFileTransferCancel(Q2PSTRING(ft_id));
 	}
-}
-
-void QtChatWindow::handleFileTransferStart(QString id) {
-	qDebug() << "QtChatWindow::handleFileTransferStart(" << id << ")";
-	
-	QString text = descriptions.find(id) == descriptions.end() ? QString() : descriptions[id];
-	onFileTransferStart(Q2PSTRING(id), Q2PSTRING(text));
-}
+	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;
 
-void QtChatWindow::handleFileTransferAccept(QString id, QString filename) {
-	qDebug() << "QtChatWindow::handleFileTransferAccept(" << id << ", " << filename << ")";
+		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;
 
-	QString path = QFileDialog::getSaveFileName(this, tr("Save File"), filename);
-	if (!path.isEmpty()) {
-		onFileTransferAccept(Q2PSTRING(id), Q2PSTRING(path));
+		eventStream_->send(boost::make_shared<JoinMUCUIEvent>(Q2PSTRING(roomJID), Q2PSTRING(password)));
+		messageLog_->setMUCInvitationJoined(elementID);
+	}
+	else {
+		qDebug() << "Unknown HTML button! ( " << id << " )";
 	}
 }
 
@@ -606,9 +633,7 @@ void QtChatWindow::addErrorMessage(const std::string& errorMessage) {
 	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_)));
 
 	previousMessageWasSelf_ = false;
-	previousMessageWasSystem_ = true;
-	previousMessageWasPresence_ = false;
-	previousMessageWasFileTransfer_ = false;
+	previousMessageKind_ = PreviousMessageWasSystem;
 }
 
 void QtChatWindow::addSystemMessage(const std::string& message) {
@@ -621,10 +646,7 @@ void QtChatWindow::addSystemMessage(const std::string& message) {
 	messageHTML.replace("\n","<br/>");
 	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
 
-	previousMessageWasSelf_ = false;
-	previousMessageWasSystem_ = true;
-	previousMessageWasPresence_ = false;
-	previousMessageWasFileTransfer_ = false;
+	previousMessageKind_ = PreviousMessageWasSystem;
 }
 
 void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) {
@@ -649,9 +671,7 @@ void QtChatWindow::addPresenceMessage(const std::string& message) {
 	messageHTML.replace("\n","<br/>");
 	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
 
-	previousMessageWasSelf_ = false;
-	previousMessageWasSystem_ = false;
-	previousMessageWasPresence_ = true;
+	previousMessageKind_ = PreviousMessageWasPresence;
 }
 
 
@@ -812,35 +832,35 @@ void QtChatWindow::showRoomConfigurationForm(Form::ref form) {
 	mucConfigurationWindow_->onFormCancelled.connect(boost::bind(boost::ref(onConfigurationFormCancelled)));
 }
 
-void QtChatWindow::addMUCInvitation(const JID& jid, const std::string& reason, const std::string& /*password*/, bool direct) {
-	bool accepted = false;
-	QMessageBox msgBox;
-	//FIXME: horrid modal untranslated popup. Fix before release.
-	msgBox.setText(QString("You have been invited to the room %1 by %2.").arg(P2QSTRING(jid.toString())).arg(contact_));
-	QString reasonString;
+void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) {
+	if (isWidgetSelected()) {
+		onAllMessagesRead();
+	}
+
+	QString htmlString = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + " </br>";
 	if (!reason.empty()) {
-		reasonString = QString("\"%1\"").arg(P2QSTRING(reason));
+		htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "</br>";
 	}
 	if (!direct) {
-		if (!reasonString.isEmpty()) {
-			reasonString += " ";
-		}
-		reasonString += QString(" (%1 may not have really sent this invitation)").arg(contact_);
-	}
-	msgBox.setInformativeText(QString("Accept invitation from %1 to enter %2?\n%3").arg(contact_).arg(P2QSTRING(jid.toString())).arg(reasonString));
-	msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
-	msgBox.setDefaultButton(QMessageBox::Yes);
-	int ret = msgBox.exec();
-	switch (ret) {
-	   case QMessageBox::Yes:
-	       accepted = true;
-	       break;
-	   default:
-	       break;
-	 }
-	if (accepted) {
-		eventStream_->send(boost::make_shared<JoinMUCUIEvent>(jid));
+		htmlString += QObject::tr("This person may not have really sent this invitation!") + "</br>";
 	}
+
+	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) +
+		"</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;
+	}
+	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)));
 }
 
 }
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index 189a12a..18eb092 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -31,11 +31,19 @@ namespace Swift {
 	class TreeWidget;
 	class QtTextEdit;
 	class UIEventStream;
-	class QtFileTransferJSBridge;
+	class QtChatWindowJSBridge;
 	class SettingsProvider;
 
 	class QtChatWindow : public QtTabbable, public ChatWindow {
 		Q_OBJECT
+
+		public:
+			static const QString ButtonFileTransferCancel;
+			static const QString ButtonFileTransferSetDescription;
+			static const QString ButtonFileTransferSendRequest;
+			static const QString ButtonFileTransferAcceptRequest;
+			static const QString ButtonMUCInvite;
+
 		public:
 			QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings);
 			~QtChatWindow();
@@ -77,10 +85,12 @@ namespace Swift {
 			virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions);
 			void setSubject(const std::string& subject);
 			void showRoomConfigurationForm(Form::ref);
-			void addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password, bool direct = true);
+			void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true);
 			void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&);
 			void setAvailableRoomActions(const std::vector<RoomAction> &actions);
 
+			static QString buildChatWindowButton(const QString& name, const QString& id, const QString& arg1 = QString(), const QString& arg2 = QString(), const QString& arg3 = QString());
+
 		public slots:
 			void handleChangeSplitterState(QByteArray state);
 			void handleFontResized(int fontSizeSteps);
@@ -113,20 +123,27 @@ namespace Swift {
 			void handleAlertButtonClicked();
 			void handleActionButtonClicked();
 
-			
-			void handleFileTransferCancel(QString id);
-			void handleFileTransferSetDescription(QString id);
-			void handleFileTransferStart(QString id);
-			void handleFileTransferAccept(QString id, QString filename);
+			void handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3);
 			void handleAffiliationEditorAccepted();
 
 		private:
+			enum PreviousMessageKind {
+				PreviosuMessageWasNone,
+				PreviousMessageWasMessage,
+				PreviousMessageWasSystem,
+				PreviousMessageWasPresence,
+				PreviousMessageWasFileTransfer,
+				PreviousMessageWasMUCInvite
+			};
+
+		private:
 			void updateTitleWithUnreadCount();
 			void tabComplete();
 			void beginCorrection();
 			void cancelCorrection();
 			std::string 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);
 			void handleOccupantSelectionChanged(RosterItem* item);
+			bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const;
 
 			int unreadCount_;
 			bool contactIsTyping_;
@@ -148,9 +165,7 @@ namespace Swift {
 			std::vector<SecurityLabelsCatalog::Item> availableLabels_;
 			bool isCorrection_;
 			bool previousMessageWasSelf_;
-			bool previousMessageWasSystem_;
-			bool previousMessageWasPresence_;
-			bool previousMessageWasFileTransfer_;
+			PreviousMessageKind previousMessageKind_;
 			QString previousSenderName_;
 			bool inputClearing_;
 			UIEventStream* eventStream_;
@@ -159,7 +174,7 @@ namespace Swift {
 			Tristate correctionEnabled_;
 			QString alertStyleSheet_;
 			std::map<QString, QString> descriptions;
-			QtFileTransferJSBridge* fileTransferJS;
+			QtChatWindowJSBridge* jsBridge;
 			QPointer<QtMUCConfigurationWindow> mucConfigurationWindow_;
 			QPointer<QtAffiliationEditor> affiliationEditor_;
 			int idCounter_;
diff --git a/Swift/QtUI/QtChatWindowJSBridge.cpp b/Swift/QtUI/QtChatWindowJSBridge.cpp
new file mode 100644
index 0000000..db67d79
--- /dev/null
+++ b/Swift/QtUI/QtChatWindowJSBridge.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtChatWindowJSBridge.h"
+
+namespace Swift {
+
+QtChatWindowJSBridge::QtChatWindowJSBridge() {
+	
+}
+
+QtChatWindowJSBridge::~QtChatWindowJSBridge() {
+	
+}
+
+}
diff --git a/Swift/QtUI/QtChatWindowJSBridge.h b/Swift/QtUI/QtChatWindowJSBridge.h
new file mode 100644
index 0000000..8e6f0c2
--- /dev/null
+++ b/Swift/QtUI/QtChatWindowJSBridge.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QObject>
+
+#include <map>
+
+namespace Swift {
+
+class FileTransferController;
+
+class QtChatWindowJSBridge : public QObject {
+	 Q_OBJECT
+public:
+	QtChatWindowJSBridge();
+	virtual ~QtChatWindowJSBridge();
+signals:
+	void buttonClicked(QString id, QString arg1, QString arg2, QString arg3);
+};
+
+}
diff --git a/Swift/QtUI/QtFileTransferJSBridge.cpp b/Swift/QtUI/QtFileTransferJSBridge.cpp
deleted file mode 100644
index 76c1509..0000000
--- a/Swift/QtUI/QtFileTransferJSBridge.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 2011 Tobias Markmann
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-#include "QtFileTransferJSBridge.h"
-
-namespace Swift {
-
-QtFileTransferJSBridge::QtFileTransferJSBridge() {
-	
-}
-
-QtFileTransferJSBridge::~QtFileTransferJSBridge() {
-	
-}
-
-}
\ No newline at end of file
diff --git a/Swift/QtUI/QtFileTransferJSBridge.h b/Swift/QtUI/QtFileTransferJSBridge.h
deleted file mode 100644
index bd884e5..0000000
--- a/Swift/QtUI/QtFileTransferJSBridge.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2011 Tobias Markmann
- * Licensed under the simplified BSD license.
- * See Documentation/Licenses/BSD-simplified.txt for more information.
- */
-
-#pragma once
-
-#include <QObject>
-
-#include <map>
-
-namespace Swift {
-
-class FileTransferController;
-
-class QtFileTransferJSBridge : public QObject {
-	 Q_OBJECT
-public:
-	QtFileTransferJSBridge();
-	virtual ~QtFileTransferJSBridge();
-signals:
-	void discard(QString id);
-	void sendRequest(QString id);
-	void setDescription(QString id);
-	void acceptRequest(QString id, QString filename);
-	void cancel(QString id);
-};
-
-}
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 0971577..06c5bc4 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -144,7 +144,7 @@ sources = [
     "QtWebView.cpp",
     "qrc_DefaultTheme.cc",
     "qrc_Swift.cc",
-    "QtFileTransferJSBridge.cpp",
+    "QtChatWindowJSBridge.cpp",
     "QtMUCConfigurationWindow.cpp",
     "QtAffiliationEditor.cpp",
     "QtUISettingConstants.cpp"
-- 
cgit v0.10.2-6-g49f6