From c889fadf1dede0955ce74ce6897094e4f0c3e341 Mon Sep 17 00:00:00 2001
From: Thilo Cestonaro <thilo@cestona.ro>
Date: Sun, 2 Sep 2012 22:27:42 +0200
Subject: moved QtChatWindow Log stuff into QtWebKitChatView added
 QtChatViewFactory started implementation of QtBarriersFreeChatView

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

diff --git a/BuildTools/Cppcheck.sh b/BuildTools/Cppcheck.sh
index 7b6a33b..753175b 100755
--- a/BuildTools/Cppcheck.sh
+++ b/BuildTools/Cppcheck.sh
@@ -11,7 +11,7 @@ cppcheck $@  \
 	-i Swiftob/linit.cpp \
 	-i Swift/QtUI/EventViewer/main.cpp \
 	-i Swift/QtUI/ApplicationTest \
-	-i Swift/QtUI/ChatView/main.cpp \
+	-i Swift/QtUI/WebKitChatView/main.cpp \
 	-i Swift/QtUI/Roster/main.cpp \
 	-i Swift/QtUI/NotifierTest/NotifierTest.cpp \
 	\
diff --git a/Swift/QtUI/ChatView/ChatView.pro b/Swift/QtUI/ChatView/ChatView.pro
deleted file mode 100644
index 3017d35..0000000
--- a/Swift/QtUI/ChatView/ChatView.pro
+++ /dev/null
@@ -1,8 +0,0 @@
-TEMPLATE = app
-TARGET = ChatView
-DEPENDPATH += .
-INCLUDEPATH += . ../../../Swiften/3rdParty/Boost
-QT += webkit network
-HEADERS += ../QtChatView.h
-SOURCES += main.cpp ../QtChatView.cpp ../ChatSnippet.cpp ../MessageSnippet.cpp ../SystemMessageSnippet.cpp
-RESOURCES += ../DefaultTheme.qrc ../Swift.qrc
diff --git a/Swift/QtUI/ChatView/main.cpp b/Swift/QtUI/ChatView/main.cpp
deleted file mode 100644
index 0f53432..0000000
--- a/Swift/QtUI/ChatView/main.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
- */
-
-#include <QtDebug>
-#include <QApplication>
-#include <iostream>
-#include <QWidget>
-#include <QFile>
-#include <QDateTime>
-#include <QLineEdit>
-#include <QVBoxLayout>
-#include <QWebView>
-#include <QWebFrame>
-#include <QNetworkRequest>
-#include <QNetworkReply>
-#include <QNetworkAccessManager>
-#include "../QtChatView.h"
-#include "../MessageSnippet.h"
-#include "../SystemMessageSnippet.h"
-
-using namespace Swift;
-
-/*
-class MyNetworkReply : public QNetworkReply {
-	public:
-		MyNetworkReply() {
-		}
-		
-		qint64 readData(char*, qint64) {
-			return 0;
-		}
-
-		virtual void abort() {
-		}
-};
-
-class MyNetworkAccessManager : public QNetworkAccessManager {
-	public:
-		MyNetworkAccessManager() {
-		}
-
-		QNetworkReply * createRequest (Operation op, const QNetworkRequest& request, QIODevice* outgoingData = 0) {
-			assert(op == QNetworkAccessManager::GetOperation);
-			qDebug() << "Requesting: " << request.url();
-			return QNetworkAccessManager::createRequest(op, request, outgoingData);
-			//return new MyNetworkReply();
-		}
-};
-
-			QVBoxLayout* mainLayout = new QVBoxLayout(this);
-			webView_ = new QWebView(this);
-
-			QFile file(":/themes/Stockholm/Contents/Resources/Incoming/Content.html");
-			file.open(QIODevice::ReadOnly);
-			QString content = QString::fromUtf8(file.readAll());
-
-			webPage_ = new QWebPage(this);
-			webPage_->setNetworkAccessManager(new MyNetworkAccessManager());
-			webView_->setPage(webPage_);
-			QString pagehtml = 
-				"<head>"
-					//"<base href=\"file:///Users/remko/src/swift/resources/themes/Stockholm/Contents/Resources/\"/>"
-					"<base href=\"file:///Users/remko/src/swift/resources/themes/Stockholm/Contents/Resources/\"/>"
-					"<link rel=\"stylesheet\" type=\"text/css\" href=\"main.css\"/>"
-					"<link rel=\"stylesheet\" type=\"text/css\" href=\"Variants/Alt Blue - Blue.css\"/>"
-				"</head><body>" + content + "</body>";
-			qDebug() << pagehtml;
-			webPage_->mainFrame()->setHtml(pagehtml);
-
-*/
-
-/*
-
-class ChatView : public QWidget {
-	public:
-		ChatView(QWidget* parent) : QWidget(parent) {
-			setFocusPolicy(Qt::NoFocus);
-
-			QVBoxLayout* mainLayout = new QVBoxLayout(this);
-			mainLayout->setSpacing(0);
-			mainLayout->setContentsMargins(0,0,0,0);
-
-			webView_ = new QWebView(this);
-			webView_->setFocusPolicy(Qt::NoFocus);
-			mainLayout->addWidget(webView_);
-
-			webPage_ = new QWebPage(this);
-			webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
-			webView_->setPage(webPage_);
-			connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard()));
-
-			QString pageHTML = "<head></head><body><div id=\"chat\"></div></body>";
-			webPage_->mainFrame()->setHtml(pageHTML);
-		}
-
-		void appendHTML(const QString& html) {
-			webPage_->mainFrame()->evaluateJavaScript(
-				"newNode = document.createElement(\"div\");"
-				"newNode.innerHTML = \"" + html + "\";" 
-				"chatElement = document.getElementById(\"chat\");"
-				"chatElement.appendChild(newNode);");
-			webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical));
-		}
-
-	private:
-		QWebView* webView_;
-		QWebPage* webPage_;
-};
-*/
-
-class MyWidget : public QWidget {
-		Q_OBJECT
-
-	public:
-		MyWidget() : previousWasIncoming_(false), previousWasOutgoing_(false), previousWasSystem_(false) {
-			QVBoxLayout* mainLayout = new QVBoxLayout(this);
-			chatView_ = new QtChatView(this);
-			mainLayout->addWidget(chatView_);
-			input1_ = new QLineEdit(this);
-			connect(input1_, SIGNAL(returnPressed()), SLOT(addIncoming()));
-			mainLayout->addWidget(input1_);
-			input2_ = new QLineEdit(this);
-			connect(input2_, SIGNAL(returnPressed()), SLOT(addOutgoing()));
-			mainLayout->addWidget(input2_);
-			input3_ = new QLineEdit(this);
-			connect(input3_, SIGNAL(returnPressed()), SLOT(addSystem()));
-			mainLayout->addWidget(input3_);
-
-			resize(300,200);
-		}
-	
-	public slots:
-		void addIncoming() {
-			chatView_->addMessage(MessageSnippet(input1_->text(), "Me", QDateTime::currentDateTime(), "qrc:/icons/avatar.png", true, previousWasIncoming_));
-			previousWasIncoming_ = true;
-			previousWasOutgoing_ = false;
-			previousWasSystem_ = false;
-			input1_->clear();
-		}
-
-		void addOutgoing() {
-			chatView_->addMessage(MessageSnippet(input2_->text(), "You", QDateTime::currentDateTime(), "qrc:/icons/avatar.png", false, previousWasOutgoing_));
-			previousWasIncoming_ = false;
-			previousWasOutgoing_ = true;
-			previousWasSystem_ = false;
-			input2_->clear();
-		}
-
-		void addSystem() {
-			chatView_->addMessage(SystemMessageSnippet(input3_->text(), QDateTime::currentDateTime(), previousWasSystem_));
-			previousWasIncoming_ = false;
-			previousWasOutgoing_ = false;
-			previousWasSystem_ = true;
-			input3_->clear();
-		}
-
-	private:
-		bool previousWasIncoming_;
-		bool previousWasOutgoing_;
-		bool previousWasSystem_;
-		QtChatView* chatView_;
-		QLineEdit* input1_;
-		QLineEdit* input2_;
-		QLineEdit* input3_;
-};
-		
-
-int main(int argc, char* argv[]) {
-	QApplication app(argc, argv);
-	MyWidget w;
-	w.show();
-	return app.exec();
-}
-
-#include "main.moc"
diff --git a/Swift/QtUI/QtBarriersFreeChatView.cpp b/Swift/QtUI/QtBarriersFreeChatView.cpp
new file mode 100644
index 0000000..eef58ad
--- /dev/null
+++ b/Swift/QtUI/QtBarriersFreeChatView.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+#include "QtBarriersFreeChatView.h"
+#include "QtSwiftUtil.h"
+
+#include <Swiften/Base/Log.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <QDateTime>
+#include <QDebug>
+
+namespace Swift {
+	QtBarriersFreeChatView::QtBarriersFreeChatView(QtChatTheme*, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtChatView(parent), eventStream_(eventStream), settings_(settings), emoticons_(emoticons) {
+
+		QVBoxLayout* mainLayout = new QVBoxLayout(this);
+		mainLayout->setSpacing(0);
+		mainLayout->setContentsMargins(0,0,0,0);
+		textEdit_ = new QTextEdit();
+		textEdit_->setReadOnly(true);
+
+		mainLayout->addWidget(textEdit_);
+
+		idCounter_ = 0;
+	}
+
+	QtBarriersFreeChatView::~QtBarriersFreeChatView() {
+
+	}
+
+	void QtBarriersFreeChatView::replaceLastMessage(const QString& newMessage) {
+
+	}
+
+	void QtBarriersFreeChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg) {
+
+	}
+
+	void QtBarriersFreeChatView::setFileTransferProgress(QString id, const int percentageDone) {
+
+	}
+
+	void QtBarriersFreeChatView::addToJSEnvironment(const QString&, QObject*) {
+
+	}
+
+	void QtBarriersFreeChatView::setAckState(std::string const& id, ChatWindow::AckState) {
+
+	}
+
+	void QtBarriersFreeChatView::setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) {
+
+	}
+
+	std::string QtBarriersFreeChatView::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) {
+		SWIFT_LOG(debug) << "[QtBarriersFreeChatView::addMessage] - entered" << std::endl;
+		std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
+		textEdit_->append("[" + B2QDATE(time).toString("hh:mm:ss") + "] <" + Qt::escape(P2QSTRING(senderName)) + "> " + Qt::escape(P2QSTRING(message)));
+		SWIFT_LOG(debug) << "[QtBarriersFreeChatView::addMessage] - return: " << id << std::endl;
+		return id;
+	}
+
+	void QtBarriersFreeChatView::setChatWindowHasFocus(bool focus) {
+
+	}
+
+	std::string QtBarriersFreeChatView::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 "";
+	}
+
+	std::string QtBarriersFreeChatView::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) {
+		return "";
+	}
+
+	void QtBarriersFreeChatView::addErrorMessage(const std::string& errorMessage) {
+
+	}
+
+	void QtBarriersFreeChatView::addSystemMessage(const std::string& message) {
+
+	}
+
+	void QtBarriersFreeChatView::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) {
+		replaceMessage(" *" + P2QSTRING(message) + "*", P2QSTRING(id), B2QDATE(time), "font-style:italic ");
+	}
+
+	void QtBarriersFreeChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time, const QString& style) {
+
+	}
+
+	void QtBarriersFreeChatView::addPresenceMessage(const std::string& message) {
+
+	}
+
+	void QtBarriersFreeChatView::scrollToBottom() {
+		if(textEdit_->isReadOnly()) {
+			QKeyEvent endPressed(QEvent::KeyPress, Qt::Key_End, Qt::NoModifier);
+			handleKeyPressEvent(&endPressed);
+		}
+		else {
+			QKeyEvent endPressed(QEvent::KeyPress, Qt::Key_End, Qt::ControlModifier);
+			handleKeyPressEvent(&endPressed);
+		}
+	}
+
+	void QtBarriersFreeChatView::resizeFont(int fontSizeSteps) {
+		qreal currentSize = textEdit_->fontPointSize();
+		qDebug() << "resizeFont: changing current font point size " << currentSize << " with fontSizeSteps " << fontSizeSteps << " to :" << currentSize + fontSizeSteps;
+		textEdit_->setFontPointSize( currentSize + fontSizeSteps );
+	}
+
+	void QtBarriersFreeChatView::handleKeyPressEvent(QKeyEvent* event) {
+		event->ignore();
+	}
+
+	void QtBarriersFreeChatView::setMUCInvitationJoined(QString id) {
+
+	}
+
+	void QtBarriersFreeChatView::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) {
+
+	}
+
+	void QtBarriersFreeChatView::showEmoticons(bool) {
+
+	}
+}
diff --git a/Swift/QtUI/QtBarriersFreeChatView.h b/Swift/QtUI/QtBarriersFreeChatView.h
new file mode 100644
index 0000000..c5b3fdc
--- /dev/null
+++ b/Swift/QtUI/QtBarriersFreeChatView.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include "QtChatView.h"
+#include <QTextEdit>
+
+namespace Swift {
+	class UIEventStream;
+	class QtBarriersFreeChatView : public QtChatView {
+		public:
+			QtBarriersFreeChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons);
+			virtual ~QtBarriersFreeChatView();
+
+			// QtChatView Implementation
+			void replaceLastMessage(const QString& newMessage);
+			void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time);
+			void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg);
+			void setFileTransferProgress(QString id, const int percentageDone);
+			void addToJSEnvironment(const QString&, QObject*);
+			void setAckState(std::string const& id, ChatWindow::AckState);
+			void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state);
+			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 setChatWindowHasFocus(bool focus);
+			std::string 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);
+			std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes);
+			void addErrorMessage(const std::string& errorMessage);
+			void addSystemMessage(const std::string& message);
+			void replaceMessage(const std::string& message, const QString& id, const QDateTime& time, const QString& style) {
+				replaceMessage(P2QSTRING(message), id, time, style);
+			}
+			void replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time, const QString& style);
+			void addPresenceMessage(const std::string& message);
+			void showEmoticons(bool);
+			void setMUCInvitationJoined(QString id);
+			void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct);
+		public slots:
+			// QtChatView Implementation
+			void scrollToBottom();
+			void resizeFont(int fontSizeSteps);
+			void handleKeyPressEvent(QKeyEvent* event);
+
+		private:
+			QTextEdit *textEdit_;
+			int idCounter_;
+			UIEventStream* eventStream_;
+			SettingsProvider* settings_;
+			QMap<QString, QString> emoticons_;
+	};
+}
diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp
index 49e5974..56cd9bc 100644
--- a/Swift/QtUI/QtChatView.cpp
+++ b/Swift/QtUI/QtChatView.cpp
@@ -4,384 +4,22 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-#include "QtChatView.h"
-
-#include <QtDebug>
-#include <QEventLoop>
-#include <QFile>
-#include <QDesktopServices>
-#include <QVBoxLayout>
-#include <QWebFrame>
-#include <QKeyEvent>
-#include <QStackedWidget>
-#include <QTimer>
-#include <QMessageBox>
-#include <QApplication>
-
-#include <Swiften/Base/Log.h>
-
-#include "QtWebView.h"
-#include "QtChatTheme.h"
-#include "QtChatWindow.h"
-#include "QtSwiftUtil.h"
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
 
+#include "QtChatView.h"
 
 namespace Swift {
 
-QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent) : QWidget(parent), fontSizeSteps_(0) {
-	theme_ = theme;
-
-	QVBoxLayout* mainLayout = new QVBoxLayout(this);
-	mainLayout->setSpacing(0);
-	mainLayout->setContentsMargins(0,0,0,0);
-	webView_ = new QtWebView(this);
-	connect(webView_, SIGNAL(linkClicked(const QUrl&)), SLOT(handleLinkClicked(const QUrl&)));
-	connect(webView_, SIGNAL(loadFinished(bool)), SLOT(handleViewLoadFinished(bool)));
-	connect(webView_, SIGNAL(gotFocus()), SIGNAL(gotFocus()));
-	connect(webView_, SIGNAL(clearRequested()), SLOT(handleClearRequested()));
-	connect(webView_, SIGNAL(fontGrowRequested()), SLOT(increaseFontSize()));
-	connect(webView_, SIGNAL(fontShrinkRequested()), SLOT(decreaseFontSize()));
-#ifdef Q_WS_X11
-	/* To give a border on Linux, where it looks bad without */
-	QStackedWidget* stack = new QStackedWidget(this);
-	stack->addWidget(webView_);
-	stack->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
-	stack->setLineWidth(2);
-	mainLayout->addWidget(stack);
-#else
-	mainLayout->addWidget(webView_);
-#endif
-
-#ifdef SWIFT_EXPERIMENTAL_FT
-	setAcceptDrops(true);
-#endif
-
-	webPage_ = new QWebPage(this);
-	webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
-	//webPage_->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
-	webView_->setPage(webPage_);
-	connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard()));
-
-	viewReady_ = false;
-	isAtBottom_ = true;
-	resetView();
-}
-
-void QtChatView::handleClearRequested() {
-	QMessageBox messageBox(this);
-	messageBox.setWindowTitle(tr("Clear log"));
-	messageBox.setText(tr("You are about to clear the contents of your chat log."));
-	messageBox.setInformativeText(tr("Are you sure?"));
-	messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
-	messageBox.setDefaultButton(QMessageBox::Yes);
-	int button = messageBox.exec();
-	if (button == QMessageBox::Yes) {
-		logCleared();
-		resetView();
-	}
-}
-
-void QtChatView::handleKeyPressEvent(QKeyEvent* event) {
-	webView_->keyPressEvent(event);
-}
-
-void QtChatView::addMessage(boost::shared_ptr<ChatSnippet> snippet) {
-	if (viewReady_) {
-		addToDOM(snippet);
-	} else {
-		/* If this asserts, the previous queuing code was necessary and should be reinstated */
-		assert(false);
-	}
-}
-
-QWebElement QtChatView::snippetToDOM(boost::shared_ptr<ChatSnippet> snippet) {
-	QWebElement newElement = newInsertPoint_.clone();
-	newElement.setInnerXml(snippet->getContent());
-	Q_ASSERT(!newElement.isNull());
-	return newElement;
-}
-
-void QtChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) {
-	rememberScrolledToBottom();
-	bool insert = snippet->getAppendToPrevious();
-	QWebElement continuationElement = lastElement_.findFirst("#insert");
-	bool fallback = insert && continuationElement.isNull();
-	boost::shared_ptr<ChatSnippet> newSnippet = (insert && fallback) ? snippet->getContinuationFallbackSnippet() : snippet;
-	QWebElement newElement = snippetToDOM(newSnippet);
-	if (insert && !fallback) {
-		Q_ASSERT(!continuationElement.isNull());
-		continuationElement.replace(newElement);
-	} else {
-		continuationElement.removeFromDocument();
-		newInsertPoint_.prependOutside(newElement);
-	}
-	lastElement_ = newElement;
-	if (fontSizeSteps_ != 0) {
-		double size = 1.0 + 0.2 * fontSizeSteps_;
-		QString sizeString(QString().setNum(size, 'g', 3) + "em");
-		const QWebElementCollection spans = lastElement_.findAll("span.swift_resizable");
-		foreach (QWebElement span, spans) {
-			span.setStyleProperty("font-size", sizeString);
-		}
-	}
-}
-
-void QtChatView::addLastSeenLine() {
-	if (lineSeparator_.isNull()) {
-		lineSeparator_ = newInsertPoint_.clone();
-		lineSeparator_.setInnerXml(QString("<hr/>"));
-		newInsertPoint_.prependOutside(lineSeparator_);
-	}
-	else {
-		QWebElement lineSeparatorC = lineSeparator_.clone();
-		lineSeparatorC.removeFromDocument();
-	}
-	newInsertPoint_.prependOutside(lineSeparator_);
-}
-
-void QtChatView::replaceLastMessage(const QString& newMessage) {
-	assert(viewReady_);
-	rememberScrolledToBottom();
-	assert(!lastElement_.isNull());
-	QWebElement replace = lastElement_.findFirst("span.swift_message");
-	assert(!replace.isNull());
-	QString old = lastElement_.toOuterXml();
-	replace.setInnerXml(ChatSnippet::escape(newMessage));
-}
-
-void QtChatView::replaceLastMessage(const QString& newMessage, const QString& note) {
-	rememberScrolledToBottom();
-	replaceLastMessage(newMessage);
-	QWebElement replace = lastElement_.findFirst("span.swift_time");
-	assert(!replace.isNull());
-	replace.setInnerXml(ChatSnippet::escape(note));
-}
-
-QString QtChatView::getLastSentMessage() {
-	return lastElement_.toPlainText();
-}
-
-void QtChatView::addToJSEnvironment(const QString& name, QObject* obj) {
-	webView_->page()->currentFrame()->addToJavaScriptWindowObject(name, obj);
-}
-
-void QtChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime) {
-	rememberScrolledToBottom();
-	QWebElement message = document_.findFirst("#" + id);
-	if (!message.isNull()) {
-		QWebElement replaceContent = message.findFirst("span.swift_inner_message");
-		assert(!replaceContent.isNull());
-		QString old = replaceContent.toOuterXml();
-		replaceContent.setInnerXml(ChatSnippet::escape(newMessage));
-		QWebElement replaceTime = message.findFirst("span.swift_time");
-		assert(!replaceTime.isNull());
-		old = replaceTime.toOuterXml();
-		replaceTime.setInnerXml(ChatSnippet::escape(tr("%1 edited").arg(ChatSnippet::timeToEscapedString(editTime))));
-	}
-	else {
-		qWarning() << "Trying to replace element with id " << id << " but it's not there.";
-	}
-}
-
-void QtChatView::showEmoticons(bool show) {
-	{
-		const QWebElementCollection spans = document_.findAll("span.swift_emoticon_image");
-		foreach (QWebElement span, spans) {
-			span.setStyleProperty("display", show ? "inline" : "none");
-		}
-	}
-	{
-		const QWebElementCollection spans = document_.findAll("span.swift_emoticon_text");
-		foreach (QWebElement span, spans) {
-			span.setStyleProperty("display", show ? "none" : "inline");
-		}
-	}
-}
-
-void QtChatView::copySelectionToClipboard() {
-	if (!webPage_->selectedText().isEmpty()) {
-		webPage_->triggerAction(QWebPage::Copy);
-	}
-}
-
-void QtChatView::setAckXML(const QString& id, const QString& xml) {
-	QWebElement message = document_.findFirst("#" + id);
-	/* Deliberately not asserting here, so that when we start expiring old messages it won't hit us */
-	if (message.isNull()) return;
-	QWebElement ackElement = message.findFirst("span.swift_ack");
-	assert(!ackElement.isNull());
-	ackElement.setInnerXml(xml);
-}
-
-void QtChatView::setReceiptXML(const QString& id, const QString& xml) {
-	QWebElement message = document_.findFirst("#" + id);
-	if (message.isNull()) return;
-	QWebElement receiptElement = message.findFirst("span.swift_receipt");
-	assert(!receiptElement.isNull());
-	receiptElement.setInnerXml(xml);
-}
-
-void QtChatView::displayReceiptInfo(const QString& id, bool showIt) {
-	QWebElement message = document_.findFirst("#" + id);
-	if (message.isNull()) return;
-	QWebElement receiptElement = message.findFirst("span.swift_receipt");
-	assert(!receiptElement.isNull());
-	receiptElement.setStyleProperty("display", showIt ? "inline" : "none");
-}
+QtChatView::QtChatView(QWidget* parent) : QWidget(parent) {
 
-void QtChatView::rememberScrolledToBottom() {
-	isAtBottom_ = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical);
 }
 
-void QtChatView::scrollToBottom() {
-	isAtBottom_ = true;
-	webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical));
-	webView_->update(); /* Work around redraw bug in some versions of Qt. */
-}
-
-void QtChatView::handleFrameSizeChanged() {
-	if (isAtBottom_) {
-		scrollToBottom();
-	}
-}
-
-void QtChatView::handleLinkClicked(const QUrl& url) {
-	QDesktopServices::openUrl(url);
-}
-
-void QtChatView::handleViewLoadFinished(bool ok) {
-	Q_ASSERT(ok);
-	viewReady_ = true;
-}
-
-void QtChatView::increaseFontSize(int numSteps) {
-	//qDebug() << "Increasing";
-	fontSizeSteps_ += numSteps;
-	emit fontResized(fontSizeSteps_);
-}
+QtChatView::~QtChatView() {
 
-void QtChatView::decreaseFontSize() {
-	fontSizeSteps_--;
-	if (fontSizeSteps_ < 0) {
-		fontSizeSteps_ = 0;
-	}
-	emit fontResized(fontSizeSteps_);
 }
 
-void QtChatView::resizeFont(int fontSizeSteps) {
-	fontSizeSteps_ = fontSizeSteps;
-	double size = 1.0 + 0.2 * fontSizeSteps_;
-	QString sizeString(QString().setNum(size, 'g', 3) + "em");
-	//qDebug() << "Setting to " << sizeString;
-	const QWebElementCollection spans = document_.findAll("span.swift_resizable");
-	foreach (QWebElement span, spans) {
-		span.setStyleProperty("font-size", sizeString);
-	}
-	webView_->setFontSizeIsMinimal(size == 1.0);
-}
-
-void QtChatView::resetView() {
-	lastElement_ = QWebElement();
-	QString pageHTML = theme_->getTemplate();
-	pageHTML.replace("==bodyBackground==", "background-color:#e3e3e3");
-	pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getBase());
-	if (pageHTML.count("%@") > 3) {
-		pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getMainCSS());
-	}
-	pageHTML.replace(pageHTML.indexOf("%@"), 2, "Variants/Blue on Green.css");
-	pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*headerSnippet.getContent()*/);
-	pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*footerSnippet.getContent()*/);
-	QEventLoop syncLoop;
-	connect(webView_, SIGNAL(loadFinished(bool)), &syncLoop, SLOT(quit()));
-	webPage_->mainFrame()->setHtml(pageHTML);
-	while (!viewReady_) {
-		QTimer t;
-		t.setSingleShot(true);
-		connect(&t, SIGNAL(timeout()), &syncLoop, SLOT(quit()));
-		t.start(50);
-		syncLoop.exec();
-	}
-	document_ = webPage_->mainFrame()->documentElement();
-	QWebElement chatElement = document_.findFirst("#Chat");
-	newInsertPoint_ = chatElement.clone();
-	newInsertPoint_.setOuterXml("<div id='swift_insert'/>");
-	chatElement.appendInside(newInsertPoint_);
-	Q_ASSERT(!newInsertPoint_.isNull());
-
-	connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection);
-}
-
-QWebElement findDivElementWithID(QWebElement document, QString id) {
-	QWebElementCollection divs = document.findAll("div");
-	foreach(QWebElement div, divs) {
-		if (div.attribute("id") == id) {
-			return div;
-		}
-	}
-	return QWebElement();
-}
-
-void QtChatView::setFileTransferProgress(QString id, const int percentageDone) {
-	QWebElement ftElement = findDivElementWithID(document_, id);
-	if (ftElement.isNull()) {
-		SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!" << std::endl;
-		return;
-	}
-	QWebElement progressBar = ftElement.findFirst("div.progressbar");
-	progressBar.setStyleProperty("width", QString::number(percentageDone) + "%");
-
-	QWebElement progressBarValue = ftElement.findFirst("div.progressbar-value");
-	progressBarValue.setInnerXml(QString::number(percentageDone) + " %");
-}
-
-void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& /* msg */) {
-	QWebElement ftElement = findDivElementWithID(document_, id);
-	if (ftElement.isNull()) {
-		SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id) << std::endl;
-		return;
-	}
-
-	QString newInnerHTML = "";
-	if (state == ChatWindow::WaitingForAccept) {
-		newInnerHTML =	tr("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 =	tr("Negotiating...") + "<br/>" +
-			QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id);
-	}
-	else if (state == ChatWindow::Transferring) {
-		// progress bar + Cancel Button
-		newInnerHTML =	"<div style=\"position: relative; width: 90%; height: 20px; border: 2px solid grey; -webkit-border-radius: 10px;\">"
-							"<div class=\"progressbar\" style=\"width: 0%; height: 100%; background: #AAA; -webkit-border-radius: 6px;\">"
-								"<div class=\"progressbar-value\" style=\"position: absolute; top: 0px; left: 0px; width: 100%; text-align: center; padding-top: 2px;\">"
-									"0%"
-								"</div>"
-							"</div>"
-						"</div>" +
-						QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id);
-	}
-	else if (state == ChatWindow::Canceled) {
-		newInnerHTML = tr("Transfer has been canceled!");
-	}
-	else if (state == ChatWindow::Finished) {
-		// text "Successful transfer"
-		newInnerHTML = tr("Transfer completed successfully.");
-	}
-	else if (state == ChatWindow::FTFailed) {
-		newInnerHTML = tr("Transfer failed.");
-	}
-
-	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"));
-	}
-}
-
-}
+} // namespace Swift
diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h
index fdbdd5a..2454672 100644
--- a/Swift/QtUI/QtChatView.h
+++ b/Swift/QtUI/QtChatView.h
@@ -1,86 +1,57 @@
 /*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
  */
 
 #ifndef SWIFT_QtChatView_H
 #define SWIFT_QtChatView_H
 
-#include <QString>
+#include "QtChatWindow.h"
 #include <QWidget>
-#include <QList>
-#include <QWebElement>
-
-#include <boost/shared_ptr.hpp>
-
-#include "ChatSnippet.h"
-
-#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
-
-class QWebPage;
-class QUrl;
+#include <QKeyEvent>
 
 namespace Swift {
-	class QtWebView;
-	class QtChatTheme;
 	class QtChatView : public QWidget {
 			Q_OBJECT
-		public:
-			QtChatView(QtChatTheme* theme, QWidget* parent);
-			void addMessage(boost::shared_ptr<ChatSnippet> snippet);
-			void addLastSeenLine();
-			void replaceLastMessage(const QString& newMessage);
-			void replaceLastMessage(const QString& newMessage, const QString& note);
-			void replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time);
-			void rememberScrolledToBottom();
-			void setAckXML(const QString& id, const QString& xml);
-			void setReceiptXML(const QString& id, const QString& xml);
-			void displayReceiptInfo(const QString& id, bool showIt);
-
-			QString getLastSentMessage();
-			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);
-			void showEmoticons(bool show);
-		signals:
-			void gotFocus();
-			void fontResized(int);
-			void logCleared();
-
-		public slots:
-			void copySelectionToClipboard();
-			void scrollToBottom();
-			void handleLinkClicked(const QUrl&);
-			void handleKeyPressEvent(QKeyEvent* event);
-			void resetView();
-			void increaseFontSize(int numSteps = 1);
-			void decreaseFontSize();
-			void resizeFont(int fontSizeSteps);
-
-		private slots:
-			void handleViewLoadFinished(bool);
-			void handleFrameSizeChanged();
-			void handleClearRequested();
-
-		private:
-			void headerEncode();
-			void messageEncode();
-			void addToDOM(boost::shared_ptr<ChatSnippet> snippet);
-			QWebElement snippetToDOM(boost::shared_ptr<ChatSnippet> snippet);
-
-			bool viewReady_;
-			bool isAtBottom_;
-			QtWebView* webView_;
-			QWebPage* webPage_;
-			int fontSizeSteps_;
-			QtChatTheme* theme_;
-			QWebElement newInsertPoint_;
-			QWebElement lineSeparator_;
-			QWebElement lastElement_;
-			QWebElement document_;
+	public:
+		QtChatView(QWidget* parent);
+		virtual ~QtChatView();
+
+
+		virtual void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg) = 0;
+		virtual void setFileTransferProgress(QString id, const int percentageDone) = 0;
+		virtual void addToJSEnvironment(const QString&, QObject*) = 0;
+		virtual void setAckState(std::string const& id, ChatWindow::AckState) = 0;
+		virtual void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) = 0;
+		virtual	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) = 0;
+		virtual void setChatWindowHasFocus(bool focus) = 0;
+		virtual std::string 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) = 0;
+		virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0;
+		virtual void addErrorMessage(const std::string& errorMessage) = 0;
+		virtual void addSystemMessage(const std::string& message) = 0;
+		virtual void replaceMessage(const std::string& newMessage, const QString& id, const QDateTime& time, const QString& style) = 0;
+		virtual void replaceLastMessage(const QString& newMessage) = 0;
+		virtual void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0;
+		virtual void addPresenceMessage(const std::string& message) = 0;
+		virtual void setMUCInvitationJoined(QString id) = 0;
+		virtual void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) = 0;
+		virtual void showEmoticons(bool show) = 0;
+
+	signals:
+		void logCleared();
+		void onFileTransferAccept(std::string, std::string);
+		void onFileTransferStart(std::string, std::string);
+		void onFileTransferCancel(std::string);
+		void gotFocus();
+		void fontResized(int);
+
+	public slots:
+		virtual void scrollToBottom() = 0;
+		virtual void resizeFont(int fontSizeSteps) = 0;
+		virtual void handleKeyPressEvent(QKeyEvent* event) = 0;
 	};
 }
 
-#endif
+#endif // #ifndef SWIFT_QtWebKitChatView_H
+
diff --git a/Swift/QtUI/QtChatViewFactory.cpp b/Swift/QtUI/QtChatViewFactory.cpp
new file mode 100644
index 0000000..5c55627
--- /dev/null
+++ b/Swift/QtUI/QtChatViewFactory.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+#include "QtChatViewFactory.h"
+#include "QtBarriersFreeChatView.h"
+#include "QtWebKitChatView.h"
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+
+#include <QDebug>
+
+namespace Swift {
+QtChatView* QtChatViewFactory::createChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) {
+	bool barriersFreeChat = settings->getSetting(QtUISettingConstants::BARRIERS_FREE_CHAT_VIEW);
+
+	qDebug() << "[QtChatViewFactory::createChatView] - barriersFreeChat: " << barriersFreeChat;
+
+	if(barriersFreeChat) {
+		qDebug() << "[QtChatViewFactory::createChatView] - using barriers free chat view ...";
+		return new QtBarriersFreeChatView(theme, parent, eventStream, settings, emoticons);
+	}
+	else {
+		qDebug() << "[QtChatViewFactory::createChatView] - using normal chat view ...";
+		return new QtWebKitChatView(theme, parent, eventStream, settings, emoticons);
+	}
+}
+
+}
+
diff --git a/Swift/QtUI/QtChatViewFactory.h b/Swift/QtUI/QtChatViewFactory.h
new file mode 100644
index 0000000..e229837
--- /dev/null
+++ b/Swift/QtUI/QtChatViewFactory.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include "Swift/QtUI/QtChatView.h"
+#include "QtSettingsProvider.h"
+#include "QtUISettingConstants.h"
+#include "Swift/Controllers/Settings/SettingsProvider.h"
+
+#include <QObject>
+
+class QWidget;
+
+namespace Swift {
+	class QtChatTheme;
+	class QtChatViewFactory {
+		public:
+			static QtChatView* createChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons);
+	};
+}
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index f42469b..d412a50 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -4,30 +4,30 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
 #include "QtChatWindow.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 "QtChatViewFactory.h"
 #include "QtTextEdit.h"
 #include "QtSettingsProvider.h"
 #include "QtScaledAvatarCache.h"
 #include "QtInviteToChatWindow.h"
-#include <Swift/QtUI/QtUISettingConstants.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 <boost/cstdint.hpp>
-#include <boost/format.hpp>
 #include <boost/lexical_cast.hpp>
 
 #include <QLabel>
@@ -46,7 +46,6 @@
 #include <QTime>
 #include <QUrl>
 #include <QPushButton>
-#include <QFileDialog>
 #include <QMenu>
 #include <QTextDocument>
 #include <Swift/Controllers/Settings/SettingsProvider.h>
@@ -54,25 +53,16 @@
 
 namespace Swift {
 
-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, 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, QMap<QString, QString> emoticons) : QtTabbable(), contact_(contact)  {
 	settings_ = settings;
 	unreadCount_ = 0;
-	idCounter_ = 0;
 	inputEnabled_ = true;
 	completer_ = NULL;
 	affiliationEditor_ = NULL;
-	theme_ = theme;
 	isCorrection_ = false;
 	labelModel_ = NULL;
 	correctionEnabled_ = Maybe;
-	showEmoticons_ = true;
 	updateTitleWithUnreadCount();
 
 #ifdef SWIFT_EXPERIMENTAL_FT
@@ -119,10 +109,10 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
 	logRosterSplitter_ = new QSplitter(this);
 	logRosterSplitter_->setAutoFillBackground(true);
 	layout->addWidget(logRosterSplitter_);
-	messageLog_ = new QtChatView(theme, this);
+	messageLog_ = QtChatViewFactory::createChatView(theme, this, eventStream, settings_, emoticons);
 	logRosterSplitter_->addWidget(messageLog_);
 
-	treeWidget_ = new QtOccupantListWidget(eventStream_, settings_, this);
+	treeWidget_ = new QtOccupantListWidget(eventStream, settings_, this);
 	treeWidget_->hide();
 	logRosterSplitter_->addWidget(treeWidget_);
 	logRosterSplitter_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@@ -173,43 +163,38 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
 	resize(400,300);
 	connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int)));
 	connect(messageLog_, SIGNAL(logCleared()), this, SLOT(handleLogCleared()));
+	connect(messageLog_, SIGNAL(onFileTransferAccept(std::string, std::string)), this, SLOT(handleFileTransferAccept(std::string, std::string)));
+	connect(messageLog_, SIGNAL(onFileTransferStart(std::string, std::string)), this, SLOT(handleFileTransferStart(std::string, std::string)));
+	connect(messageLog_, SIGNAL(onFileTransferCancel(std::string)), this, SLOT(handleFileTransferCancel(std::string)));
 
 	treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1));
 	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));
-	showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS);
-
 }
 
 QtChatWindow::~QtChatWindow() {
-	delete jsBridge;
 	if (mucConfigurationWindow_) {
 		delete mucConfigurationWindow_.data();
 	}
 }
 
-void QtChatWindow::handleSettingChanged(const std::string& setting) {
-	if (setting == QtUISettingConstants::SHOW_EMOTICONS.getKey()) {
-		showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS);
-		messageLog_->showEmoticons(showEmoticons_);
-	}
-}
-
 void QtChatWindow::handleLogCleared() {
 	onLogCleared();
 }
 
-void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) {
-	onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(item));
+void QtChatWindow::handleFileTransferAccept(std::string id, std::string description) {
+	onFileTransferAccept(id, description);
+}
+
+void QtChatWindow::handleFileTransferStart(std::string id, std::string description) {
+	onFileTransferStart(id, description);
+}
+
+void QtChatWindow::handleFileTransferCancel(std::string id) {
+	onFileTransferCancel(id);
 }
 
-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::handleOccupantSelectionChanged(RosterItem* item) {
+	onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(item));
 }
 
 void QtChatWindow::handleFontResized(int fontSizeSteps) {
@@ -415,12 +400,12 @@ void QtChatWindow::convertToMUC() {
 
 void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) {
 	if (isWidgetSelected()) {
-		lastLineTracker_.setHasFocus(true);
+		messageLog_->setChatWindowHasFocus(true);
 		input_->setFocus();
 		onAllMessagesRead();
 	}
 	else {
-		lastLineTracker_.setHasFocus(false);
+		messageLog_->setChatWindowHasFocus(false);
 	}
 }
 
@@ -479,57 +464,14 @@ 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(linkimoticonify(P2QSTRING(message)), senderName, senderIsSelf, label, avatarPath, "", time);
-}
-
-QString QtChatWindow::linkimoticonify(const QString& message) const {
-	QString messageHTML(message);
-	messageHTML = Qt::escape(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 = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML)));
-	}
-	messageHTML.replace("\n","<br/>");
-	return messageHTML;
+	return addMessage(message, senderName, senderIsSelf, label, avatarPath, "", 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) {
+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; \" 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())));
-	}
-	QString messageHTML(message);
-	QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
-	QString styleSpanEnd = style == "" ? "" : "</span>";
-	htmlString += "<span class='swift_inner_message'>" + styleSpanStart + messageHTML + styleSpanEnd + "</span>" ;
-
-	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;
+	return messageLog_->addMessage(message, senderName, senderIsSelf, label, avatarPath, style, time);
 }
 
 void QtChatWindow::flash() {
@@ -537,32 +479,11 @@ 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);
+	messageLog_->setAckState(id, state);
 }
 
 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);
+	messageLog_->setMessageReceiptState(id, state);
 }
 
 int QtChatWindow::getCount() {
@@ -570,75 +491,11 @@ int QtChatWindow::getCount() {
 }
 
 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 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;
+	return messageLog_->addAction(message, senderName, senderIsSelf, label, avatarPath, time);
 }
 
 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);
+	return messageLog_->addFileTransfer(senderName, senderIsSelf, filename, sizeInBytes);
 }
 
 void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) {
@@ -649,116 +506,45 @@ void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState
 	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;
+	messageLog_->addErrorMessage(errorMessage);
 }
 
-void QtChatWindow::addSystemMessage(const std::string& message) {
+void QtChatWindow::addSystemMessage(const std::string& systemMessage) {
 	if (isWidgetSelected()) {
 		onAllMessagesRead();
 	}
-
-	QString messageHTML(P2QSTRING(message));
-	messageHTML = linkimoticonify(messageHTML);
-	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
-
-	previousMessageKind_ = PreviousMessageWasSystem;
+	messageLog_->addSystemMessage(systemMessage);
 }
 
 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 ");
+	messageLog_->replaceWithAction(message, id, time);
 }
 
 void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) {
-	replaceMessage(linkimoticonify(P2QSTRING(message)), id, time, "");
+	replaceMessage(message, id, time, "");
 }
 
-void QtChatWindow::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style) {
-	if (!id.empty()) {
-		if (isWidgetSelected()) {
-			onAllMessagesRead();
-		}
-
-		QString messageHTML(message);
-
-		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::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style) {
+	if (isWidgetSelected()) {
+		onAllMessagesRead();
 	}
+
+	messageLog_->replaceMessage(message, P2QSTRING(id), B2QDATE(time), style);
 }
 
 void QtChatWindow::addPresenceMessage(const std::string& message) {
 	if (isWidgetSelected()) {
 		onAllMessagesRead();
 	}
-
-	QString messageHTML(P2QSTRING(message));
-	messageHTML = linkimoticonify(messageHTML);
-	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
-
-	previousMessageKind_ = PreviousMessageWasPresence;
+	messageLog_->addPresenceMessage(message);
 }
 
-
 void QtChatWindow::returnPressed() {
 	if (!inputEnabled_) {
 		return;
@@ -829,7 +615,7 @@ void QtChatWindow::dropEvent(QDropEvent *event) {
 }
 
 void QtChatWindow::replaceLastMessage(const std::string& message) {
-	messageLog_->replaceLastMessage(linkimoticonify(P2QSTRING(message)));
+	messageLog_->replaceLastMessage(P2QSTRING(message));
 }
 
 void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {
@@ -927,33 +713,7 @@ 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/>";
-	if (!reason.empty()) {
-		htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "<br/>";
-	}
-	if (!direct) {
-		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)));
-	previousMessageWasSelf_ = false;
-	previousSenderName_ = P2QSTRING(senderName);
-	previousMessageKind_ = PreviousMessageWasMUCInvite;
+	messageLog_->addMUCInvitation(senderName, jid, reason, password, direct);
 }
 
 
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index a703818..6871a3d 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -4,6 +4,12 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
 #pragma once
 
 #include <Swift/Controllers/UIInterfaces/ChatWindow.h>
@@ -13,8 +19,6 @@
 
 #include <QtTabbable.h>
 
-#include <SwifTools/LastLineTracker.h>
-
 #include <map>
 #include <QPointer>
 #include <QTextCursor>
@@ -34,7 +38,6 @@ namespace Swift {
 	class TreeWidget;
 	class QtTextEdit;
 	class UIEventStream;
-	class QtChatWindowJSBridge;
 	class SettingsProvider;
 
 	class LabelModel : public QAbstractListModel {
@@ -73,13 +76,6 @@ namespace Swift {
 		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, QMap<QString, QString> emoticons);
 			~QtChatWindow();
 			std::string 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);
@@ -127,8 +123,6 @@ namespace Swift {
 
 			InviteToChatWindow* createInviteToChatWindow();
 
-			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);
@@ -162,36 +156,24 @@ namespace Swift {
 			void handleSplitterMoved(int pos, int index);
 			void handleAlertButtonClicked();
 			void handleActionButtonClicked();
+			void handleFileTransferAccept(std::string id, std::string description);
+			void handleFileTransferStart(std::string id, std::string description);
+			void handleFileTransferCancel(std::string id);
 
-			void handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3);
 			void handleAffiliationEditorAccepted();
 			void handleCurrentLabelChanged(int);
 
 		private:
-			enum PreviousMessageKind {
-				PreviosuMessageWasNone,
-				PreviousMessageWasMessage,
-				PreviousMessageWasSystem,
-				PreviousMessageWasPresence,
-				PreviousMessageWasFileTransfer,
-				PreviousMessageWasMUCInvite
-			};
-
-		private:
 			void updateTitleWithUnreadCount();
 			void tabComplete();
 			void beginCorrection();
 			void cancelCorrection();
-			void handleSettingChanged(const std::string& setting);
-			std::string 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);
-			void replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style);
+			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 replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style);
 			void handleOccupantSelectionChanged(RosterItem* item);
-			bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const;
-			QString linkimoticonify(const QString& message) const;
 
 			int unreadCount_;
 			bool contactIsTyping_;
-			LastLineTracker lastLineTracker_;
 			QString contact_;
 			QString lastSentMessage_;
 			QTextCursor tabCompleteCursor_;
@@ -209,25 +191,16 @@ namespace Swift {
 			QLineEdit* subject_;
 			QPushButton* actionButton_;
 			bool isCorrection_;
-			bool previousMessageWasSelf_;
-			PreviousMessageKind previousMessageKind_;
-			QString previousSenderName_;
 			bool inputClearing_;
 			bool tabCompletion_;
-			UIEventStream* eventStream_;
 			bool inputEnabled_;
 			QSplitter *logRosterSplitter_;
 			Tristate correctionEnabled_;
 			QString alertStyleSheet_;
-			std::map<QString, QString> descriptions;
-			QtChatWindowJSBridge* jsBridge;
 			QPointer<QtMUCConfigurationWindow> mucConfigurationWindow_;
 			QPointer<QtAffiliationEditor> affiliationEditor_;
-			int idCounter_;
 			SettingsProvider* settings_;
 			std::vector<ChatWindow::RoomAction> availableRoomActions_;
-			QMap<QString, QString> emoticons_;
-			bool showEmoticons_;
 			QPalette defaultLabelsPalette_;
 			LabelModel* labelModel_;
 	};
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index 13b2175..e5cca01 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -22,6 +22,7 @@
 #include <QtSwiftUtil.h>
 #include <QtUIFactory.h>
 #include <QtChatWindowFactory.h>
+#include <QtChatViewFactory.h>
 #include <Swiften/Base/Log.h>
 #include <Swift/Controllers/Storages/CertificateFileStorageFactory.h>
 #include <Swift/Controllers/Storages/FileStoragesFactory.h>
diff --git a/Swift/QtUI/QtUISettingConstants.cpp b/Swift/QtUI/QtUISettingConstants.cpp
index 81022ec..816307a 100644
--- a/Swift/QtUI/QtUISettingConstants.cpp
+++ b/Swift/QtUI/QtUISettingConstants.cpp
@@ -4,6 +4,12 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
 #include <Swift/QtUI/QtUISettingConstants.h>
 
 namespace Swift {
@@ -13,5 +19,6 @@ const SettingsProvider::Setting<std::string> QtUISettingConstants::CLICKTHROUGH_
 const SettingsProvider::Setting<int> QtUISettingConstants::CURRENT_ROSTER_TAB("currentRosterTab", 0);
 const SettingsProvider::Setting<bool> QtUISettingConstants::SHOW_NICK_IN_ROSTER_HEADER("showNickInRosterHeader", true);
 const SettingsProvider::Setting<int> QtUISettingConstants::CHATWINDOW_FONT_SIZE("chatWindowFontSize", 0);
+const SettingsProvider::Setting<bool> QtUISettingConstants::BARRIERS_FREE_CHAT_VIEW("barriersFreeChatView", false);
 const SettingsProvider::Setting<bool> QtUISettingConstants::SHOW_EMOTICONS("showEmoticons", true);
 }
diff --git a/Swift/QtUI/QtUISettingConstants.h b/Swift/QtUI/QtUISettingConstants.h
index 2740abb..048dc46 100644
--- a/Swift/QtUI/QtUISettingConstants.h
+++ b/Swift/QtUI/QtUISettingConstants.h
@@ -16,6 +16,7 @@ namespace Swift {
 			static const SettingsProvider::Setting<int> CURRENT_ROSTER_TAB;
 			static const SettingsProvider::Setting<bool> SHOW_NICK_IN_ROSTER_HEADER;
 			static const SettingsProvider::Setting<int> CHATWINDOW_FONT_SIZE;
+			static const SettingsProvider::Setting<bool> BARRIERS_FREE_CHAT_VIEW;
 			static const SettingsProvider::Setting<bool> SHOW_EMOTICONS;
 	};
 }
diff --git a/Swift/QtUI/QtWebKitChatView.cpp b/Swift/QtUI/QtWebKitChatView.cpp
new file mode 100644
index 0000000..5e4eb33
--- /dev/null
+++ b/Swift/QtUI/QtWebKitChatView.cpp
@@ -0,0 +1,713 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+
+#include <QtDebug>
+#include <QEventLoop>
+#include <QFile>
+#include <QDesktopServices>
+#include <QVBoxLayout>
+#include <QWebFrame>
+#include <QKeyEvent>
+#include <QStackedWidget>
+#include <QTimer>
+#include <QMessageBox>
+#include <QApplication>
+#include <QTextDocument>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/StringCodecs/Base64.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
+#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
+#include <Swift/QtUI/QtUISettingConstants.h>
+
+#include "QtWebView.h"
+#include "QtChatTheme.h"
+#include "QtSwiftUtil.h"
+#include "QtScaledAvatarCache.h"
+#include "SwifTools/Linkify.h"
+#include "SystemMessageSnippet.h"
+#include "QtWebKitChatView.h"
+#include "QtChatWindowJSBridge.h"
+
+namespace Swift {
+
+const QString QtWebKitChatView::ButtonFileTransferCancel = QString("filetransfer-cancel");
+const QString QtWebKitChatView::ButtonFileTransferSetDescription = QString("filetransfer-setdescription");
+const QString QtWebKitChatView::ButtonFileTransferSendRequest = QString("filetransfer-sendrequest");
+const QString QtWebKitChatView::ButtonFileTransferAcceptRequest = QString("filetransfer-acceptrequest");
+const QString QtWebKitChatView::ButtonMUCInvite = QString("mucinvite");
+
+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))));
+}
+
+QtWebKitChatView::QtWebKitChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtChatView(parent), fontSizeSteps_(0), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), settings_(settings), emoticons_(emoticons) {
+	theme_ = theme;
+	showEmoticons_ = true;
+
+	QVBoxLayout* mainLayout = new QVBoxLayout(this);
+	mainLayout->setSpacing(0);
+	mainLayout->setContentsMargins(0,0,0,0);
+	webView_ = new QtWebView(this);
+	connect(webView_, SIGNAL(linkClicked(const QUrl&)), SLOT(handleLinkClicked(const QUrl&)));
+	connect(webView_, SIGNAL(loadFinished(bool)), SLOT(handleViewLoadFinished(bool)));
+	connect(webView_, SIGNAL(gotFocus()), SIGNAL(gotFocus()));
+	connect(webView_, SIGNAL(clearRequested()), SLOT(handleClearRequested()));
+	connect(webView_, SIGNAL(fontGrowRequested()), SLOT(increaseFontSize()));
+	connect(webView_, SIGNAL(fontShrinkRequested()), SLOT(decreaseFontSize()));
+#ifdef Q_WS_X11
+	/* To give a border on Linux, where it looks bad without */
+	QStackedWidget* stack = new QStackedWidget(this);
+	stack->addWidget(webView_);
+	stack->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+	stack->setLineWidth(2);
+	mainLayout->addWidget(stack);
+#else
+	mainLayout->addWidget(webView_);
+#endif
+
+#ifdef SWIFT_EXPERIMENTAL_FT
+	setAcceptDrops(true);
+#endif
+
+	webPage_ = new QWebPage(this);
+	webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
+	//webPage_->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
+	webView_->setPage(webPage_);
+	connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard()));
+
+	viewReady_ = false;
+	isAtBottom_ = true;
+	resetView();
+
+	idCounter_ = 0;
+
+	jsBridge_ = new QtChatWindowJSBridge();
+	addToJSEnvironment("chatwindow", jsBridge_);
+	connect(jsBridge_, SIGNAL(buttonClicked(QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString)));
+
+	settings_->onSettingChanged.connect(boost::bind(&QtWebKitChatView::handleSettingChanged, this, _1));
+	showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS);
+}
+
+QtWebKitChatView::~QtWebKitChatView() {
+	delete jsBridge_;
+}
+
+void QtWebKitChatView::handleSettingChanged(const std::string& setting) {
+	if (setting == QtUISettingConstants::SHOW_EMOTICONS.getKey()) {
+		showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS);
+		showEmoticons(showEmoticons_);
+	}
+}
+
+void QtWebKitChatView::handleClearRequested() {
+	QMessageBox messageBox(this);
+	messageBox.setWindowTitle(tr("Clear log"));
+	messageBox.setText(tr("You are about to clear the contents of your chat log."));
+	messageBox.setInformativeText(tr("Are you sure?"));
+	messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+	messageBox.setDefaultButton(QMessageBox::Yes);
+	int button = messageBox.exec();
+	if (button == QMessageBox::Yes) {
+		logCleared();
+		resetView();
+	}
+}
+
+void QtWebKitChatView::handleKeyPressEvent(QKeyEvent* event) {
+	webView_->keyPressEvent(event);
+}
+
+void QtWebKitChatView::addMessage(boost::shared_ptr<ChatSnippet> snippet) {
+	if (viewReady_) {
+		addToDOM(snippet);
+	} else {
+		/* If this asserts, the previous queuing code was necessary and should be reinstated */
+		assert(false);
+	}
+}
+
+QWebElement QtWebKitChatView::snippetToDOM(boost::shared_ptr<ChatSnippet> snippet) {
+	QWebElement newElement = newInsertPoint_.clone();
+	newElement.setInnerXml(snippet->getContent());
+	Q_ASSERT(!newElement.isNull());
+	return newElement;
+}
+
+void QtWebKitChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) {
+	rememberScrolledToBottom();
+	bool insert = snippet->getAppendToPrevious();
+	QWebElement continuationElement = lastElement_.findFirst("#insert");
+	bool fallback = insert && continuationElement.isNull();
+	boost::shared_ptr<ChatSnippet> newSnippet = (insert && fallback) ? snippet->getContinuationFallbackSnippet() : snippet;
+	QWebElement newElement = snippetToDOM(newSnippet);
+	if (insert && !fallback) {
+		Q_ASSERT(!continuationElement.isNull());
+		continuationElement.replace(newElement);
+	} else {
+		continuationElement.removeFromDocument();
+		newInsertPoint_.prependOutside(newElement);
+	}
+	lastElement_ = newElement;
+	if (fontSizeSteps_ != 0) {
+		double size = 1.0 + 0.2 * fontSizeSteps_;
+		QString sizeString(QString().setNum(size, 'g', 3) + "em");
+		const QWebElementCollection spans = lastElement_.findAll("span.swift_resizable");
+		foreach (QWebElement span, spans) {
+			span.setStyleProperty("font-size", sizeString);
+		}
+	}
+}
+
+void QtWebKitChatView::addLastSeenLine() {
+	if (lineSeparator_.isNull()) {
+		lineSeparator_ = newInsertPoint_.clone();
+		lineSeparator_.setInnerXml(QString("<hr/>"));
+		newInsertPoint_.prependOutside(lineSeparator_);
+	}
+	else {
+		QWebElement lineSeparatorC = lineSeparator_.clone();
+		lineSeparatorC.removeFromDocument();
+	}
+	newInsertPoint_.prependOutside(lineSeparator_);
+}
+
+void QtWebKitChatView::replaceLastMessage(const QString& message) {
+	QString newMessage = linkimoticonify(message);
+	assert(viewReady_);
+	rememberScrolledToBottom();
+	assert(!lastElement_.isNull());
+	QWebElement replace = lastElement_.findFirst("span.swift_message");
+	assert(!replace.isNull());
+	QString old = lastElement_.toOuterXml();
+	replace.setInnerXml(ChatSnippet::escape(newMessage));
+}
+
+void QtWebKitChatView::replaceLastMessage(const QString& newMessage, const QString& note) {
+	rememberScrolledToBottom();
+	replaceLastMessage(newMessage);
+	QWebElement replace = lastElement_.findFirst("span.swift_time");
+	assert(!replace.isNull());
+	replace.setInnerXml(ChatSnippet::escape(note));
+}
+
+QString QtWebKitChatView::getLastSentMessage() {
+	return lastElement_.toPlainText();
+}
+
+void QtWebKitChatView::addToJSEnvironment(const QString& name, QObject* obj) {
+	webView_->page()->currentFrame()->addToJavaScriptWindowObject(name, obj);
+}
+
+void QtWebKitChatView::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) {
+	replaceMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", P2QSTRING(id), B2QDATE(time), "font-style:italic ");
+}
+
+void QtWebKitChatView::replaceMessage(const std::string& newMessage, const QString& id, const QDateTime& editTime, const QString& style) {
+	replaceMessage(linkimoticonify(P2QSTRING(newMessage)), id, editTime, style);
+}
+
+void QtWebKitChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime, const QString& style) {
+	if (id.isEmpty()) {
+		qWarning() << "Trying to replace a message with no id";
+		return;
+	}
+
+	QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
+	QString styleSpanEnd = style == "" ? "" : "</span>";
+	QString messageWithStyle = styleSpanStart + newMessage + styleSpanEnd;
+
+	rememberScrolledToBottom();
+	QWebElement message = document_.findFirst("#" + id);
+	if (!message.isNull()) {
+		QWebElement replaceContent = message.findFirst("span.swift_message");
+		assert(!replaceContent.isNull());
+		QString old = replaceContent.toOuterXml();
+		replaceContent.setInnerXml(ChatSnippet::escape(messageWithStyle));
+		QWebElement replaceTime = message.findFirst("span.swift_time");
+		assert(!replaceTime.isNull());
+		old = replaceTime.toOuterXml();
+		replaceTime.setInnerXml(ChatSnippet::escape(tr("%1 edited").arg(ChatSnippet::timeToEscapedString(editTime))));
+	}
+	else {
+		qWarning() << "Trying to replace element with id " << id << " but it's not there.";
+	}
+}
+
+void QtWebKitChatView::showEmoticons(bool show) {
+	{
+		const QWebElementCollection spans = document_.findAll("span.swift_emoticon_image");
+		foreach (QWebElement span, spans) {
+			span.setStyleProperty("display", show ? "inline" : "none");
+		}
+	}
+	{
+		const QWebElementCollection spans = document_.findAll("span.swift_emoticon_text");
+		foreach (QWebElement span, spans) {
+			span.setStyleProperty("display", show ? "none" : "inline");
+		}
+	}
+}
+
+void QtWebKitChatView::copySelectionToClipboard() {
+	if (!webPage_->selectedText().isEmpty()) {
+		webPage_->triggerAction(QWebPage::Copy);
+	}
+}
+
+void QtWebKitChatView::setAckXML(const QString& id, const QString& xml) {
+	QWebElement message = document_.findFirst("#" + id);
+	/* Deliberately not asserting here, so that when we start expiring old messages it won't hit us */
+	if (message.isNull()) return;
+	QWebElement ackElement = message.findFirst("span.swift_ack");
+	assert(!ackElement.isNull());
+	ackElement.setInnerXml(xml);
+}
+
+void QtWebKitChatView::setReceiptXML(const QString& id, const QString& xml) {
+	QWebElement message = document_.findFirst("#" + id);
+	if (message.isNull()) return;
+	QWebElement receiptElement = message.findFirst("span.swift_receipt");
+	assert(!receiptElement.isNull());
+	receiptElement.setInnerXml(xml);
+}
+
+void QtWebKitChatView::displayReceiptInfo(const QString& id, bool showIt) {
+	QWebElement message = document_.findFirst("#" + id);
+	if (message.isNull()) return;
+	QWebElement receiptElement = message.findFirst("span.swift_receipt");
+	assert(!receiptElement.isNull());
+	receiptElement.setStyleProperty("display", showIt ? "inline" : "none");
+}
+
+void QtWebKitChatView::rememberScrolledToBottom() {
+	isAtBottom_ = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical);
+}
+
+void QtWebKitChatView::scrollToBottom() {
+	isAtBottom_ = true;
+	webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical));
+	webView_->update(); /* Work around redraw bug in some versions of Qt. */
+}
+
+void QtWebKitChatView::handleFrameSizeChanged() {
+	if (isAtBottom_) {
+		scrollToBottom();
+	}
+}
+
+void QtWebKitChatView::handleLinkClicked(const QUrl& url) {
+	QDesktopServices::openUrl(url);
+}
+
+void QtWebKitChatView::handleViewLoadFinished(bool ok) {
+	Q_ASSERT(ok);
+	viewReady_ = true;
+}
+
+void QtWebKitChatView::increaseFontSize(int numSteps) {
+	//qDebug() << "Increasing";
+	fontSizeSteps_ += numSteps;
+	emit fontResized(fontSizeSteps_);
+}
+
+void QtWebKitChatView::decreaseFontSize() {
+	fontSizeSteps_--;
+	if (fontSizeSteps_ < 0) {
+		fontSizeSteps_ = 0;
+	}
+	emit fontResized(fontSizeSteps_);
+}
+
+bool QtWebKitChatView::appendToPreviousCheck(QtWebKitChatView::PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const {
+	return previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
+}
+
+void QtWebKitChatView::resizeFont(int fontSizeSteps) {
+	fontSizeSteps_ = fontSizeSteps;
+	double size = 1.0 + 0.2 * fontSizeSteps_;
+	QString sizeString(QString().setNum(size, 'g', 3) + "em");
+	//qDebug() << "Setting to " << sizeString;
+	const QWebElementCollection spans = document_.findAll("span.swift_resizable");
+	foreach (QWebElement span, spans) {
+		span.setStyleProperty("font-size", sizeString);
+	}
+	webView_->setFontSizeIsMinimal(size == 1.0);
+}
+
+void QtWebKitChatView::resetView() {
+	lastElement_ = QWebElement();
+	QString pageHTML = theme_->getTemplate();
+	pageHTML.replace("==bodyBackground==", "background-color:#e3e3e3");
+	pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getBase());
+	if (pageHTML.count("%@") > 3) {
+		pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getMainCSS());
+	}
+	pageHTML.replace(pageHTML.indexOf("%@"), 2, "Variants/Blue on Green.css");
+	pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*headerSnippet.getContent()*/);
+	pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*footerSnippet.getContent()*/);
+	QEventLoop syncLoop;
+	connect(webView_, SIGNAL(loadFinished(bool)), &syncLoop, SLOT(quit()));
+	webPage_->mainFrame()->setHtml(pageHTML);
+	while (!viewReady_) {
+		QTimer t;
+		t.setSingleShot(true);
+		connect(&t, SIGNAL(timeout()), &syncLoop, SLOT(quit()));
+		t.start(50);
+		syncLoop.exec();
+	}
+	document_ = webPage_->mainFrame()->documentElement();
+	QWebElement chatElement = document_.findFirst("#Chat");
+	newInsertPoint_ = chatElement.clone();
+	newInsertPoint_.setOuterXml("<div id='swift_insert'/>");
+	chatElement.appendInside(newInsertPoint_);
+	Q_ASSERT(!newInsertPoint_.isNull());
+
+	connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection);
+}
+
+QWebElement findDivElementWithID(QWebElement document, QString id) {
+	QWebElementCollection divs = document.findAll("div");
+	foreach(QWebElement div, divs) {
+		if (div.attribute("id") == id) {
+			return div;
+		}
+	}
+	return QWebElement();
+}
+
+void QtWebKitChatView::setFileTransferProgress(QString id, const int percentageDone) {
+	QWebElement ftElement = findDivElementWithID(document_, id);
+	if (ftElement.isNull()) {
+		SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!" << std::endl;
+		return;
+	}
+	QWebElement progressBar = ftElement.findFirst("div.progressbar");
+	progressBar.setStyleProperty("width", QString::number(percentageDone) + "%");
+
+	QWebElement progressBarValue = ftElement.findFirst("div.progressbar-value");
+	progressBarValue.setInnerXml(QString::number(percentageDone) + " %");
+}
+
+void QtWebKitChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& /* msg */) {
+	QWebElement ftElement = findDivElementWithID(document_, id);
+	if (ftElement.isNull()) {
+		SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id) << std::endl;
+		return;
+	}
+
+	QString newInnerHTML = "";
+	if (state == ChatWindow::WaitingForAccept) {
+		newInnerHTML =	tr("Waiting for other side to accept the transfer.") + "<br/>" +
+			QtWebKitChatView::buildChatWindowButton(tr("Cancel"), QtWebKitChatView::ButtonFileTransferCancel, id);
+	}
+	if (state == ChatWindow::Negotiating) {
+		// replace with text "Negotiaging" + Cancel button
+		newInnerHTML =	tr("Negotiating...") + "<br/>" +
+			QtWebKitChatView::buildChatWindowButton(tr("Cancel"), QtWebKitChatView::ButtonFileTransferCancel, id);
+	}
+	else if (state == ChatWindow::Transferring) {
+		// progress bar + Cancel Button
+		newInnerHTML =	"<div style=\"position: relative; width: 90%; height: 20px; border: 2px solid grey; -webkit-border-radius: 10px;\">"
+							"<div class=\"progressbar\" style=\"width: 0%; height: 100%; background: #AAA; -webkit-border-radius: 6px;\">"
+								"<div class=\"progressbar-value\" style=\"position: absolute; top: 0px; left: 0px; width: 100%; text-align: center; padding-top: 2px;\">"
+									"0%"
+								"</div>"
+							"</div>"
+						"</div>" +
+						QtWebKitChatView::buildChatWindowButton(tr("Cancel"), QtWebKitChatView::ButtonFileTransferCancel, id);
+	}
+	else if (state == ChatWindow::Canceled) {
+		newInnerHTML = tr("Transfer has been canceled!");
+	}
+	else if (state == ChatWindow::Finished) {
+		// text "Successful transfer"
+		newInnerHTML = tr("Transfer completed successfully.");
+	}
+	else if (state == ChatWindow::FTFailed) {
+		newInnerHTML = tr("Transfer failed.");
+	}
+
+	ftElement.setInnerXml(newInnerHTML);
+}
+
+void QtWebKitChatView::setMUCInvitationJoined(QString id) {
+	QWebElement divElement = findDivElementWithID(document_, id);
+	QWebElement buttonElement = divElement.findFirst("input#mucinvite");
+	if (!buttonElement.isNull()) {
+		buttonElement.setAttribute("value", tr("Return to room"));
+	}
+}
+void QtWebKitChatView::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.") + "'/>";
+			displayReceiptInfo(P2QSTRING(id), false);
+			break;
+		case ChatWindow::Received:
+			xml = "";
+			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;
+	}
+	setAckXML(P2QSTRING(id), xml);
+}
+
+void QtWebKitChatView::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;
+	}
+	setReceiptXML(P2QSTRING(id), xml);
+}
+
+QString QtWebKitChatView::linkimoticonify(const QString& message) const {
+	QString messageHTML(message);
+	messageHTML = Qt::escape(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 = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML)));
+	}
+	messageHTML.replace("\n","<br/>");
+	return messageHTML;
+}
+
+std::string QtWebKitChatView::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) {
+	return addMessage(linkimoticonify(P2QSTRING(message)), senderName, senderIsSelf, label, avatarPath, style, time);
+}
+
+std::string QtWebKitChatView::addMessage(const QString &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) {
+	SWIFT_LOG(debug) << "[QtWebKitChatView::addMessage] - entered" << std::endl;
+	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(message);
+
+	QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
+	QString styleSpanEnd = style == "" ? "" : "</span>";
+	htmlString += styleSpanStart + messageHTML + styleSpanEnd;
+	SWIFT_LOG(debug) << "[QtWebKitChatView::addMessage] - message html string: " << Q2PSTRING(htmlString) << std::endl;
+
+	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf);
+	if (lastLineTracker_.getShouldMoveLastLine()) {
+		/* should this be queued? */
+		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_++);
+	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;
+	SWIFT_LOG(debug) << "[QtWebKitChatView::addMessage] - return: " << id << std::endl;
+	return id;
+}
+
+void QtWebKitChatView::setChatWindowHasFocus(bool focus) {
+	lastLineTracker_.setHasFocus(focus);
+}
+
+std::string QtWebKitChatView::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 QtWebKitChatView::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 QtWebKitChatView::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) {
+	qDebug() << "addFileTransfer";
+	QString ft_id = "ft" + P2QSTRING(boost::lexical_cast<std::string>(idCounter_++));
+	
+	QString htmlString;
+	QString formattedSize = P2QSTRING(formatSize(sizeInBytes));
+	if (senderIsSelf) {
+		// outgoing
+		htmlString = tr("Send file") + ": " + P2QSTRING(filename) + " ( " + formattedSize + ") </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) + " ( " + formattedSize + ") <br/>" +
+			"<div id='" + ft_id + "'>" +
+				buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) +
+				buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) +
+			"</div>";
+	}
+
+
+	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasFileTransfer, senderName, senderIsSelf);
+	if ( lastLineTracker_.getShouldMoveLastLine()) {
+		/* should this be queued? */
+		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_++);
+	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 QtWebKitChatView::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)));
+		setMUCInvitationJoined(elementID);
+	}
+	else {
+		SWIFT_LOG(debug) << "Unknown HTML button! ( " << Q2PSTRING(id) << " )" << std::endl;
+	}
+}
+
+void QtWebKitChatView::addErrorMessage(const std::string& errorMessage) {
+	QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage)));
+	errorMessageHTML.replace("\n","<br/>");
+	addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_)));
+
+	previousMessageWasSelf_ = false;
+	previousMessageKind_ = PreviousMessageWasSystem;
+}
+
+void QtWebKitChatView::addSystemMessage(const std::string& message) {
+	QString messageHTML(Qt::escape(P2QSTRING(message)));
+	messageHTML = linkimoticonify(messageHTML);
+	addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
+
+	previousMessageWasSelf_ = false;
+	previousMessageKind_ = PreviousMessageWasSystem;
+}
+
+void QtWebKitChatView::addPresenceMessage(const std::string& message) {
+	QString messageHTML(linkimoticonify(P2QSTRING(message)));
+	addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
+
+	previousMessageWasSelf_ = false;
+	previousMessageKind_ = PreviousMessageWasPresence;
+}
+
+void QtWebKitChatView::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) {
+
+	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/>";
+	}
+	if (!direct) {
+		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? */
+		addLastSeenLine();
+		/* if the line is added we should break the snippet */
+		appendToPrevious = false;
+	}
+	QString qAvatarPath = "qrc:/icons/avatar.png";
+
+	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;
+}
+
+}
diff --git a/Swift/QtUI/QtWebKitChatView.h b/Swift/QtUI/QtWebKitChatView.h
new file mode 100644
index 0000000..45585c8
--- /dev/null
+++ b/Swift/QtUI/QtWebKitChatView.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#ifndef SWIFT_QtWebKitChatView_H
+#define SWIFT_QtWebKitChatView_H
+
+#include <QString>
+#include <QWidget>
+#include <QList>
+#include <QWebElement>
+#include <QInputDialog>
+#include <QFileDialog>
+
+#include <boost/shared_ptr.hpp>
+
+#include "MessageSnippet.h"
+
+#include "QtChatView.h"
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include "Swift/Controllers/Settings/SettingsProvider.h"
+
+#include <SwifTools/LastLineTracker.h>
+
+class QWebPage;
+class QUrl;
+
+namespace Swift {
+	class QtWebView;
+	class QtChatTheme;
+	class UIEventStream;
+	class QtChatWindowJSBridge;
+
+	class QtWebKitChatView : public QtChatView {
+			Q_OBJECT
+
+		public:
+			static const QString ButtonFileTransferCancel;
+			static const QString ButtonFileTransferSetDescription;
+			static const QString ButtonFileTransferSendRequest;
+			static const QString ButtonFileTransferAcceptRequest;
+			static const QString ButtonMUCInvite;
+
+		public:
+			QtWebKitChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons);
+			virtual ~QtWebKitChatView();
+			void addMessage(boost::shared_ptr<ChatSnippet> snippet);
+			void addLastSeenLine();
+			void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time);
+			void replaceLastMessage(const QString& newMessage);
+			void replaceLastMessage(const QString& newMessage, const QString& note);
+			void replaceMessage(const std::string& newMessage, const QString& id, const QDateTime& time, const QString& style);
+			void replaceMessage(const QString& message, const QString& id, const QDateTime& time, const QString& style);
+			void rememberScrolledToBottom();
+			void setAckXML(const QString& id, const QString& xml);
+			void setReceiptXML(const QString& id, const QString& xml);
+			void displayReceiptInfo(const QString& id, bool showIt);
+			QString linkimoticonify(const QString& message) const;
+
+			QString getLastSentMessage();
+			void addToJSEnvironment(const QString&, QObject*);
+			void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg);
+			void setFileTransferProgress(QString id, const int percentageDone);
+			void setMUCInvitationJoined(QString id);
+			void showEmoticons(bool show);
+			void setAckState(std::string const& id, ChatWindow::AckState);
+			void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state);
+			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);
+			std::string 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);
+			void setChatWindowHasFocus(bool focus);
+			std::string 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);
+			std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes);
+			void addErrorMessage(const std::string& errorMessage);
+			void addSystemMessage(const std::string& message);
+			void addPresenceMessage(const std::string& message);
+			void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct);
+
+			static QString buildChatWindowButton(const QString& name, const QString& id, const QString& arg1 = QString(), const QString& arg2 = QString(), const QString& arg3 = QString());
+
+		public slots:
+			void copySelectionToClipboard();
+			void scrollToBottom();
+			void handleLinkClicked(const QUrl&);
+			void handleKeyPressEvent(QKeyEvent* event);
+			void resetView();
+			void increaseFontSize(int numSteps = 1);
+			void decreaseFontSize();
+			void resizeFont(int fontSizeSteps);
+
+		private slots:
+			void handleViewLoadFinished(bool);
+			void handleFrameSizeChanged();
+			void handleClearRequested();
+			void handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3);
+
+		private:
+			enum PreviousMessageKind {
+				PreviosuMessageWasNone,
+				PreviousMessageWasMessage,
+				PreviousMessageWasSystem,
+				PreviousMessageWasPresence,
+				PreviousMessageWasFileTransfer,
+				PreviousMessageWasMUCInvite
+			};
+
+			void headerEncode();
+			void messageEncode();
+			void addToDOM(boost::shared_ptr<ChatSnippet> snippet);
+			QWebElement snippetToDOM(boost::shared_ptr<ChatSnippet> snippet);
+			bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const;
+			void handleSettingChanged(const std::string& setting);
+
+			bool viewReady_;
+			bool isAtBottom_;
+			QtWebView* webView_;
+			QWebPage* webPage_;
+			int fontSizeSteps_;
+			QtChatTheme* theme_;
+			QWebElement newInsertPoint_;
+			QWebElement lineSeparator_;
+			QWebElement lastElement_;
+			QWebElement document_;
+			bool previousMessageWasSelf_;
+			QString previousSenderName_;
+			PreviousMessageKind previousMessageKind_;
+			int idCounter_;
+			LastLineTracker lastLineTracker_;
+			UIEventStream* eventStream_;
+			std::map<QString, QString> descriptions_;
+			QtChatWindowJSBridge* jsBridge_;
+			SettingsProvider* settings_;
+			QMap<QString, QString> emoticons_;
+			bool showEmoticons_;
+	};
+}
+
+#endif
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 064faab..9070291 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -73,6 +73,7 @@ sources = [
     "QtAvatarWidget.cpp",
     "QtUIFactory.cpp",
     "QtChatWindowFactory.cpp",
+    "QtChatViewFactory.cpp",
     "QtChatWindow.cpp",
     "QtClickableLabel.cpp",
     "QtLoginWindow.cpp",
@@ -84,7 +85,7 @@ sources = [
     "QtScaledAvatarCache.cpp",
     "QtSwift.cpp",
     "QtURIHandler.cpp",
-    "QtChatView.cpp",
+    "QtWebKitChatView.cpp",
     "QtChatTheme.cpp",
     "QtChatTabs.cpp",
     "QtSoundPlayer.cpp",
@@ -150,7 +151,9 @@ sources = [
     "QtChatWindowJSBridge.cpp",
     "QtMUCConfigurationWindow.cpp",
     "QtAffiliationEditor.cpp",
-    "QtUISettingConstants.cpp"
+    "QtUISettingConstants.cpp",
+    "QtChatView.cpp",
+    "QtBarriersFreeChatView.cpp",
   ]
 
 myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
diff --git a/Swift/QtUI/WebKitChatView/WebKitChatView.pro b/Swift/QtUI/WebKitChatView/WebKitChatView.pro
new file mode 100644
index 0000000..25a6dd0
--- /dev/null
+++ b/Swift/QtUI/WebKitChatView/WebKitChatView.pro
@@ -0,0 +1,8 @@
+TEMPLATE = app
+TARGET = ChatView
+DEPENDPATH += .
+INCLUDEPATH += . ../../../Swiften/3rdParty/Boost
+QT += webkit network
+HEADERS += ../QtWebKitChatView.h
+SOURCES += main.cpp ../QtWebKitChatView.cpp ../ChatSnippet.cpp ../MessageSnippet.cpp ../SystemMessageSnippet.cpp
+RESOURCES += ../DefaultTheme.qrc ../Swift.qrc
diff --git a/Swift/QtUI/WebKitChatView/main.cpp b/Swift/QtUI/WebKitChatView/main.cpp
new file mode 100644
index 0000000..0f53432
--- /dev/null
+++ b/Swift/QtUI/WebKitChatView/main.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <QtDebug>
+#include <QApplication>
+#include <iostream>
+#include <QWidget>
+#include <QFile>
+#include <QDateTime>
+#include <QLineEdit>
+#include <QVBoxLayout>
+#include <QWebView>
+#include <QWebFrame>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QNetworkAccessManager>
+#include "../QtChatView.h"
+#include "../MessageSnippet.h"
+#include "../SystemMessageSnippet.h"
+
+using namespace Swift;
+
+/*
+class MyNetworkReply : public QNetworkReply {
+	public:
+		MyNetworkReply() {
+		}
+		
+		qint64 readData(char*, qint64) {
+			return 0;
+		}
+
+		virtual void abort() {
+		}
+};
+
+class MyNetworkAccessManager : public QNetworkAccessManager {
+	public:
+		MyNetworkAccessManager() {
+		}
+
+		QNetworkReply * createRequest (Operation op, const QNetworkRequest& request, QIODevice* outgoingData = 0) {
+			assert(op == QNetworkAccessManager::GetOperation);
+			qDebug() << "Requesting: " << request.url();
+			return QNetworkAccessManager::createRequest(op, request, outgoingData);
+			//return new MyNetworkReply();
+		}
+};
+
+			QVBoxLayout* mainLayout = new QVBoxLayout(this);
+			webView_ = new QWebView(this);
+
+			QFile file(":/themes/Stockholm/Contents/Resources/Incoming/Content.html");
+			file.open(QIODevice::ReadOnly);
+			QString content = QString::fromUtf8(file.readAll());
+
+			webPage_ = new QWebPage(this);
+			webPage_->setNetworkAccessManager(new MyNetworkAccessManager());
+			webView_->setPage(webPage_);
+			QString pagehtml = 
+				"<head>"
+					//"<base href=\"file:///Users/remko/src/swift/resources/themes/Stockholm/Contents/Resources/\"/>"
+					"<base href=\"file:///Users/remko/src/swift/resources/themes/Stockholm/Contents/Resources/\"/>"
+					"<link rel=\"stylesheet\" type=\"text/css\" href=\"main.css\"/>"
+					"<link rel=\"stylesheet\" type=\"text/css\" href=\"Variants/Alt Blue - Blue.css\"/>"
+				"</head><body>" + content + "</body>";
+			qDebug() << pagehtml;
+			webPage_->mainFrame()->setHtml(pagehtml);
+
+*/
+
+/*
+
+class ChatView : public QWidget {
+	public:
+		ChatView(QWidget* parent) : QWidget(parent) {
+			setFocusPolicy(Qt::NoFocus);
+
+			QVBoxLayout* mainLayout = new QVBoxLayout(this);
+			mainLayout->setSpacing(0);
+			mainLayout->setContentsMargins(0,0,0,0);
+
+			webView_ = new QWebView(this);
+			webView_->setFocusPolicy(Qt::NoFocus);
+			mainLayout->addWidget(webView_);
+
+			webPage_ = new QWebPage(this);
+			webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
+			webView_->setPage(webPage_);
+			connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard()));
+
+			QString pageHTML = "<head></head><body><div id=\"chat\"></div></body>";
+			webPage_->mainFrame()->setHtml(pageHTML);
+		}
+
+		void appendHTML(const QString& html) {
+			webPage_->mainFrame()->evaluateJavaScript(
+				"newNode = document.createElement(\"div\");"
+				"newNode.innerHTML = \"" + html + "\";" 
+				"chatElement = document.getElementById(\"chat\");"
+				"chatElement.appendChild(newNode);");
+			webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical));
+		}
+
+	private:
+		QWebView* webView_;
+		QWebPage* webPage_;
+};
+*/
+
+class MyWidget : public QWidget {
+		Q_OBJECT
+
+	public:
+		MyWidget() : previousWasIncoming_(false), previousWasOutgoing_(false), previousWasSystem_(false) {
+			QVBoxLayout* mainLayout = new QVBoxLayout(this);
+			chatView_ = new QtChatView(this);
+			mainLayout->addWidget(chatView_);
+			input1_ = new QLineEdit(this);
+			connect(input1_, SIGNAL(returnPressed()), SLOT(addIncoming()));
+			mainLayout->addWidget(input1_);
+			input2_ = new QLineEdit(this);
+			connect(input2_, SIGNAL(returnPressed()), SLOT(addOutgoing()));
+			mainLayout->addWidget(input2_);
+			input3_ = new QLineEdit(this);
+			connect(input3_, SIGNAL(returnPressed()), SLOT(addSystem()));
+			mainLayout->addWidget(input3_);
+
+			resize(300,200);
+		}
+	
+	public slots:
+		void addIncoming() {
+			chatView_->addMessage(MessageSnippet(input1_->text(), "Me", QDateTime::currentDateTime(), "qrc:/icons/avatar.png", true, previousWasIncoming_));
+			previousWasIncoming_ = true;
+			previousWasOutgoing_ = false;
+			previousWasSystem_ = false;
+			input1_->clear();
+		}
+
+		void addOutgoing() {
+			chatView_->addMessage(MessageSnippet(input2_->text(), "You", QDateTime::currentDateTime(), "qrc:/icons/avatar.png", false, previousWasOutgoing_));
+			previousWasIncoming_ = false;
+			previousWasOutgoing_ = true;
+			previousWasSystem_ = false;
+			input2_->clear();
+		}
+
+		void addSystem() {
+			chatView_->addMessage(SystemMessageSnippet(input3_->text(), QDateTime::currentDateTime(), previousWasSystem_));
+			previousWasIncoming_ = false;
+			previousWasOutgoing_ = false;
+			previousWasSystem_ = true;
+			input3_->clear();
+		}
+
+	private:
+		bool previousWasIncoming_;
+		bool previousWasOutgoing_;
+		bool previousWasSystem_;
+		QtChatView* chatView_;
+		QLineEdit* input1_;
+		QLineEdit* input2_;
+		QLineEdit* input3_;
+};
+		
+
+int main(int argc, char* argv[]) {
+	QApplication app(argc, argv);
+	MyWidget w;
+	w.show();
+	return app.exec();
+}
+
+#include "main.moc"
-- 
cgit v0.10.2-6-g49f6