diff options
Diffstat (limited to 'Swift/QtUI')
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%", "%message%"); + result.replace("%sender%", "%sender%"); + result.replace("%time%", "%%time%"); + result.replace("%shortTime%", "%%shortTime%"); + result.replace("%userIconPath%", "%userIconPath%"); + 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(¬ificationClicked, "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() + +""" |