summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swift/QtUI')
-rw-r--r--Swift/QtUI/ApplicationTest/ApplicationTest.pro14
-rw-r--r--Swift/QtUI/ApplicationTest/main.cpp39
-rw-r--r--Swift/QtUI/ChatSnippet.cpp33
-rw-r--r--Swift/QtUI/ChatSnippet.h26
-rw-r--r--Swift/QtUI/ChatView/ChatView.pro8
-rw-r--r--Swift/QtUI/ChatView/main.cpp172
-rw-r--r--Swift/QtUI/Makefile.inc28
-rw-r--r--Swift/QtUI/MessageSnippet.cpp32
-rw-r--r--Swift/QtUI/MessageSnippet.h25
-rw-r--r--Swift/QtUI/NotifierTest/NotifierTest.cpp20
-rw-r--r--Swift/QtUI/NotifierTest/NotifierTest.pro9
-rw-r--r--Swift/QtUI/QtChatView.cpp79
-rw-r--r--Swift/QtUI/QtChatView.h32
-rw-r--r--Swift/QtUI/QtChatWindow.cpp180
-rw-r--r--Swift/QtUI/QtChatWindow.h56
-rw-r--r--Swift/QtUI/QtChatWindowFactory.cpp16
-rw-r--r--Swift/QtUI/QtChatWindowFactory.h18
-rw-r--r--Swift/QtUI/QtJoinMUCDialog.cpp32
-rw-r--r--Swift/QtUI/QtJoinMUCDialog.h26
-rw-r--r--Swift/QtUI/QtJoinMUCDialog.ui72
-rw-r--r--Swift/QtUI/QtLoginWindow.cpp131
-rw-r--r--Swift/QtUI/QtLoginWindow.h42
-rw-r--r--Swift/QtUI/QtLoginWindowFactory.cpp8
-rw-r--r--Swift/QtUI/QtLoginWindowFactory.h12
-rw-r--r--Swift/QtUI/QtMainEventLoop.h40
-rw-r--r--Swift/QtUI/QtMainWindow.cpp71
-rw-r--r--Swift/QtUI/QtMainWindow.h43
-rw-r--r--Swift/QtUI/QtMainWindowFactory.cpp15
-rw-r--r--Swift/QtUI/QtMainWindowFactory.h17
-rw-r--r--Swift/QtUI/QtSettingsProvider.cpp23
-rw-r--r--Swift/QtUI/QtSettingsProvider.h24
-rw-r--r--Swift/QtUI/QtStatusWidget.cpp32
-rw-r--r--Swift/QtUI/QtStatusWidget.h28
-rw-r--r--Swift/QtUI/QtSwift.cpp43
-rw-r--r--Swift/QtUI/QtSwift.h36
-rw-r--r--Swift/QtUI/QtSwiftUtil.h7
-rw-r--r--Swift/QtUI/QtTreeWidget.cpp29
-rw-r--r--Swift/QtUI/QtTreeWidget.h28
-rw-r--r--Swift/QtUI/QtTreeWidgetFactory.cpp0
-rw-r--r--Swift/QtUI/QtTreeWidgetFactory.h36
-rw-r--r--Swift/QtUI/QtTreeWidgetItem.cpp0
-rw-r--r--Swift/QtUI/QtTreeWidgetItem.h57
-rw-r--r--Swift/QtUI/Swift.pro92
-rw-r--r--Swift/QtUI/Swift.qrc9
-rw-r--r--Swift/QtUI/SystemMessageSnippet.cpp15
-rw-r--r--Swift/QtUI/SystemMessageSnippet.h25
-rw-r--r--Swift/QtUI/main.cpp10
-rwxr-xr-xSwift/QtUI/qmakeish.py107
48 files changed, 1897 insertions, 0 deletions
diff --git a/Swift/QtUI/ApplicationTest/ApplicationTest.pro b/Swift/QtUI/ApplicationTest/ApplicationTest.pro
new file mode 100644
index 0000000..9222aec
--- /dev/null
+++ b/Swift/QtUI/ApplicationTest/ApplicationTest.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+TARGET =
+DEPENDPATH += .
+INCLUDEPATH += . ../../.. ../../../Swiften/3rdParty/Boost
+mac {
+ ICON = ../../../../resources/MacOSX/Swift.icns
+}
+
+HEADERS += \
+ ../QtSwiftUtil.h
+SOURCES += \
+ main.cpp
+LIBS += \
+ ../../../Swiften/Swift.a
diff --git a/Swift/QtUI/ApplicationTest/main.cpp b/Swift/QtUI/ApplicationTest/main.cpp
new file mode 100644
index 0000000..7ddb76d
--- /dev/null
+++ b/Swift/QtUI/ApplicationTest/main.cpp
@@ -0,0 +1,39 @@
+#include <QApplication>
+#include <QWidget>
+#include <QVBoxLayout>
+#include <QLineEdit>
+#include "../QtSwiftUtil.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Application/Platform/PlatformApplication.h"
+
+using namespace Swift;
+
+class MyWidget : public QWidget {
+ Q_OBJECT
+
+ public:
+ MyWidget() : application_("MyApplication") {
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ input_ = new QLineEdit(this);
+ layout->addWidget(input_);
+ connect(input_, SIGNAL(returnPressed()), SLOT(handleReturnPressed()));
+ }
+
+ private slots:
+ void handleReturnPressed() {
+ application_.getApplicationMessageDisplay()->setMessage(Q2PSTRING(input_->text()));
+ }
+
+ private:
+ PlatformApplication application_;
+ QLineEdit* input_;
+};
+
+int main(int argc, char* argv[]) {
+ QApplication app(argc, argv);
+ MyWidget widget;
+ widget.show();
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/Swift/QtUI/ChatSnippet.cpp b/Swift/QtUI/ChatSnippet.cpp
new file mode 100644
index 0000000..7e8326c
--- /dev/null
+++ b/Swift/QtUI/ChatSnippet.cpp
@@ -0,0 +1,33 @@
+#include <QFile>
+
+#include "ChatSnippet.h"
+
+namespace Swift {
+
+ChatSnippet::ChatSnippet(bool appendToPrevious) : appendToPrevious_(appendToPrevious) {
+}
+
+ChatSnippet::~ChatSnippet() {
+}
+
+QString ChatSnippet::loadTemplate(const QString& filename) {
+ QFile file(filename);
+ bool result = file.open(QIODevice::ReadOnly);
+ Q_ASSERT(result);
+ Q_UNUSED(result);
+ QString content = file.readAll();
+ file.close();
+ return content;
+}
+
+QString ChatSnippet::escape(const QString& original) {
+ QString result(original);
+ result.replace("%message%", "&#37;message&#37;");
+ result.replace("%sender%", "&#37;sender&#37;");
+ result.replace("%time%", "%&#37;time&#37;");
+ result.replace("%shortTime%", "%&#37;shortTime&#37;");
+ result.replace("%userIconPath%", "&#37;userIconPath&#37;");
+ return result;
+}
+
+};
diff --git a/Swift/QtUI/ChatSnippet.h b/Swift/QtUI/ChatSnippet.h
new file mode 100644
index 0000000..5d4b6eb
--- /dev/null
+++ b/Swift/QtUI/ChatSnippet.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <QString>
+
+namespace Swift {
+ class ChatSnippet {
+ public:
+ ChatSnippet(bool appendToPrevious = false);
+ virtual ~ChatSnippet();
+
+ virtual const QString& getContent() const = 0;
+ virtual QString getContinuationElementID() const { return ""; }
+
+ bool getAppendToPrevious() const {
+ return appendToPrevious_;
+ }
+
+ protected:
+ QString loadTemplate(const QString& file);
+ static QString escape(const QString&);
+
+ private:
+ bool appendToPrevious_;
+ };
+}
+
diff --git a/Swift/QtUI/ChatView/ChatView.pro b/Swift/QtUI/ChatView/ChatView.pro
new file mode 100644
index 0000000..3017d35
--- /dev/null
+++ b/Swift/QtUI/ChatView/ChatView.pro
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 0000000..9345f16
--- /dev/null
+++ b/Swift/QtUI/ChatView/main.cpp
@@ -0,0 +1,172 @@
+#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/Makefile.inc b/Swift/QtUI/Makefile.inc
new file mode 100644
index 0000000..8b0ab30
--- /dev/null
+++ b/Swift/QtUI/Makefile.inc
@@ -0,0 +1,28 @@
+TARGETS += qt
+CLEAN_TARGETS += clean-qt
+
+ifeq ($(WIN32),1)
+QT_MAKE=nmake
+else
+QT_MAKE=$(MAKE)
+endif
+
+
+.PHONY: qt
+qt: Swift/QtUI/Makefile Swiften/Swiften.a $(BUNDLED_LIBS)
+ cd Swift/QtUI && $(QT_MAKE)
+
+.PHONY: clean-qt
+clean-qt:
+ if [ -f "Swift/QtUI/Makefile" ]; then \
+ cd Swift/QtUI && $(QT_MAKE) clean; \
+ fi
+
+Swift/QtUI/Makefile: Swift/QtUI/DefaultTheme.qrc Swift/QtUI/Swiften.pri
+ cd Swift/QtUI && $(QMAKE) Swift.pro
+
+Swift/QtUI/Swiften.pri:
+ cd Swift/QtUI && ./qmakeish.py ../../Makefile > Swiften.pri
+
+Swift/QtUI/DefaultTheme.qrc:
+ cd Swift/QtUI && ../../tools/ThemeQRC.py ../../resources/themes/Default > DefaultTheme.qrc
diff --git a/Swift/QtUI/MessageSnippet.cpp b/Swift/QtUI/MessageSnippet.cpp
new file mode 100644
index 0000000..0529a23
--- /dev/null
+++ b/Swift/QtUI/MessageSnippet.cpp
@@ -0,0 +1,32 @@
+#include "MessageSnippet.h"
+
+#include <QtDebug>
+#include <QDateTime>
+
+namespace Swift {
+
+MessageSnippet::MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious) : ChatSnippet(appendToPrevious) {
+ if (isIncoming) {
+ if (appendToPrevious) {
+ content_ = loadTemplate(":/themes/Default/Incoming/NextContent.html");
+ }
+ else {
+ content_ = loadTemplate(":/themes/Default/Incoming/Content.html");
+ }
+ }
+ else {
+ if (appendToPrevious) {
+ content_ = loadTemplate(":/themes/Default/Outgoing/NextContent.html");
+ }
+ else {
+ content_ = loadTemplate(":/themes/Default/Outgoing/Content.html");
+ }
+ }
+
+ content_.replace("%message%", escape(message));
+ content_.replace("%sender%", escape(sender));
+ content_.replace("%time%", escape(time.toString("h:mm")));
+ content_.replace("%userIconPath%", escape(iconURI));
+}
+
+}
diff --git a/Swift/QtUI/MessageSnippet.h b/Swift/QtUI/MessageSnippet.h
new file mode 100644
index 0000000..b24c4f9
--- /dev/null
+++ b/Swift/QtUI/MessageSnippet.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <QString>
+
+#include "ChatSnippet.h"
+
+class QDateTime;
+
+namespace Swift {
+ class MessageSnippet : public ChatSnippet {
+ public:
+ MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious);
+
+ const QString& getContent() const {
+ return content_;
+ }
+
+ QString getContinuationElementID() const {
+ return "insert";
+ };
+
+ private:
+ QString content_;
+ };
+}
diff --git a/Swift/QtUI/NotifierTest/NotifierTest.cpp b/Swift/QtUI/NotifierTest/NotifierTest.cpp
new file mode 100644
index 0000000..8a0fb22
--- /dev/null
+++ b/Swift/QtUI/NotifierTest/NotifierTest.cpp
@@ -0,0 +1,20 @@
+#include <iostream>
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Notifier/GrowlNotifier.h"
+#include <QApplication>
+
+using namespace Swift;
+
+void notificationClicked(const String& message) {
+ std::cout << "Notification clicked: " << message << std::endl;
+}
+
+int main(int argc, char* argv[]) {
+ QApplication app(argc, argv);
+ GrowlNotifier notifier("Swift-NotifierTest");
+ notifier.showMessage(Notifier::ContactAvailable, "Contact is available", "The contact has become available", ByteArray(), boost::bind(&notificationClicked, "Message 1"));
+ return app.exec();
+}
diff --git a/Swift/QtUI/NotifierTest/NotifierTest.pro b/Swift/QtUI/NotifierTest/NotifierTest.pro
new file mode 100644
index 0000000..fb15f13
--- /dev/null
+++ b/Swift/QtUI/NotifierTest/NotifierTest.pro
@@ -0,0 +1,9 @@
+TEMPLATE = app
+TARGET =
+DEPENDPATH += .
+INCLUDEPATH += .
+
+INCLUDEPATH += . ../../../Swiften/3rdParty/Boost
+INCLUDEPATH += . ../../..
+SOURCES += NotifierTest.cpp
+LIBS += ../../../Swiften/Swift.a -framework Growl
diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp
new file mode 100644
index 0000000..ab093db
--- /dev/null
+++ b/Swift/QtUI/QtChatView.cpp
@@ -0,0 +1,79 @@
+#include "QtChatView.h"
+
+#include <QtDebug>
+#include <QFile>
+#include <QVBoxLayout>
+#include <QWebView>
+#include <QWebFrame>
+#include <QKeyEvent>
+
+namespace Swift {
+
+QtChatView::QtChatView(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()));
+
+ QFile file(":/themes/Default/Template.html");
+ bool result = file.open(QIODevice::ReadOnly);
+ Q_ASSERT(result);
+ Q_UNUSED(result);
+ QString pageHTML = file.readAll();
+ pageHTML.replace("==bodyBackground==", "background-color:#e3e3e3");
+ pageHTML.replace(pageHTML.indexOf("%@"), 2, "qrc:/themes/Default/");
+ pageHTML.replace(pageHTML.indexOf("%@"), 2, "Variants/Blue on Green.css");
+ pageHTML.replace(pageHTML.indexOf("%@"), 2, "");
+ pageHTML.replace(pageHTML.indexOf("%@"), 2, "");
+ file.close();
+ webPage_->mainFrame()->setHtml(pageHTML);
+}
+
+void QtChatView::addMessage(const ChatSnippet& snippet) {
+ //bool wasScrolledToBottom = isScrolledToBottom();
+
+ QString content = snippet.getContent();
+ content.replace("\\", "\\\\");
+ content.replace("\"", "\\\"");
+ content.replace("\n", "\\n");
+ content.replace("\r", "");
+ if (previousContinuationElementID_.isEmpty() || !snippet.getAppendToPrevious()) {
+ webPage_->mainFrame()->evaluateJavaScript("appendMessage(\"" + content + "\");");
+ }
+ else {
+ webPage_->mainFrame()->evaluateJavaScript("appendNextMessage(\"" + content + "\");");
+ }
+
+ //qDebug() << webPage_->mainFrame()->toHtml();
+ previousContinuationElementID_ = snippet.getContinuationElementID();
+
+ /*if (wasScrolledToBottom) {
+ scrollToBottom();
+ }*/
+}
+
+void QtChatView::copySelectionToClipboard() {
+ if (!webPage_->selectedText().isEmpty()) {
+ webPage_->triggerAction(QWebPage::Copy);
+ }
+}
+
+bool QtChatView::isScrolledToBottom() const {
+ return webPage_->mainFrame()->scrollBarValue(Qt::Vertical) == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical);
+}
+
+void QtChatView::scrollToBottom() {
+ webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical));
+}
+
+}
diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h
new file mode 100644
index 0000000..2a50129
--- /dev/null
+++ b/Swift/QtUI/QtChatView.h
@@ -0,0 +1,32 @@
+#ifndef SWIFT_QtChatView_H
+#define SWIFT_QtChatView_H
+
+#include <QString>
+#include <QWidget>
+
+#include "ChatSnippet.h"
+
+class QWebView;
+class QWebPage;
+
+namespace Swift {
+ class QtChatView : public QWidget {
+ Q_OBJECT
+ public:
+ QtChatView(QWidget* parent);
+
+ void addMessage(const ChatSnippet& snippet);
+ bool isScrolledToBottom() const;
+
+ public slots:
+ void copySelectionToClipboard();
+ void scrollToBottom();
+
+ private:
+ QWebView* webView_;
+ QWebPage* webPage_;
+ QString previousContinuationElementID_;
+ };
+}
+
+#endif
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
new file mode 100644
index 0000000..1db8dae
--- /dev/null
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -0,0 +1,180 @@
+#include "QtChatWindow.h"
+#include "QtSwiftUtil.h"
+#include "QtTreeWidget.h"
+#include "QtTreeWidgetFactory.h"
+#include "QtChatView.h"
+#include "MessageSnippet.h"
+#include "SystemMessageSnippet.h"
+
+#include <QApplication>
+#include <QBoxLayout>
+#include <QCloseEvent>
+#include <QComboBox>
+#include <QLineEdit>
+#include <QSplitter>
+#include <QString>
+#include <QTextEdit>
+#include <QTime>
+
+namespace Swift {
+QtChatWindow::QtChatWindow(const QString &contact, QtTreeWidgetFactory *treeWidgetFactory) : QWidget(), contact_(contact), previousMessageWasSelf_(false), previousMessageWasSystem_(false) {
+ unreadCount_ = 0;
+ updateTitleWithUnreadCount();
+
+ QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
+ layout->setContentsMargins(0,0,0,0);
+ layout->setSpacing(2);
+
+
+ QSplitter *logRosterSplitter = new QSplitter(this);
+ layout->addWidget(logRosterSplitter);
+
+ messageLog_ = new QtChatView(this);
+ logRosterSplitter->addWidget(messageLog_);
+
+ treeWidget_ = dynamic_cast<QtTreeWidget*>(treeWidgetFactory->createTreeWidget());
+ treeWidget_->hide();
+ logRosterSplitter->addWidget(treeWidget_);
+
+
+ QWidget* midBar = new QWidget(this);
+ layout->addWidget(midBar);
+ QHBoxLayout *midBarLayout = new QHBoxLayout(midBar);
+ midBarLayout->setContentsMargins(0,0,0,0);
+ midBarLayout->setSpacing(2);
+ midBarLayout->addStretch();
+ labelsWidget_ = new QComboBox(this);
+ labelsWidget_->setFocusPolicy(Qt::NoFocus);
+ labelsWidget_->hide();
+ labelsWidget_->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ midBarLayout->addWidget(labelsWidget_,0);
+
+ input_ = new QLineEdit(this);
+ layout->addWidget(input_);
+
+ connect(input_, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
+ setFocusProxy(input_);
+ connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(qAppFocusChanged(QWidget*, QWidget*)));
+
+ resize(400,300);
+}
+
+TreeWidget* QtChatWindow::getTreeWidget() {
+ return treeWidget_;
+}
+
+void QtChatWindow::setAvailableSecurityLabels(const std::vector<SecurityLabel>& labels) {
+ availableLabels_ = labels;
+ labelsWidget_->clear();
+ int i = 0;
+ foreach (SecurityLabel label, labels) {
+ QString labelName = P2QSTRING(label.getDisplayMarking());
+ labelsWidget_->addItem(labelName, QVariant(i));
+ i++;
+ }
+}
+
+
+void QtChatWindow::setSecurityLabelsError() {
+ labelsWidget_->setEnabled(false);
+}
+
+void QtChatWindow::setSecurityLabelsEnabled(bool enabled) {
+ if (enabled) {
+ labelsWidget_->setEnabled(true);
+ labelsWidget_->show();
+ } else {
+ labelsWidget_->hide();
+ }
+}
+
+SecurityLabel QtChatWindow::getSelectedSecurityLabel() {
+ assert(labelsWidget_->isEnabled());
+ return availableLabels_[labelsWidget_->currentIndex()];
+}
+
+void QtChatWindow::closeEvent(QCloseEvent* event) {
+ onClosed();
+ event->accept();
+}
+
+void QtChatWindow::convertToMUC() {
+ treeWidget_->show();
+}
+
+void QtChatWindow::qAppFocusChanged(QWidget *old, QWidget *now) {
+ Q_UNUSED(old);
+ if (now == this || now == messageLog_ || now == input_) {
+ onAllMessagesRead();
+ }
+
+}
+
+void QtChatWindow::setUnreadMessageCount(int count) {
+ unreadCount_ = count;
+ updateTitleWithUnreadCount();
+}
+
+void QtChatWindow::updateTitleWithUnreadCount() {
+ setWindowTitle(unreadCount_ > 0 ? QString("(%1) %2)").arg(unreadCount_).arg(contact_) : contact_);
+}
+
+void QtChatWindow::addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label) {
+ if (isActiveWindow()) {
+ onAllMessagesRead();
+ }
+
+ QString htmlString;
+ if (label) {
+ htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \">").arg(Qt::escape(P2QSTRING(label->getForegroundColor()))).arg(Qt::escape(P2QSTRING(label->getBackgroundColor())));
+ htmlString += QString("%3</span> ").arg(Qt::escape(P2QSTRING(label->getDisplayMarking())));
+ }
+ QString messageHTML(Qt::escape(P2QSTRING(message)));
+ messageHTML.replace("\n","<br/>");
+ htmlString += messageHTML;
+
+ bool appendToPrevious = !previousMessageWasSystem_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
+ messageLog_->addMessage(MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), QDateTime::currentDateTime(), "qrc:/icons/avatar.png", senderIsSelf, appendToPrevious));
+
+ previousMessageWasSelf_ = senderIsSelf;
+ previousSenderName_ = P2QSTRING(senderName);
+ previousMessageWasSystem_ = false;
+}
+
+void QtChatWindow::addErrorMessage(const String& errorMessage) {
+ if (isActiveWindow()) {
+ onAllMessagesRead();
+ }
+
+ QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage)));
+ errorMessageHTML.replace("\n","<br/>");
+ messageLog_->addMessage(SystemMessageSnippet(QString("<span class=\"error\">%1</span>").arg(errorMessageHTML), QDateTime::currentDateTime(),previousMessageWasSystem_));
+
+ previousMessageWasSelf_ = false;
+ previousMessageWasSystem_ = true;
+}
+
+void QtChatWindow::addSystemMessage(const String& message) {
+ if (isActiveWindow()) {
+ onAllMessagesRead();
+ }
+
+ QString messageHTML(Qt::escape(P2QSTRING(message)));
+ messageHTML.replace("\n","<br/>");
+ messageLog_->addMessage(SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(),previousMessageWasSystem_));
+
+ previousMessageWasSelf_ = false;
+ previousMessageWasSystem_ = true;
+}
+
+void QtChatWindow::returnPressed() {
+ onSendMessageRequest(Q2PSTRING(input_->text()));
+ messageLog_->scrollToBottom();
+ input_->clear();
+}
+
+void QtChatWindow::show() {
+ QWidget::show();
+}
+
+}
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
new file mode 100644
index 0000000..ef091e3
--- /dev/null
+++ b/Swift/QtUI/QtChatWindow.h
@@ -0,0 +1,56 @@
+#ifndef SWIFT_QtChatWindow_H
+#define SWIFT_QtChatWindow_H
+
+#include "Swiften/Controllers/ChatWindow.h"
+
+#include <QWidget>
+
+class QTextEdit;
+class QLineEdit;
+class QComboBox;
+
+namespace Swift {
+ class QtChatView;
+ class QtTreeWidget;
+ class QtTreeWidgetFactory;
+ class TreeWidget;
+ class QtChatWindow : public QWidget, public ChatWindow {
+ Q_OBJECT
+ public:
+ QtChatWindow(const QString &contact, QtTreeWidgetFactory* treeWidgetFactory);
+ void addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label);
+ void addSystemMessage(const String& message);
+ void addErrorMessage(const String& errorMessage);
+ void show();
+ void setUnreadMessageCount(int count);
+ void convertToMUC();
+ TreeWidget *getTreeWidget();
+ void setAvailableSecurityLabels(const std::vector<SecurityLabel>& labels);
+ void setSecurityLabelsEnabled(bool enabled);
+ void setSecurityLabelsError();
+ SecurityLabel getSelectedSecurityLabel();
+
+ protected slots:
+ void qAppFocusChanged(QWidget* old, QWidget* now);
+ void closeEvent(QCloseEvent* event);
+
+ private slots:
+ void returnPressed();
+
+ private:
+ void updateTitleWithUnreadCount();
+
+ int unreadCount_;
+ QString contact_;
+ QtChatView *messageLog_;
+ QLineEdit* input_;
+ QComboBox *labelsWidget_;
+ QtTreeWidget *treeWidget_;
+ std::vector<SecurityLabel> availableLabels_;
+ bool previousMessageWasSelf_;
+ bool previousMessageWasSystem_;
+ QString previousSenderName_;
+ };
+}
+
+#endif
diff --git a/Swift/QtUI/QtChatWindowFactory.cpp b/Swift/QtUI/QtChatWindowFactory.cpp
new file mode 100644
index 0000000..b0b3679
--- /dev/null
+++ b/Swift/QtUI/QtChatWindowFactory.cpp
@@ -0,0 +1,16 @@
+#include "QtChatWindowFactory.h"
+#include "QtChatWindow.h"
+#include "QtSwiftUtil.h"
+#include "QtTreeWidgetFactory.h"
+
+namespace Swift {
+QtChatWindowFactory::QtChatWindowFactory(QtTreeWidgetFactory *treeWidgetFactory) : treeWidgetFactory_(treeWidgetFactory) {
+
+}
+ChatWindow* QtChatWindowFactory::createChatWindow(const JID &contact) {
+ QtChatWindow *chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), treeWidgetFactory_);
+ chatWindow->show();
+ return chatWindow;
+}
+
+}
diff --git a/Swift/QtUI/QtChatWindowFactory.h b/Swift/QtUI/QtChatWindowFactory.h
new file mode 100644
index 0000000..9e5004e
--- /dev/null
+++ b/Swift/QtUI/QtChatWindowFactory.h
@@ -0,0 +1,18 @@
+#ifndef SWIFT_QtChatWindowFactory_H
+#define SWIFT_QtChatWindowFactory_H
+
+#include "Swiften/Controllers/ChatWindowFactory.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class QtTreeWidgetFactory;
+ class QtChatWindowFactory : public ChatWindowFactory {
+ public:
+ QtChatWindowFactory(QtTreeWidgetFactory *treeWidgetFactory);
+ ChatWindow* createChatWindow(const JID &contact);
+ private:
+ QtTreeWidgetFactory *treeWidgetFactory_;
+ };
+}
+
+#endif
diff --git a/Swift/QtUI/QtJoinMUCDialog.cpp b/Swift/QtUI/QtJoinMUCDialog.cpp
new file mode 100644
index 0000000..b6f5814
--- /dev/null
+++ b/Swift/QtUI/QtJoinMUCDialog.cpp
@@ -0,0 +1,32 @@
+#include "QtJoinMUCDialog.h"
+#include "QtSwiftUtil.h"
+
+namespace Swift {
+
+QtJoinMUCDialog::QtJoinMUCDialog(const QString& nick, const QString& muc, QWidget* parent) : QDialog(parent) {
+ setupUi(this);
+ errorLabel_->hide();
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ connect(buttons_, SIGNAL(accepted()), SLOT(accept()));
+ connect(buttons_, SIGNAL(rejected()), SLOT(reject()));
+}
+
+void QtJoinMUCDialog::accept() {
+ if (mucJID_->displayText().isEmpty()) {
+ showError("You must specify a room to join.");
+ return;
+ }
+ if (nick_->displayText().isEmpty()) {
+ showError("You must specify a nickname to join a room.");
+ return;
+ }
+ errorLabel_->hide();
+ emit onJoinCommand(JID(Q2PSTRING(mucJID_->displayText())), nick_->displayText());
+ QDialog::accept();
+}
+
+void QtJoinMUCDialog::showError(const QString& error) {
+ errorLabel_->setText(QString("<font color='red'>%1</font>").arg(error));
+ errorLabel_->show();
+}
+}
diff --git a/Swift/QtUI/QtJoinMUCDialog.h b/Swift/QtUI/QtJoinMUCDialog.h
new file mode 100644
index 0000000..9f1781d
--- /dev/null
+++ b/Swift/QtUI/QtJoinMUCDialog.h
@@ -0,0 +1,26 @@
+#ifndef SWIFT_QtJoinMUCDialog_H
+#define SWIFT_QtJoinMUCDialog_H
+
+#include "ui_QtJoinMUCDialog.h"
+#include "Swiften/JID/JID.h"
+
+#include <QDialog>
+
+namespace Swift {
+
+class QtJoinMUCDialog : public QDialog, private Ui::QtJoinMUCDialog {
+ Q_OBJECT
+
+ public:
+ QtJoinMUCDialog(const QString& muc, const QString& nick, QWidget* parent);
+ signals:
+ void onJoinCommand(const JID& muc, const QString& nick);
+ public slots:
+ void accept();
+ private:
+ void showError(const QString& error);
+};
+
+}
+
+#endif
diff --git a/Swift/QtUI/QtJoinMUCDialog.ui b/Swift/QtUI/QtJoinMUCDialog.ui
new file mode 100644
index 0000000..3b67c68
--- /dev/null
+++ b/Swift/QtUI/QtJoinMUCDialog.ui
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtJoinMUCDialog</class>
+ <widget class="QDialog" name="QtJoinMUCDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>232</width>
+ <height>110</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Join chatroom</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Chatroom</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="mucJID_"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Nickname</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="nick_"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="errorLabel_">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttons_">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ <property name="centerButtons">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp
new file mode 100644
index 0000000..a05d3bf
--- /dev/null
+++ b/Swift/QtUI/QtLoginWindow.cpp
@@ -0,0 +1,131 @@
+#include "QtLoginWindow.h"
+#include "QtSwiftUtil.h"
+#include "QtMainWindow.h"
+
+#include <QBoxLayout>
+#include <QFileDialog>
+#include <QStatusBar>
+#include <QToolButton>
+#include <QLabel>
+#include <QMenuBar>
+
+#include <cassert>
+
+namespace Swift{
+
+QtLoginWindow::QtLoginWindow(const String& defaultJID, const String& defaultPassword, const String& defaultCertificate) : QMainWindow() {
+ setWindowTitle("Swift");
+ resize(200, 500);
+ setContentsMargins(0,0,0,0);
+ QWidget *centralWidget = new QWidget(this);
+ setCentralWidget(centralWidget);
+ QBoxLayout *topLayout = new QBoxLayout(QBoxLayout::TopToBottom, centralWidget);
+ stack_ = new QStackedWidget(centralWidget);
+ topLayout->addWidget(stack_);
+ topLayout->setMargin(5);
+ QWidget *wrapperWidget = new QWidget(this);
+ wrapperWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
+ QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, wrapperWidget);
+ layout->addStretch();
+
+ QLabel* logo = new QLabel(this);
+ logo->setPixmap(QPixmap(":/logo-shaded-text.256.png"));
+ logo->setScaledContents(true);
+ logo->setFixedSize(192,192);
+ layout->addWidget(logo);
+ layout->addStretch();
+
+ username_ = new QLineEdit(this);
+ layout->addWidget(username_);
+
+ QWidget* w = new QWidget(this);
+ w->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
+ layout->addWidget(w);
+
+ QHBoxLayout* credentialsLayout = new QHBoxLayout(w);
+ credentialsLayout->setMargin(0);
+ credentialsLayout->setSpacing(3);
+ password_ = new QLineEdit(this);
+ password_->setEchoMode(QLineEdit::Password);
+ credentialsLayout->addWidget(password_);
+
+ certificateButton_ = new QToolButton(this);
+ certificateButton_->setCheckable(true);
+ certificateButton_->setIcon(QIcon(":/icons/certificate.png"));
+ certificateFile_ = P2QSTRING(defaultCertificate);
+ if (!certificateFile_.isEmpty()) {
+ certificateButton_->setChecked(true);
+ }
+ credentialsLayout->addWidget(certificateButton_);
+ connect(certificateButton_, SIGNAL(clicked(bool)), SLOT(handleCertficateChecked(bool)));
+
+ loginButton_ = new QPushButton(this);
+ loginButton_->setText(tr("Connect"));
+ loginButton_->setAutoDefault(true);
+ loginButton_->setDefault(true);
+ layout->addWidget(loginButton_);
+
+ username_->setText(P2QSTRING(defaultJID));
+ password_->setText(P2QSTRING(defaultPassword));
+
+ message_ = new QLabel(this);
+ message_->setTextFormat(Qt::RichText);
+ message_->setWordWrap(true);
+ layout->addWidget(message_);
+
+ layout->addStretch();
+ remember_ = new QCheckBox(tr("Remember Password?"), this);
+ remember_->setChecked(defaultPassword != "");
+ layout->addWidget(remember_);
+ connect(loginButton_, SIGNAL(clicked()), SLOT(loginClicked()));
+ stack_->addWidget(wrapperWidget);
+ this->show();
+}
+
+void QtLoginWindow::loggedOut() {
+ if (stack_->count() > 1) {
+ QWidget* current = stack_->currentWidget();
+ stack_->setCurrentIndex(0);
+ stack_->removeWidget(current);
+ }
+ setEnabled(true);
+}
+
+void QtLoginWindow::loginClicked() {
+ setEnabled(false);
+ onLoginRequest(Q2PSTRING(username_->text()), Q2PSTRING(password_->text()), Q2PSTRING(certificateFile_), remember_->isChecked());
+}
+
+void QtLoginWindow::handleCertficateChecked(bool checked) {
+ if (checked) {
+ certificateFile_ = QFileDialog::getOpenFileName(this, "Select an authentication certificate", QString(), QString("*.cert"));
+ if (certificateFile_.isEmpty()) {
+ certificateButton_->setChecked(false);
+ }
+ }
+ else {
+ certificateFile_ = "";
+ }
+}
+
+void QtLoginWindow::morphInto(MainWindow *mainWindow) {
+ QtMainWindow *qtMainWindow = dynamic_cast<QtMainWindow*>(mainWindow);
+ assert(qtMainWindow);
+ stack_->addWidget(qtMainWindow);
+ stack_->setCurrentWidget(qtMainWindow);
+ setEnabled(true);
+ foreach (QMenu* menu, qtMainWindow->getMenus()) {
+ menuBar()->addMenu(menu);
+ }
+}
+
+void QtLoginWindow::setMessage(const String& message) {
+ if (!message.isEmpty()) {
+ message_->setText("<center><font color=\"red\">" + P2QSTRING(message) + "</font></center>");
+ }
+ else {
+ message_->setText("");
+ }
+}
+
+}
diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h
new file mode 100644
index 0000000..5a14202
--- /dev/null
+++ b/Swift/QtUI/QtLoginWindow.h
@@ -0,0 +1,42 @@
+#ifndef SWIFT_QtLoginWindow_H
+#define SWIFT_QtLoginWindow_H
+
+#include <QMainWindow>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QCheckBox>
+#include <QStackedWidget>
+
+#include "Swiften/Controllers/LoginWindow.h"
+#include "Swiften/Controllers/MainWindow.h"
+
+class QLabel;
+class QToolButton;
+
+namespace Swift {
+ class QtLoginWindow : public QMainWindow, public LoginWindow {
+ Q_OBJECT
+ public:
+ QtLoginWindow(const String& defaultJID, const String& defaultPassword, const String& defaultCertificate);
+
+ void morphInto(MainWindow *mainWindow);
+ virtual void loggedOut();
+ virtual void setMessage(const String& message);
+
+ private slots:
+ void loginClicked();
+ void handleCertficateChecked(bool);
+
+ private:
+ QLineEdit *username_;
+ QLineEdit *password_;
+ QPushButton *loginButton_;
+ QCheckBox *remember_;
+ QStackedWidget *stack_;
+ QLabel* message_;
+ QString certificateFile_;
+ QToolButton* certificateButton_;
+ };
+}
+
+#endif
diff --git a/Swift/QtUI/QtLoginWindowFactory.cpp b/Swift/QtUI/QtLoginWindowFactory.cpp
new file mode 100644
index 0000000..4874828
--- /dev/null
+++ b/Swift/QtUI/QtLoginWindowFactory.cpp
@@ -0,0 +1,8 @@
+#include "QtLoginWindowFactory.h"
+#include "QtLoginWindow.h"
+
+namespace Swift {
+LoginWindow* QtLoginWindowFactory::createLoginWindow(const String& defaultJID, const String& defaultPassword, const String& defaultCertificate) {
+ return new QtLoginWindow(defaultJID, defaultPassword, defaultCertificate);
+}
+}
diff --git a/Swift/QtUI/QtLoginWindowFactory.h b/Swift/QtUI/QtLoginWindowFactory.h
new file mode 100644
index 0000000..52f92c8
--- /dev/null
+++ b/Swift/QtUI/QtLoginWindowFactory.h
@@ -0,0 +1,12 @@
+#ifndef SWIFT_QtLoginWindowFactory_H
+#define SWIFT_QtLoginWindowFactory_H
+
+#include "Swiften/Controllers/LoginWindowFactory.h"
+
+namespace Swift {
+ class QtLoginWindowFactory : public LoginWindowFactory{
+ LoginWindow* createLoginWindow(const String& defaultJID, const String& defaultPassword, const String& defaultCertificate);
+ };
+}
+
+#endif
diff --git a/Swift/QtUI/QtMainEventLoop.h b/Swift/QtUI/QtMainEventLoop.h
new file mode 100644
index 0000000..678ea04
--- /dev/null
+++ b/Swift/QtUI/QtMainEventLoop.h
@@ -0,0 +1,40 @@
+#ifndef SWIFT_QtMainEventLoop_H
+#define SWIFT_QtMainEventLoop_H
+
+#include <QObject>
+#include <QEvent>
+#include <QCoreApplication>
+
+#include "Swiften/EventLoop/EventLoop.h"
+
+class QtMainEventLoop : public QObject, public Swift::EventLoop
+{
+ public:
+ QtMainEventLoop() {}
+
+ virtual void post(const Event& event) {
+ QCoreApplication::postEvent(this, new Event(event));
+ }
+
+ virtual bool event(QEvent* qevent) {
+ Event* event = dynamic_cast<Event*>(qevent);
+ if (event) {
+ handleEvent(event->event_);
+ //event->deleteLater(); FIXME: Leak?
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ struct Event : public QEvent {
+ Event(const EventLoop::Event& event) :
+ QEvent(QEvent::User), event_(event) {
+ }
+
+ EventLoop::Event event_;
+ };
+};
+
+#endif
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
new file mode 100644
index 0000000..a9ffc51
--- /dev/null
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -0,0 +1,71 @@
+#include "QtMainWindow.h"
+
+#include "QtJoinMUCDialog.h"
+#include "QtSwiftUtil.h"
+#include "QtTreeWidgetFactory.h"
+#include "QtTreeWidget.h"
+#include "QtStatusWidget.h"
+
+#include <QBoxLayout>
+#include <QComboBox>
+#include <QLineEdit>
+#include <QListWidget>
+#include <QListWidgetItem>
+#include <QPushButton>
+#include <QMenuBar>
+#include <QToolBar>
+
+namespace Swift {
+
+QtMainWindow::QtMainWindow(QtTreeWidgetFactory *treeWidgetFactory) : QWidget() {
+ setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
+ QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);
+ mainLayout->setContentsMargins(0,0,0,0);
+ mainLayout->setSpacing(0);
+ statusWidget_ = new QtStatusWidget(this);
+ connect(statusWidget_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleStatusChanged(StatusShow::Type, const QString&)));
+ mainLayout->addWidget(statusWidget_);
+ treeWidget_ = dynamic_cast<QtTreeWidget*>(treeWidgetFactory->createTreeWidget());
+ mainLayout->addWidget(treeWidget_);
+
+ this->setLayout(mainLayout);
+
+ QMenu* viewMenu = new QMenu(tr("View"), this);
+ menus_.push_back(viewMenu);
+ QAction* showOfflineAction = new QAction("Show offline contacts", this);
+ showOfflineAction->setCheckable(true);
+ showOfflineAction->setChecked(false);
+ connect(showOfflineAction, SIGNAL(toggled(bool)), SLOT(handleShowOfflineToggled(bool)));
+ viewMenu->addAction(showOfflineAction);
+
+ QMenu* chatMenu = new QMenu(tr("Chat"), this);
+ menus_.push_back(chatMenu);
+ QAction* joinMUCAction = new QAction("Join chatroom", this);
+ connect(joinMUCAction, SIGNAL(triggered()), SLOT(handleJoinMUCAction()));
+ chatMenu->addAction(joinMUCAction);
+}
+
+TreeWidget* QtMainWindow::getTreeWidget() {
+ return treeWidget_;
+}
+
+void QtMainWindow::handleJoinMUCAction() {
+ QtJoinMUCDialog* joinMUC = new QtJoinMUCDialog("jabber@conference.jabber.org", "SwiftUser", this);
+ connect(joinMUC, SIGNAL(onJoinCommand(const JID&, const QString&)), SLOT(handleJoinMUCDialogComplete(const JID&, const QString&)));
+ joinMUC->show();
+}
+
+void QtMainWindow::handleJoinMUCDialogComplete(const JID& muc, const QString& nick) {
+ onJoinMUCRequest(muc, Q2PSTRING(nick));
+}
+
+void QtMainWindow::handleStatusChanged(StatusShow::Type showType, const QString &statusMessage) {
+ onChangeStatusRequest(showType, Q2PSTRING(statusMessage));
+}
+
+void QtMainWindow::handleShowOfflineToggled(bool state) {
+ onShowOfflineToggled(state);
+}
+
+}
+
diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h
new file mode 100644
index 0000000..fe5c8b4
--- /dev/null
+++ b/Swift/QtUI/QtMainWindow.h
@@ -0,0 +1,43 @@
+#ifndef SWIFT_QtMainWindow_H
+#define SWIFT_QtMainWindow_H
+
+#include <QWidget>
+#include <QMenu>
+#include "Swiften/Controllers/MainWindow.h"
+
+#include <vector>
+
+class QComboBox;
+class QLineEdit;
+class QPushButton;
+
+namespace Swift {
+ class QtTreeWidget;
+ class QtTreeWidgetFactory;
+ class QtStatusWidget;
+ class TreeWidget;
+
+
+ class QtMainWindow : public QWidget, public MainWindow {
+ Q_OBJECT
+ public:
+ QtMainWindow(QtTreeWidgetFactory *treeWidgetFactory);
+ TreeWidget* getTreeWidget();
+ std::vector<QMenu*> getMenus() {return menus_;}
+ private slots:
+ void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage);
+ void handleShowOfflineToggled(bool);
+ void handleJoinMUCAction();
+ void handleJoinMUCDialogComplete(const JID& muc, const QString& nick);
+ private:
+ std::vector<QMenu*> menus_;
+ QtStatusWidget *statusWidget_;
+ QLineEdit *muc_;
+ QLineEdit *mucNick_;
+ QPushButton *mucButton_;
+ QtTreeWidget *treeWidget_;
+ };
+}
+
+#endif
+
diff --git a/Swift/QtUI/QtMainWindowFactory.cpp b/Swift/QtUI/QtMainWindowFactory.cpp
new file mode 100644
index 0000000..1fa7d75
--- /dev/null
+++ b/Swift/QtUI/QtMainWindowFactory.cpp
@@ -0,0 +1,15 @@
+#include "QtMainWindowFactory.h"
+#include "QtMainWindow.h"
+#include "QtTreeWidgetFactory.h"
+
+namespace Swift {
+
+QtMainWindowFactory::QtMainWindowFactory(QtTreeWidgetFactory *treeWidgetFactory) : treeWidgetFactory_(treeWidgetFactory) {
+
+}
+
+MainWindow* QtMainWindowFactory::createMainWindow() {
+ return new QtMainWindow(treeWidgetFactory_);
+}
+
+}
diff --git a/Swift/QtUI/QtMainWindowFactory.h b/Swift/QtUI/QtMainWindowFactory.h
new file mode 100644
index 0000000..86fc02f
--- /dev/null
+++ b/Swift/QtUI/QtMainWindowFactory.h
@@ -0,0 +1,17 @@
+#ifndef SWIFT_QtMainWindowFactory_H
+#define SWIFT_QtMainWindowFactory_H
+
+#include "Swiften/Controllers/MainWindowFactory.h"
+
+namespace Swift {
+ class QtTreeWidgetFactory;
+ class QtMainWindowFactory : public MainWindowFactory{
+ public:
+ QtMainWindowFactory(QtTreeWidgetFactory *treeWidgetFactory);
+ MainWindow* createMainWindow();
+ private:
+ QtTreeWidgetFactory *treeWidgetFactory_;
+ };
+}
+
+#endif
diff --git a/Swift/QtUI/QtSettingsProvider.cpp b/Swift/QtUI/QtSettingsProvider.cpp
new file mode 100644
index 0000000..42540c1
--- /dev/null
+++ b/Swift/QtUI/QtSettingsProvider.cpp
@@ -0,0 +1,23 @@
+#include "QtSettingsProvider.h"
+#include "QtSwiftUtil.h"
+
+namespace Swift {
+
+QtSettingsProvider::QtSettingsProvider() {
+}
+
+QtSettingsProvider::~QtSettingsProvider() {
+
+}
+
+String QtSettingsProvider::getStringSetting(const String &settingPath) {
+ QVariant setting = settings_.value(P2QSTRING(settingPath));
+ return setting.isNull() ? "" : Q2PSTRING(setting.toString());
+}
+
+void QtSettingsProvider::storeString(const String &settingPath, const String &settingValue) {
+ settings_.setValue(P2QSTRING(settingPath), P2QSTRING(settingValue));
+}
+
+}
+
diff --git a/Swift/QtUI/QtSettingsProvider.h b/Swift/QtUI/QtSettingsProvider.h
new file mode 100644
index 0000000..4739f60
--- /dev/null
+++ b/Swift/QtUI/QtSettingsProvider.h
@@ -0,0 +1,24 @@
+#ifndef SWIFT_QtSettingsProvider_H
+#define SWIFT_QtSettingsProvider_H
+
+#include "Swiften/Settings/SettingsProvider.h"
+
+#include <QSettings>
+
+namespace Swift {
+
+class QtSettingsProvider : public SettingsProvider {
+ public:
+ QtSettingsProvider();
+ virtual ~QtSettingsProvider();
+ virtual String getStringSetting(const String &settingPath);
+ virtual void storeString(const String &settingPath, const String &settingValue);
+ private:
+ QSettings settings_;
+};
+
+}
+#endif
+
+
+
diff --git a/Swift/QtUI/QtStatusWidget.cpp b/Swift/QtUI/QtStatusWidget.cpp
new file mode 100644
index 0000000..53f93b5
--- /dev/null
+++ b/Swift/QtUI/QtStatusWidget.cpp
@@ -0,0 +1,32 @@
+#include "QtStatusWidget.h"
+
+#include <QBoxLayout>
+#include <QComboBox>
+#include <QLineEdit>
+
+
+namespace Swift {
+QtStatusWidget::QtStatusWidget(QWidget *parent) {
+ types_ = new QComboBox(this);
+ types_->addItem("Available", QVariant(StatusShow::Online));
+ types_->addItem("Free For Chat", QVariant(StatusShow::FFC));
+ types_->addItem("Away", QVariant(StatusShow::Away));
+ types_->addItem("Extended Away", QVariant(StatusShow::XA));
+ types_->addItem("Do Not Disturb", QVariant(StatusShow::DND));
+ types_->addItem("Offline", QVariant(StatusShow::None));
+ connect(types_, SIGNAL(activated(int)), this, SLOT(handleTypeSelected(int)));
+ QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);
+ mainLayout->setContentsMargins(0,0,0,0);
+ mainLayout->setSpacing(0);
+ mainLayout->addWidget(types_);
+}
+
+void QtStatusWidget::handleTypeSelected(int index) {
+ emit onChangeStatusRequest((StatusShow::Type)types_->itemData(index).toInt(), "");
+}
+
+}
+
+
+
+
diff --git a/Swift/QtUI/QtStatusWidget.h b/Swift/QtUI/QtStatusWidget.h
new file mode 100644
index 0000000..abd352f
--- /dev/null
+++ b/Swift/QtUI/QtStatusWidget.h
@@ -0,0 +1,28 @@
+#ifndef SWIFT_QtStatusWidget_H
+#define SWIFT_QtStatusWidget_H
+
+#include "Swiften/Elements/StatusShow.h"
+
+#include <QWidget>
+
+class QComboBox;
+class QLineEdit;
+
+namespace Swift {
+ class QtStatusWidget : public QWidget {
+ Q_OBJECT
+ public:
+ QtStatusWidget(QWidget *parent);
+ signals:
+ void onChangeStatusRequest(StatusShow::Type showType, const QString &statusMessage);
+ private slots:
+ void handleTypeSelected(int index);
+ private:
+ QComboBox *types_;
+ QLineEdit *message_;
+ };
+}
+
+#endif
+
+
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
new file mode 100644
index 0000000..e7d4152
--- /dev/null
+++ b/Swift/QtUI/QtSwift.cpp
@@ -0,0 +1,43 @@
+#include "QtSwift.h"
+
+#include "QtLoginWindowFactory.h"
+#include "QtChatWindowFactory.h"
+#include "QtMainWindowFactory.h"
+#include "QtTreeWidgetFactory.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Application/Application.h"
+#include "Swiften/Application/Platform/PlatformApplication.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Client/Client.h"
+#include "Swiften/Controllers/ChatController.h"
+#include "Swiften/Controllers/MainController.h"
+
+namespace Swift{
+
+QtSwift::QtSwift() :
+ treeWidgetFactory_(new QtTreeWidgetFactory()),
+ chatWindowFactory_(new QtChatWindowFactory(treeWidgetFactory_)),
+ rosterWindowFactory_(new QtMainWindowFactory(treeWidgetFactory_)),
+ loginWindowFactory_(new QtLoginWindowFactory()) {
+ QCoreApplication::setApplicationName("Swift");
+ QCoreApplication::setOrganizationName("Swift");
+ QCoreApplication::setOrganizationDomain("swift.im");
+ settings_ = new QtSettingsProvider();
+ application_ = new PlatformApplication("Swift");
+ mainController_ = new MainController(chatWindowFactory_, rosterWindowFactory_, loginWindowFactory_, treeWidgetFactory_, settings_, application_);
+}
+
+QtSwift::~QtSwift() {
+ delete chatWindowFactory_;
+ delete rosterWindowFactory_;
+ delete loginWindowFactory_;
+ delete treeWidgetFactory_;
+ delete mainController_;
+ delete settings_;
+ delete application_;
+}
+
+}
diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h
new file mode 100644
index 0000000..5bfd62c
--- /dev/null
+++ b/Swift/QtUI/QtSwift.h
@@ -0,0 +1,36 @@
+#ifndef SWIFT_QtSwift_H
+#define SWIFT_QtSwift_H
+
+#include "Swiften/Base/String.h"
+#include "QtMainEventLoop.h"
+#include "QtLoginWindowFactory.h"
+#include "QtMainWindowFactory.h"
+#include "QtChatWindowFactory.h"
+#include "QtSettingsProvider.h"
+
+namespace Swift {
+ class Application;
+ class MainController;
+ class QtChatWindowFactory;
+ class QtMainWindowFactory;
+ class QtLoginWindowFactory;
+ class QtTreeWidgetFactory;
+
+ class QtSwift : public QObject {
+ Q_OBJECT
+ public:
+ QtSwift();
+ ~QtSwift();
+ private:
+ MainController *mainController_;
+ QtTreeWidgetFactory *treeWidgetFactory_;
+ QtChatWindowFactory *chatWindowFactory_;
+ QtMainWindowFactory *rosterWindowFactory_;
+ QtLoginWindowFactory *loginWindowFactory_;
+ QtMainEventLoop clientMainThreadCaller_;
+ QtSettingsProvider *settings_;
+ Application* application_;
+ };
+}
+
+#endif
diff --git a/Swift/QtUI/QtSwiftUtil.h b/Swift/QtUI/QtSwiftUtil.h
new file mode 100644
index 0000000..9a3feca
--- /dev/null
+++ b/Swift/QtUI/QtSwiftUtil.h
@@ -0,0 +1,7 @@
+#ifndef SWIFT_QtSwiftUtil_H
+#define SWIFT_QtSwiftUtil_H
+
+#define P2QSTRING(a) QString::fromUtf8(a.getUTF8Data())
+#define Q2PSTRING(a) Swift::String(a.toUtf8())
+
+#endif
diff --git a/Swift/QtUI/QtTreeWidget.cpp b/Swift/QtUI/QtTreeWidget.cpp
new file mode 100644
index 0000000..66c653e
--- /dev/null
+++ b/Swift/QtUI/QtTreeWidget.cpp
@@ -0,0 +1,29 @@
+#include "QtTreeWidget.h"
+
+#include "Swiften/Base/Platform.h"
+#include "Swiften/Roster/OpenChatRosterAction.h"
+
+namespace Swift {
+
+QtTreeWidget::QtTreeWidget(QWidget* parent) : QTreeWidget(parent) {
+ setHeaderHidden(true);
+#ifdef SWIFT_PLATFORM_MACOSX
+ setAlternatingRowColors(true);
+#endif
+ setAnimated(true);
+ setIndentation(0);
+ setRootIsDecorated(true);
+ connect(this, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(handleItemActivated(QTreeWidgetItem*, int)));
+}
+
+void QtTreeWidget::handleItemActivated(QTreeWidgetItem* item, int column) {
+ QtTreeWidgetItem* qtItem = dynamic_cast<QtTreeWidgetItem*>(item);
+ if (qtItem) {
+ qtItem->performUserAction(boost::shared_ptr<UserRosterAction>(new OpenChatRosterAction()));
+ }
+}
+
+void QtTreeWidget::drawBranches(QPainter*, const QRect&, const QModelIndex&) const {
+}
+
+}
diff --git a/Swift/QtUI/QtTreeWidget.h b/Swift/QtUI/QtTreeWidget.h
new file mode 100644
index 0000000..e1d83de
--- /dev/null
+++ b/Swift/QtUI/QtTreeWidget.h
@@ -0,0 +1,28 @@
+#ifndef SWIFT_QtTreeWidget_H
+#define SWIFT_QtTreeWidget_H
+
+#include <QTreeWidget>
+
+#include "Swiften/Roster/TreeWidgetFactory.h"
+#include "Swiften/Roster/TreeWidget.h"
+#include "Swiften/Roster/TreeWidgetItem.h"
+#include "Swift/QtUI/QtTreeWidgetItem.h"
+#include "Swift/QtUI/QtTreeWidget.h"
+
+namespace Swift {
+
+class QtTreeWidget : public QTreeWidget, public TreeWidget {
+ Q_OBJECT
+ public:
+ QtTreeWidget(QWidget* parent = 0);
+
+ private slots:
+ void handleItemActivated(QTreeWidgetItem*, int);
+
+ private:
+ void drawBranches(QPainter*, const QRect&, const QModelIndex&) const;
+};
+
+}
+#endif
+
diff --git a/Swift/QtUI/QtTreeWidgetFactory.cpp b/Swift/QtUI/QtTreeWidgetFactory.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Swift/QtUI/QtTreeWidgetFactory.cpp
diff --git a/Swift/QtUI/QtTreeWidgetFactory.h b/Swift/QtUI/QtTreeWidgetFactory.h
new file mode 100644
index 0000000..e0140d2
--- /dev/null
+++ b/Swift/QtUI/QtTreeWidgetFactory.h
@@ -0,0 +1,36 @@
+#ifndef SWIFT_QtTreeWidgetFactory_H
+#define SWIFT_QtTreeWidgetFactory_H
+
+#include "Swiften/Roster/TreeWidgetFactory.h"
+#include "Swiften/Roster/TreeWidget.h"
+#include "Swiften/Roster/TreeWidgetItem.h"
+#include "Swift/QtUI/QtTreeWidgetItem.h"
+#include "Swift/QtUI/QtTreeWidget.h"
+
+namespace Swift {
+
+class QtTreeWidgetFactory : public TreeWidgetFactory {
+ public:
+ QtTreeWidgetFactory() {
+ }
+
+ TreeWidget* createTreeWidget() {
+ return new QtTreeWidget();
+ }
+
+ TreeWidgetItem* createTreeWidgetItem(TreeWidgetItem* item) {
+ QtTreeWidgetItem* qtItem = dynamic_cast<QtTreeWidgetItem*>(item);
+ assert(qtItem);
+ return new QtTreeWidgetItem(qtItem);
+ }
+
+ TreeWidgetItem* createTreeWidgetItem(TreeWidget* item) {
+ QtTreeWidget* qtItem = dynamic_cast<QtTreeWidget*>(item);
+ assert(qtItem);
+ return new QtTreeWidgetItem(qtItem);
+ }
+};
+
+}
+#endif
+
diff --git a/Swift/QtUI/QtTreeWidgetItem.cpp b/Swift/QtUI/QtTreeWidgetItem.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Swift/QtUI/QtTreeWidgetItem.cpp
diff --git a/Swift/QtUI/QtTreeWidgetItem.h b/Swift/QtUI/QtTreeWidgetItem.h
new file mode 100644
index 0000000..34ad93c
--- /dev/null
+++ b/Swift/QtUI/QtTreeWidgetItem.h
@@ -0,0 +1,57 @@
+#ifndef SWIFT_QtTreeWidgetItem_H
+#define SWIFT_QtTreeWidgetItem_H
+
+#include <QColor>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+#include "Swiften/Roster/TreeWidget.h"
+#include "Swiften/Roster/TreeWidgetItem.h"
+#include "Swift/QtUI/QtTreeWidgetItem.h"
+#include "Swift/QtUI/QtTreeWidget.h"
+#include "Swift/QtUI/QtSwiftUtil.h"
+
+namespace Swift {
+
+class QtTreeWidgetItem : public QTreeWidgetItem, public TreeWidgetItem {
+ public:
+ QtTreeWidgetItem(QTreeWidget* parent) : QTreeWidgetItem(parent) {
+ }
+
+ QtTreeWidgetItem(QTreeWidgetItem* parent) : QTreeWidgetItem(parent) {
+ }
+
+ void setText(const String& text) {
+ QTreeWidgetItem::setText(0, P2QSTRING(text));
+ }
+
+ void setTextColor(unsigned long color) {
+ QTreeWidgetItem::setTextColor(0, QColor(
+ ((color & 0xFF0000)>>16),
+ ((color & 0xFF00)>>8),
+ (color & 0xFF)));
+ }
+
+ void setBackgroundColor(unsigned long color) {
+ QTreeWidgetItem::setBackgroundColor(0, QColor(
+ ((color & 0xFF0000)>>16),
+ ((color & 0xFF00)>>8),
+ (color & 0xFF)));
+ }
+
+ void setExpanded(bool b) {
+ treeWidget()->setItemExpanded(this, b);
+ }
+
+ void hide() {
+ setHidden(true);
+ }
+
+ void show() {
+ setHidden(false);
+ }
+};
+
+}
+#endif
+
diff --git a/Swift/QtUI/Swift.pro b/Swift/QtUI/Swift.pro
new file mode 100644
index 0000000..16db0d1
--- /dev/null
+++ b/Swift/QtUI/Swift.pro
@@ -0,0 +1,92 @@
+TEMPLATE = app
+QT += webkit
+CONFIG += debug
+unix:!mac {
+ TARGET = swift
+}
+else {
+ TARGET = Swift
+}
+
+win32 {
+ CONFIG += console
+
+ # Configuration
+ HAVE_EXPAT=yes
+ USE_BUNDLED_EXPAT=yes
+ DEFINES += HAVE_EXPAT
+
+ HAVE_OPENSSL=yes
+ DEFINES += HAVE_OPENSSL
+ INCLUDEPATH += F:/OpenSSL/include
+ LIBS += -LF:/OpenSSL/lib/VC -llibeay32MT -lssleay32MT
+
+ include(Swiften.pri)
+ LIBS += -ldnsapi -lws2_32 -lwsock32
+}
+else {
+ DEPENDPATH += . ../.. ../../3rdParty/Boost
+ INCLUDEPATH += . ../.. ../../3rdParty/Boost
+ LIBS += ../../Swiften/Swiften.a -lexpat -lssl -lcrypto
+ unix {
+ LIBS += -lresolv
+ }
+}
+
+# Resources
+win32 {
+ RC_FILE = ../../resources/Windows/Swift.rc
+}
+mac {
+ ICON = ../../resources/MacOSX/Swift.icns
+}
+
+DEFINES += BOOST_SIGNALS_NAMESPACE=bsignals BOOST_ALL_NO_LIB
+
+HEADERS += \
+ QtChatWindow.h \
+ QtChatWindowFactory.h \
+ QtJoinMUCDialog.h \
+ QtLoginWindow.h \
+ QtLoginWindowFactory.h \
+ QtMainEventLoop.h \
+ QtMainWindow.h \
+ QtMainWindowFactory.h \
+ QtSettingsProvider.h \
+ QtStatusWidget.h \
+ QtSwift.h \
+ QtTreeWidget.h \
+ QtTreeWidgetFactory.h \
+ QtTreeWidgetItem.h \
+ QtChatView.h \
+ ChatSnippet.h \
+ MessageSnippet.h \
+ SystemMessageSnippet.h
+
+SOURCES += \
+ main.cpp \
+ QtChatWindow.cpp \
+ QtChatWindowFactory.cpp \
+ QtJoinMUCDialog.cpp \
+ QtLoginWindow.cpp \
+ QtLoginWindowFactory.cpp \
+ QtMainWindow.cpp \
+ QtMainWindowFactory.cpp \
+ QtSettingsProvider.cpp \
+ QtStatusWidget.cpp \
+ QtSwift.cpp \
+ QtTreeWidget.cpp \
+ QtChatView.cpp \
+ ChatSnippet.cpp \
+ MessageSnippet.cpp \
+ SystemMessageSnippet.cpp
+
+FORMS += QtJoinMUCDialog.ui
+
+RESOURCES += Swift.qrc DefaultTheme.qrc
+
+win32 {
+ DefaultThemeQRC.target = DefaultTheme.qrc
+ DefaultThemeQRC.commands = ..\..\tools\ThemeQRC.py ../../resources/themes/Default > DefaultTheme.qrc
+ QMAKE_EXTRA_TARGETS = DefaultThemeQRC
+}
diff --git a/Swift/QtUI/Swift.qrc b/Swift/QtUI/Swift.qrc
new file mode 100644
index 0000000..2db382e
--- /dev/null
+++ b/Swift/QtUI/Swift.qrc
@@ -0,0 +1,9 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file alias="logo-shaded-text.256.png">../../resources/logo/logo-shaded-text.256.png</file>
+ <file alias="icons/certificate.png">../../resources/icons/certificate.png</file>
+ <file alias="icons/error.png">../../resources/icons/error.png</file>
+ <file alias="icons/avatar.png">../../resources/icons/avatar.png</file>
+ </qresource>
+</RCC>
diff --git a/Swift/QtUI/SystemMessageSnippet.cpp b/Swift/QtUI/SystemMessageSnippet.cpp
new file mode 100644
index 0000000..d8feddc
--- /dev/null
+++ b/Swift/QtUI/SystemMessageSnippet.cpp
@@ -0,0 +1,15 @@
+#include "SystemMessageSnippet.h"
+
+#include <QDateTime>
+
+namespace Swift {
+
+SystemMessageSnippet::SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious) : ChatSnippet(appendToPrevious) {
+ content_ = loadTemplate(":/themes/Default/Status.html");
+
+ content_.replace("%message%", escape(message));
+ content_.replace("%shortTime%", escape(time.toString("h:mm")));
+ content_.replace("%time%", escape(time.toString("h:mm")));
+}
+
+}
diff --git a/Swift/QtUI/SystemMessageSnippet.h b/Swift/QtUI/SystemMessageSnippet.h
new file mode 100644
index 0000000..34cd780
--- /dev/null
+++ b/Swift/QtUI/SystemMessageSnippet.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <QString>
+
+#include "ChatSnippet.h"
+
+class QDateTime;
+
+namespace Swift {
+ class SystemMessageSnippet : public ChatSnippet {
+ public:
+ SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious);
+
+ const QString& getContent() const {
+ return content_;
+ }
+
+ /*QString getContinuationElementID() const {
+ return "insert";
+ };*/
+
+ private:
+ QString content_;
+ };
+}
diff --git a/Swift/QtUI/main.cpp b/Swift/QtUI/main.cpp
new file mode 100644
index 0000000..7a438fd
--- /dev/null
+++ b/Swift/QtUI/main.cpp
@@ -0,0 +1,10 @@
+#include <QApplication>
+
+#include "QtSwift.h"
+
+int main(int argc, char* argv[])
+{
+ QApplication app(argc, argv);
+ Swift::QtSwift swift;
+ return app.exec();
+}
diff --git a/Swift/QtUI/qmakeish.py b/Swift/QtUI/qmakeish.py
new file mode 100755
index 0000000..72a5f62
--- /dev/null
+++ b/Swift/QtUI/qmakeish.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# Run this from the Swift/QtUI dir with:
+# ./qmakeish.py ../../Makefile > Swiften.pri
+
+import sys, re, os.path
+
+def processSourcesLine(line) :
+ strippedLine = line.rstrip("\n")
+ sourceFile = re.sub("\\\\$", "", strippedLine).strip()
+ if len(sourceFile) > 0 :
+ print "SOURCES += $$PWD/../../" + sourceFile
+ return strippedLine.endswith("\\")
+
+def processFlags(name, flags) :
+ flags = flags.replace("-isystem ", "-I")
+ for flag in flags.split(" ") :
+ if flag.startswith("-D") :
+ print "DEFINES += " + flag[2:]
+ elif flag.startswith("-I") :
+ print "INCLUDEPATH += $$PWD/../../" + flag[2:]
+ elif len(flag) > 0 :
+ print name + " += " + flag
+
+
+assert(len(sys.argv) == 2)
+
+basedir = os.path.dirname(sys.argv[1])
+
+# Flatten the makefile
+makefile = []
+files = [open(sys.argv[1])]
+while len(files) > 0 :
+ file = files[-1]
+ line = file.readline()
+ if line :
+ match = re.match("include (.*)", line)
+ if match and match.group(1) != "Makefile.config" :
+ files.append(open(os.path.join(basedir, match.group(1))))
+ makefile.append("## Begin File: " + match.group(1))
+ else :
+ makefile.append(line)
+ else :
+ makefile.append("## End file")
+ file.close()
+ files.pop()
+
+# Process makefile
+inSources = False
+for line in makefile :
+ if inSources :
+ inSources = processSourcesLine(line)
+ else :
+ # Conditional
+ match = re.match("if(n?)eq \(\$\((.*)\),(.*)\)", line)
+ if match :
+ conditional = match.group(2)
+ if conditional == "WIN32" :
+ conditional = "win32"
+ elif conditional == "MACOSX" :
+ conditional = "mac"
+ elif match.group(2).startswith("HAVE_") or match.group(2).startswith("USE_") :
+ conditional = "!isEmpty(" + match.group(2) + ")"
+ else :
+ conditional = "DUMMY"
+ if (match.group(1) == "n") ^ (match.group(3) not in ["1", "yes"]) :
+ conditional = "!" + conditional
+ print conditional + " {"
+ continue
+ if re.match("^if(n?)def", line) :
+ print "DUMMY {"
+ continue
+ elif re.match("^if(n?)eq", line) :
+ print "DUMMY {"
+ continue
+ if re.match("^else$", line) :
+ print "} else {"
+ continue
+ if re.match("^endif$", line) :
+ print "}"
+ continue
+
+ match = re.match("(\w+)_SOURCES (\+?)= (.*)", line)
+ if match and match.group(1) in ["SWIFTEN", "ZLIB", "LIBIDN", "BOOST", "EXPAT"] :
+ inSources = processSourcesLine(match.group(3))
+ continue
+
+ match = re.match("(LIBS|CXXFLAGS|CPPFLAGS|CFLAGS) \+= (.*)", line)
+ if match :
+ processFlags(match.group(1), match.group(2))
+
+ if line.startswith("## ") :
+ print line
+
+"""
+#print sourceFiles
+sys.exit(0)
+
+print files
+pro = open ('swiftall.pri', 'w')
+for sourceType in files.keys():
+ pro.write("%s += \\\n" % sourceType)
+ for sourceFile in files[sourceType]:
+ pro.write("$$PWD/Swift/%s \\\n" % sourceFile)
+ pro.write("\n")
+pro.close()
+
+"""