summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThilo Cestonaro <thilo@cestona.ro>2012-09-02 20:27:42 (GMT)
committerThilo Cestonaro <thilo@cestona.ro>2012-09-02 20:32:56 (GMT)
commitc889fadf1dede0955ce74ce6897094e4f0c3e341 (patch)
treebe6a461a337fa9f46d9d9ac5e973d4e035c67a46
parent6856199274e9c5e581220fccf520b8f011519d17 (diff)
downloadswift-contrib-ephraim/chatview.zip
swift-contrib-ephraim/chatview.tar.bz2
moved QtChatWindow Log stuff into QtWebKitChatViewephraim/chatview
added QtChatViewFactory started implementation of QtBarriersFreeChatView License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php
-rwxr-xr-xBuildTools/Cppcheck.sh2
-rw-r--r--Swift/QtUI/QtBarriersFreeChatView.cpp128
-rw-r--r--Swift/QtUI/QtBarriersFreeChatView.h54
-rw-r--r--Swift/QtUI/QtChatView.cpp380
-rw-r--r--Swift/QtUI/QtChatView.h115
-rw-r--r--Swift/QtUI/QtChatViewFactory.cpp30
-rw-r--r--Swift/QtUI/QtChatViewFactory.h24
-rw-r--r--Swift/QtUI/QtChatWindow.cpp334
-rw-r--r--Swift/QtUI/QtChatWindow.h49
-rw-r--r--Swift/QtUI/QtSwift.cpp1
-rw-r--r--Swift/QtUI/QtUISettingConstants.cpp7
-rw-r--r--Swift/QtUI/QtUISettingConstants.h1
-rw-r--r--Swift/QtUI/QtWebKitChatView.cpp713
-rw-r--r--Swift/QtUI/QtWebKitChatView.h145
-rw-r--r--Swift/QtUI/SConscript7
-rw-r--r--Swift/QtUI/WebKitChatView/WebKitChatView.pro (renamed from Swift/QtUI/ChatView/ChatView.pro)4
-rw-r--r--Swift/QtUI/WebKitChatView/main.cpp (renamed from Swift/QtUI/ChatView/main.cpp)0
17 files changed, 1221 insertions, 773 deletions
diff --git a/BuildTools/Cppcheck.sh b/BuildTools/Cppcheck.sh
index 7b6a33b..753175b 100755
--- a/BuildTools/Cppcheck.sh
+++ b/BuildTools/Cppcheck.sh
@@ -11,7 +11,7 @@ cppcheck $@ \
-i Swiftob/linit.cpp \
-i Swift/QtUI/EventViewer/main.cpp \
-i Swift/QtUI/ApplicationTest \
- -i Swift/QtUI/ChatView/main.cpp \
+ -i Swift/QtUI/WebKitChatView/main.cpp \
-i Swift/QtUI/Roster/main.cpp \
-i Swift/QtUI/NotifierTest/NotifierTest.cpp \
\
diff --git a/Swift/QtUI/QtBarriersFreeChatView.cpp b/Swift/QtUI/QtBarriersFreeChatView.cpp
new file mode 100644
index 0000000..eef58ad
--- /dev/null
+++ b/Swift/QtUI/QtBarriersFreeChatView.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+#include "QtBarriersFreeChatView.h"
+#include "QtSwiftUtil.h"
+
+#include <Swiften/Base/Log.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <QDateTime>
+#include <QDebug>
+
+namespace Swift {
+ QtBarriersFreeChatView::QtBarriersFreeChatView(QtChatTheme*, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtChatView(parent), eventStream_(eventStream), settings_(settings), emoticons_(emoticons) {
+
+ QVBoxLayout* mainLayout = new QVBoxLayout(this);
+ mainLayout->setSpacing(0);
+ mainLayout->setContentsMargins(0,0,0,0);
+ textEdit_ = new QTextEdit();
+ textEdit_->setReadOnly(true);
+
+ mainLayout->addWidget(textEdit_);
+
+ idCounter_ = 0;
+ }
+
+ QtBarriersFreeChatView::~QtBarriersFreeChatView() {
+
+ }
+
+ void QtBarriersFreeChatView::replaceLastMessage(const QString& newMessage) {
+
+ }
+
+ void QtBarriersFreeChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg) {
+
+ }
+
+ void QtBarriersFreeChatView::setFileTransferProgress(QString id, const int percentageDone) {
+
+ }
+
+ void QtBarriersFreeChatView::addToJSEnvironment(const QString&, QObject*) {
+
+ }
+
+ void QtBarriersFreeChatView::setAckState(std::string const& id, ChatWindow::AckState) {
+
+ }
+
+ void QtBarriersFreeChatView::setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) {
+
+ }
+
+ std::string QtBarriersFreeChatView::addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) {
+ SWIFT_LOG(debug) << "[QtBarriersFreeChatView::addMessage] - entered" << std::endl;
+ std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
+ textEdit_->append("[" + B2QDATE(time).toString("hh:mm:ss") + "] <" + Qt::escape(P2QSTRING(senderName)) + "> " + Qt::escape(P2QSTRING(message)));
+ SWIFT_LOG(debug) << "[QtBarriersFreeChatView::addMessage] - return: " << id << std::endl;
+ return id;
+ }
+
+ void QtBarriersFreeChatView::setChatWindowHasFocus(bool focus) {
+
+ }
+
+ std::string QtBarriersFreeChatView::addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) {
+ return "";
+ }
+
+ std::string QtBarriersFreeChatView::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) {
+ return "";
+ }
+
+ void QtBarriersFreeChatView::addErrorMessage(const std::string& errorMessage) {
+
+ }
+
+ void QtBarriersFreeChatView::addSystemMessage(const std::string& message) {
+
+ }
+
+ void QtBarriersFreeChatView::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) {
+ replaceMessage(" *" + P2QSTRING(message) + "*", P2QSTRING(id), B2QDATE(time), "font-style:italic ");
+ }
+
+ void QtBarriersFreeChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time, const QString& style) {
+
+ }
+
+ void QtBarriersFreeChatView::addPresenceMessage(const std::string& message) {
+
+ }
+
+ void QtBarriersFreeChatView::scrollToBottom() {
+ if(textEdit_->isReadOnly()) {
+ QKeyEvent endPressed(QEvent::KeyPress, Qt::Key_End, Qt::NoModifier);
+ handleKeyPressEvent(&endPressed);
+ }
+ else {
+ QKeyEvent endPressed(QEvent::KeyPress, Qt::Key_End, Qt::ControlModifier);
+ handleKeyPressEvent(&endPressed);
+ }
+ }
+
+ void QtBarriersFreeChatView::resizeFont(int fontSizeSteps) {
+ qreal currentSize = textEdit_->fontPointSize();
+ qDebug() << "resizeFont: changing current font point size " << currentSize << " with fontSizeSteps " << fontSizeSteps << " to :" << currentSize + fontSizeSteps;
+ textEdit_->setFontPointSize( currentSize + fontSizeSteps );
+ }
+
+ void QtBarriersFreeChatView::handleKeyPressEvent(QKeyEvent* event) {
+ event->ignore();
+ }
+
+ void QtBarriersFreeChatView::setMUCInvitationJoined(QString id) {
+
+ }
+
+ void QtBarriersFreeChatView::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) {
+
+ }
+
+ void QtBarriersFreeChatView::showEmoticons(bool) {
+
+ }
+}
diff --git a/Swift/QtUI/QtBarriersFreeChatView.h b/Swift/QtUI/QtBarriersFreeChatView.h
new file mode 100644
index 0000000..c5b3fdc
--- /dev/null
+++ b/Swift/QtUI/QtBarriersFreeChatView.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include "QtChatView.h"
+#include <QTextEdit>
+
+namespace Swift {
+ class UIEventStream;
+ class QtBarriersFreeChatView : public QtChatView {
+ public:
+ QtBarriersFreeChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons);
+ virtual ~QtBarriersFreeChatView();
+
+ // QtChatView Implementation
+ void replaceLastMessage(const QString& newMessage);
+ void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time);
+ void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg);
+ void setFileTransferProgress(QString id, const int percentageDone);
+ void addToJSEnvironment(const QString&, QObject*);
+ void setAckState(std::string const& id, ChatWindow::AckState);
+ void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state);
+ std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time);
+ void setChatWindowHasFocus(bool focus);
+ std::string addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
+ std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes);
+ void addErrorMessage(const std::string& errorMessage);
+ void addSystemMessage(const std::string& message);
+ void replaceMessage(const std::string& message, const QString& id, const QDateTime& time, const QString& style) {
+ replaceMessage(P2QSTRING(message), id, time, style);
+ }
+ void replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time, const QString& style);
+ void addPresenceMessage(const std::string& message);
+ void showEmoticons(bool);
+ void setMUCInvitationJoined(QString id);
+ void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct);
+ public slots:
+ // QtChatView Implementation
+ void scrollToBottom();
+ void resizeFont(int fontSizeSteps);
+ void handleKeyPressEvent(QKeyEvent* event);
+
+ private:
+ QTextEdit *textEdit_;
+ int idCounter_;
+ UIEventStream* eventStream_;
+ SettingsProvider* settings_;
+ QMap<QString, QString> emoticons_;
+ };
+}
diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp
index 49e5974..56cd9bc 100644
--- a/Swift/QtUI/QtChatView.cpp
+++ b/Swift/QtUI/QtChatView.cpp
@@ -4,384 +4,22 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "QtChatView.h"
-
-#include <QtDebug>
-#include <QEventLoop>
-#include <QFile>
-#include <QDesktopServices>
-#include <QVBoxLayout>
-#include <QWebFrame>
-#include <QKeyEvent>
-#include <QStackedWidget>
-#include <QTimer>
-#include <QMessageBox>
-#include <QApplication>
-
-#include <Swiften/Base/Log.h>
-
-#include "QtWebView.h"
-#include "QtChatTheme.h"
-#include "QtChatWindow.h"
-#include "QtSwiftUtil.h"
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+#include "QtChatView.h"
namespace Swift {
-QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent) : QWidget(parent), fontSizeSteps_(0) {
- theme_ = theme;
-
- QVBoxLayout* mainLayout = new QVBoxLayout(this);
- mainLayout->setSpacing(0);
- mainLayout->setContentsMargins(0,0,0,0);
- webView_ = new QtWebView(this);
- connect(webView_, SIGNAL(linkClicked(const QUrl&)), SLOT(handleLinkClicked(const QUrl&)));
- connect(webView_, SIGNAL(loadFinished(bool)), SLOT(handleViewLoadFinished(bool)));
- connect(webView_, SIGNAL(gotFocus()), SIGNAL(gotFocus()));
- connect(webView_, SIGNAL(clearRequested()), SLOT(handleClearRequested()));
- connect(webView_, SIGNAL(fontGrowRequested()), SLOT(increaseFontSize()));
- connect(webView_, SIGNAL(fontShrinkRequested()), SLOT(decreaseFontSize()));
-#ifdef Q_WS_X11
- /* To give a border on Linux, where it looks bad without */
- QStackedWidget* stack = new QStackedWidget(this);
- stack->addWidget(webView_);
- stack->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
- stack->setLineWidth(2);
- mainLayout->addWidget(stack);
-#else
- mainLayout->addWidget(webView_);
-#endif
-
-#ifdef SWIFT_EXPERIMENTAL_FT
- setAcceptDrops(true);
-#endif
-
- webPage_ = new QWebPage(this);
- webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
- //webPage_->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
- webView_->setPage(webPage_);
- connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard()));
-
- viewReady_ = false;
- isAtBottom_ = true;
- resetView();
-}
-
-void QtChatView::handleClearRequested() {
- QMessageBox messageBox(this);
- messageBox.setWindowTitle(tr("Clear log"));
- messageBox.setText(tr("You are about to clear the contents of your chat log."));
- messageBox.setInformativeText(tr("Are you sure?"));
- messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
- messageBox.setDefaultButton(QMessageBox::Yes);
- int button = messageBox.exec();
- if (button == QMessageBox::Yes) {
- logCleared();
- resetView();
- }
-}
-
-void QtChatView::handleKeyPressEvent(QKeyEvent* event) {
- webView_->keyPressEvent(event);
-}
-
-void QtChatView::addMessage(boost::shared_ptr<ChatSnippet> snippet) {
- if (viewReady_) {
- addToDOM(snippet);
- } else {
- /* If this asserts, the previous queuing code was necessary and should be reinstated */
- assert(false);
- }
-}
-
-QWebElement QtChatView::snippetToDOM(boost::shared_ptr<ChatSnippet> snippet) {
- QWebElement newElement = newInsertPoint_.clone();
- newElement.setInnerXml(snippet->getContent());
- Q_ASSERT(!newElement.isNull());
- return newElement;
-}
-
-void QtChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) {
- rememberScrolledToBottom();
- bool insert = snippet->getAppendToPrevious();
- QWebElement continuationElement = lastElement_.findFirst("#insert");
- bool fallback = insert && continuationElement.isNull();
- boost::shared_ptr<ChatSnippet> newSnippet = (insert && fallback) ? snippet->getContinuationFallbackSnippet() : snippet;
- QWebElement newElement = snippetToDOM(newSnippet);
- if (insert && !fallback) {
- Q_ASSERT(!continuationElement.isNull());
- continuationElement.replace(newElement);
- } else {
- continuationElement.removeFromDocument();
- newInsertPoint_.prependOutside(newElement);
- }
- lastElement_ = newElement;
- if (fontSizeSteps_ != 0) {
- double size = 1.0 + 0.2 * fontSizeSteps_;
- QString sizeString(QString().setNum(size, 'g', 3) + "em");
- const QWebElementCollection spans = lastElement_.findAll("span.swift_resizable");
- foreach (QWebElement span, spans) {
- span.setStyleProperty("font-size", sizeString);
- }
- }
-}
-
-void QtChatView::addLastSeenLine() {
- if (lineSeparator_.isNull()) {
- lineSeparator_ = newInsertPoint_.clone();
- lineSeparator_.setInnerXml(QString("<hr/>"));
- newInsertPoint_.prependOutside(lineSeparator_);
- }
- else {
- QWebElement lineSeparatorC = lineSeparator_.clone();
- lineSeparatorC.removeFromDocument();
- }
- newInsertPoint_.prependOutside(lineSeparator_);
-}
-
-void QtChatView::replaceLastMessage(const QString& newMessage) {
- assert(viewReady_);
- rememberScrolledToBottom();
- assert(!lastElement_.isNull());
- QWebElement replace = lastElement_.findFirst("span.swift_message");
- assert(!replace.isNull());
- QString old = lastElement_.toOuterXml();
- replace.setInnerXml(ChatSnippet::escape(newMessage));
-}
-
-void QtChatView::replaceLastMessage(const QString& newMessage, const QString& note) {
- rememberScrolledToBottom();
- replaceLastMessage(newMessage);
- QWebElement replace = lastElement_.findFirst("span.swift_time");
- assert(!replace.isNull());
- replace.setInnerXml(ChatSnippet::escape(note));
-}
-
-QString QtChatView::getLastSentMessage() {
- return lastElement_.toPlainText();
-}
-
-void QtChatView::addToJSEnvironment(const QString& name, QObject* obj) {
- webView_->page()->currentFrame()->addToJavaScriptWindowObject(name, obj);
-}
-
-void QtChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime) {
- rememberScrolledToBottom();
- QWebElement message = document_.findFirst("#" + id);
- if (!message.isNull()) {
- QWebElement replaceContent = message.findFirst("span.swift_inner_message");
- assert(!replaceContent.isNull());
- QString old = replaceContent.toOuterXml();
- replaceContent.setInnerXml(ChatSnippet::escape(newMessage));
- QWebElement replaceTime = message.findFirst("span.swift_time");
- assert(!replaceTime.isNull());
- old = replaceTime.toOuterXml();
- replaceTime.setInnerXml(ChatSnippet::escape(tr("%1 edited").arg(ChatSnippet::timeToEscapedString(editTime))));
- }
- else {
- qWarning() << "Trying to replace element with id " << id << " but it's not there.";
- }
-}
-
-void QtChatView::showEmoticons(bool show) {
- {
- const QWebElementCollection spans = document_.findAll("span.swift_emoticon_image");
- foreach (QWebElement span, spans) {
- span.setStyleProperty("display", show ? "inline" : "none");
- }
- }
- {
- const QWebElementCollection spans = document_.findAll("span.swift_emoticon_text");
- foreach (QWebElement span, spans) {
- span.setStyleProperty("display", show ? "none" : "inline");
- }
- }
-}
-
-void QtChatView::copySelectionToClipboard() {
- if (!webPage_->selectedText().isEmpty()) {
- webPage_->triggerAction(QWebPage::Copy);
- }
-}
-
-void QtChatView::setAckXML(const QString& id, const QString& xml) {
- QWebElement message = document_.findFirst("#" + id);
- /* Deliberately not asserting here, so that when we start expiring old messages it won't hit us */
- if (message.isNull()) return;
- QWebElement ackElement = message.findFirst("span.swift_ack");
- assert(!ackElement.isNull());
- ackElement.setInnerXml(xml);
-}
-
-void QtChatView::setReceiptXML(const QString& id, const QString& xml) {
- QWebElement message = document_.findFirst("#" + id);
- if (message.isNull()) return;
- QWebElement receiptElement = message.findFirst("span.swift_receipt");
- assert(!receiptElement.isNull());
- receiptElement.setInnerXml(xml);
-}
-
-void QtChatView::displayReceiptInfo(const QString& id, bool showIt) {
- QWebElement message = document_.findFirst("#" + id);
- if (message.isNull()) return;
- QWebElement receiptElement = message.findFirst("span.swift_receipt");
- assert(!receiptElement.isNull());
- receiptElement.setStyleProperty("display", showIt ? "inline" : "none");
-}
+QtChatView::QtChatView(QWidget* parent) : QWidget(parent) {
-void QtChatView::rememberScrolledToBottom() {
- isAtBottom_ = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical);
}
-void QtChatView::scrollToBottom() {
- isAtBottom_ = true;
- webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical));
- webView_->update(); /* Work around redraw bug in some versions of Qt. */
-}
-
-void QtChatView::handleFrameSizeChanged() {
- if (isAtBottom_) {
- scrollToBottom();
- }
-}
-
-void QtChatView::handleLinkClicked(const QUrl& url) {
- QDesktopServices::openUrl(url);
-}
-
-void QtChatView::handleViewLoadFinished(bool ok) {
- Q_ASSERT(ok);
- viewReady_ = true;
-}
-
-void QtChatView::increaseFontSize(int numSteps) {
- //qDebug() << "Increasing";
- fontSizeSteps_ += numSteps;
- emit fontResized(fontSizeSteps_);
-}
+QtChatView::~QtChatView() {
-void QtChatView::decreaseFontSize() {
- fontSizeSteps_--;
- if (fontSizeSteps_ < 0) {
- fontSizeSteps_ = 0;
- }
- emit fontResized(fontSizeSteps_);
}
-void QtChatView::resizeFont(int fontSizeSteps) {
- fontSizeSteps_ = fontSizeSteps;
- double size = 1.0 + 0.2 * fontSizeSteps_;
- QString sizeString(QString().setNum(size, 'g', 3) + "em");
- //qDebug() << "Setting to " << sizeString;
- const QWebElementCollection spans = document_.findAll("span.swift_resizable");
- foreach (QWebElement span, spans) {
- span.setStyleProperty("font-size", sizeString);
- }
- webView_->setFontSizeIsMinimal(size == 1.0);
-}
-
-void QtChatView::resetView() {
- lastElement_ = QWebElement();
- QString pageHTML = theme_->getTemplate();
- pageHTML.replace("==bodyBackground==", "background-color:#e3e3e3");
- pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getBase());
- if (pageHTML.count("%@") > 3) {
- pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getMainCSS());
- }
- pageHTML.replace(pageHTML.indexOf("%@"), 2, "Variants/Blue on Green.css");
- pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*headerSnippet.getContent()*/);
- pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*footerSnippet.getContent()*/);
- QEventLoop syncLoop;
- connect(webView_, SIGNAL(loadFinished(bool)), &syncLoop, SLOT(quit()));
- webPage_->mainFrame()->setHtml(pageHTML);
- while (!viewReady_) {
- QTimer t;
- t.setSingleShot(true);
- connect(&t, SIGNAL(timeout()), &syncLoop, SLOT(quit()));
- t.start(50);
- syncLoop.exec();
- }
- document_ = webPage_->mainFrame()->documentElement();
- QWebElement chatElement = document_.findFirst("#Chat");
- newInsertPoint_ = chatElement.clone();
- newInsertPoint_.setOuterXml("<div id='swift_insert'/>");
- chatElement.appendInside(newInsertPoint_);
- Q_ASSERT(!newInsertPoint_.isNull());
-
- connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection);
-}
-
-QWebElement findDivElementWithID(QWebElement document, QString id) {
- QWebElementCollection divs = document.findAll("div");
- foreach(QWebElement div, divs) {
- if (div.attribute("id") == id) {
- return div;
- }
- }
- return QWebElement();
-}
-
-void QtChatView::setFileTransferProgress(QString id, const int percentageDone) {
- QWebElement ftElement = findDivElementWithID(document_, id);
- if (ftElement.isNull()) {
- SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!" << std::endl;
- return;
- }
- QWebElement progressBar = ftElement.findFirst("div.progressbar");
- progressBar.setStyleProperty("width", QString::number(percentageDone) + "%");
-
- QWebElement progressBarValue = ftElement.findFirst("div.progressbar-value");
- progressBarValue.setInnerXml(QString::number(percentageDone) + " %");
-}
-
-void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& /* msg */) {
- QWebElement ftElement = findDivElementWithID(document_, id);
- if (ftElement.isNull()) {
- SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id) << std::endl;
- return;
- }
-
- QString newInnerHTML = "";
- if (state == ChatWindow::WaitingForAccept) {
- newInnerHTML = tr("Waiting for other side to accept the transfer.") + "<br/>" +
- QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id);
- }
- if (state == ChatWindow::Negotiating) {
- // replace with text "Negotiaging" + Cancel button
- newInnerHTML = tr("Negotiating...") + "<br/>" +
- QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id);
- }
- else if (state == ChatWindow::Transferring) {
- // progress bar + Cancel Button
- newInnerHTML = "<div style=\"position: relative; width: 90%; height: 20px; border: 2px solid grey; -webkit-border-radius: 10px;\">"
- "<div class=\"progressbar\" style=\"width: 0%; height: 100%; background: #AAA; -webkit-border-radius: 6px;\">"
- "<div class=\"progressbar-value\" style=\"position: absolute; top: 0px; left: 0px; width: 100%; text-align: center; padding-top: 2px;\">"
- "0%"
- "</div>"
- "</div>"
- "</div>" +
- QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id);
- }
- else if (state == ChatWindow::Canceled) {
- newInnerHTML = tr("Transfer has been canceled!");
- }
- else if (state == ChatWindow::Finished) {
- // text "Successful transfer"
- newInnerHTML = tr("Transfer completed successfully.");
- }
- else if (state == ChatWindow::FTFailed) {
- newInnerHTML = tr("Transfer failed.");
- }
-
- ftElement.setInnerXml(newInnerHTML);
-}
-
-void QtChatView::setMUCInvitationJoined(QString id) {
- QWebElement divElement = findDivElementWithID(document_, id);
- QWebElement buttonElement = divElement.findFirst("input#mucinvite");
- if (!buttonElement.isNull()) {
- buttonElement.setAttribute("value", tr("Return to room"));
- }
-}
-
-}
+} // namespace Swift
diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h
index fdbdd5a..2454672 100644
--- a/Swift/QtUI/QtChatView.h
+++ b/Swift/QtUI/QtChatView.h
@@ -1,86 +1,57 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
*/
#ifndef SWIFT_QtChatView_H
#define SWIFT_QtChatView_H
-#include <QString>
+#include "QtChatWindow.h"
#include <QWidget>
-#include <QList>
-#include <QWebElement>
-
-#include <boost/shared_ptr.hpp>
-
-#include "ChatSnippet.h"
-
-#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
-
-class QWebPage;
-class QUrl;
+#include <QKeyEvent>
namespace Swift {
- class QtWebView;
- class QtChatTheme;
class QtChatView : public QWidget {
Q_OBJECT
- public:
- QtChatView(QtChatTheme* theme, QWidget* parent);
- void addMessage(boost::shared_ptr<ChatSnippet> snippet);
- void addLastSeenLine();
- void replaceLastMessage(const QString& newMessage);
- void replaceLastMessage(const QString& newMessage, const QString& note);
- void replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time);
- void rememberScrolledToBottom();
- void setAckXML(const QString& id, const QString& xml);
- void setReceiptXML(const QString& id, const QString& xml);
- void displayReceiptInfo(const QString& id, bool showIt);
-
- QString getLastSentMessage();
- void addToJSEnvironment(const QString&, QObject*);
- void setFileTransferProgress(QString id, const int percentageDone);
- void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg);
- void setMUCInvitationJoined(QString id);
- void showEmoticons(bool show);
- signals:
- void gotFocus();
- void fontResized(int);
- void logCleared();
-
- public slots:
- void copySelectionToClipboard();
- void scrollToBottom();
- void handleLinkClicked(const QUrl&);
- void handleKeyPressEvent(QKeyEvent* event);
- void resetView();
- void increaseFontSize(int numSteps = 1);
- void decreaseFontSize();
- void resizeFont(int fontSizeSteps);
-
- private slots:
- void handleViewLoadFinished(bool);
- void handleFrameSizeChanged();
- void handleClearRequested();
-
- private:
- void headerEncode();
- void messageEncode();
- void addToDOM(boost::shared_ptr<ChatSnippet> snippet);
- QWebElement snippetToDOM(boost::shared_ptr<ChatSnippet> snippet);
-
- bool viewReady_;
- bool isAtBottom_;
- QtWebView* webView_;
- QWebPage* webPage_;
- int fontSizeSteps_;
- QtChatTheme* theme_;
- QWebElement newInsertPoint_;
- QWebElement lineSeparator_;
- QWebElement lastElement_;
- QWebElement document_;
+ public:
+ QtChatView(QWidget* parent);
+ virtual ~QtChatView();
+
+
+ virtual void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg) = 0;
+ virtual void setFileTransferProgress(QString id, const int percentageDone) = 0;
+ virtual void addToJSEnvironment(const QString&, QObject*) = 0;
+ virtual void setAckState(std::string const& id, ChatWindow::AckState) = 0;
+ virtual void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) = 0;
+ virtual std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) = 0;
+ virtual void setChatWindowHasFocus(bool focus) = 0;
+ virtual std::string addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) = 0;
+ virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0;
+ virtual void addErrorMessage(const std::string& errorMessage) = 0;
+ virtual void addSystemMessage(const std::string& message) = 0;
+ virtual void replaceMessage(const std::string& newMessage, const QString& id, const QDateTime& time, const QString& style) = 0;
+ virtual void replaceLastMessage(const QString& newMessage) = 0;
+ virtual void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0;
+ virtual void addPresenceMessage(const std::string& message) = 0;
+ virtual void setMUCInvitationJoined(QString id) = 0;
+ virtual void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) = 0;
+ virtual void showEmoticons(bool show) = 0;
+
+ signals:
+ void logCleared();
+ void onFileTransferAccept(std::string, std::string);
+ void onFileTransferStart(std::string, std::string);
+ void onFileTransferCancel(std::string);
+ void gotFocus();
+ void fontResized(int);
+
+ public slots:
+ virtual void scrollToBottom() = 0;
+ virtual void resizeFont(int fontSizeSteps) = 0;
+ virtual void handleKeyPressEvent(QKeyEvent* event) = 0;
};
}
-#endif
+#endif // #ifndef SWIFT_QtWebKitChatView_H
+
diff --git a/Swift/QtUI/QtChatViewFactory.cpp b/Swift/QtUI/QtChatViewFactory.cpp
new file mode 100644
index 0000000..5c55627
--- /dev/null
+++ b/Swift/QtUI/QtChatViewFactory.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+#include "QtChatViewFactory.h"
+#include "QtBarriersFreeChatView.h"
+#include "QtWebKitChatView.h"
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+
+#include <QDebug>
+
+namespace Swift {
+QtChatView* QtChatViewFactory::createChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) {
+ bool barriersFreeChat = settings->getSetting(QtUISettingConstants::BARRIERS_FREE_CHAT_VIEW);
+
+ qDebug() << "[QtChatViewFactory::createChatView] - barriersFreeChat: " << barriersFreeChat;
+
+ if(barriersFreeChat) {
+ qDebug() << "[QtChatViewFactory::createChatView] - using barriers free chat view ...";
+ return new QtBarriersFreeChatView(theme, parent, eventStream, settings, emoticons);
+ }
+ else {
+ qDebug() << "[QtChatViewFactory::createChatView] - using normal chat view ...";
+ return new QtWebKitChatView(theme, parent, eventStream, settings, emoticons);
+ }
+}
+
+}
+
diff --git a/Swift/QtUI/QtChatViewFactory.h b/Swift/QtUI/QtChatViewFactory.h
new file mode 100644
index 0000000..e229837
--- /dev/null
+++ b/Swift/QtUI/QtChatViewFactory.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include "Swift/QtUI/QtChatView.h"
+#include "QtSettingsProvider.h"
+#include "QtUISettingConstants.h"
+#include "Swift/Controllers/Settings/SettingsProvider.h"
+
+#include <QObject>
+
+class QWidget;
+
+namespace Swift {
+ class QtChatTheme;
+ class QtChatViewFactory {
+ public:
+ static QtChatView* createChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons);
+ };
+}
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index f42469b..d412a50 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -4,30 +4,30 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
#include "QtChatWindow.h"
#include "Swift/Controllers/Roster/Roster.h"
#include "Swift/Controllers/Roster/RosterItem.h"
#include "Swift/Controllers/Roster/ContactRosterItem.h"
#include "Roster/QtOccupantListWidget.h"
#include "SwifTools/Linkify.h"
-#include "QtChatView.h"
-#include "MessageSnippet.h"
-#include "SystemMessageSnippet.h"
+#include "QtChatViewFactory.h"
#include "QtTextEdit.h"
#include "QtSettingsProvider.h"
#include "QtScaledAvatarCache.h"
#include "QtInviteToChatWindow.h"
-#include <Swift/QtUI/QtUISettingConstants.h>
-#include <Swiften/StringCodecs/Base64.h>
#include "SwifTools/TabComplete.h"
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
-#include "QtChatWindowJSBridge.h"
#include <boost/cstdint.hpp>
-#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <QLabel>
@@ -46,7 +46,6 @@
#include <QTime>
#include <QUrl>
#include <QPushButton>
-#include <QFileDialog>
#include <QMenu>
#include <QTextDocument>
#include <Swift/Controllers/Settings/SettingsProvider.h>
@@ -54,25 +53,16 @@
namespace Swift {
-const QString QtChatWindow::ButtonFileTransferCancel = QString("filetransfer-cancel");
-const QString QtChatWindow::ButtonFileTransferSetDescription = QString("filetransfer-setdescription");
-const QString QtChatWindow::ButtonFileTransferSendRequest = QString("filetransfer-sendrequest");
-const QString QtChatWindow::ButtonFileTransferAcceptRequest = QString("filetransfer-acceptrequest");
-const QString QtChatWindow::ButtonMUCInvite = QString("mucinvite");
-
-QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), emoticons_(emoticons) {
+QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtTabbable(), contact_(contact) {
settings_ = settings;
unreadCount_ = 0;
- idCounter_ = 0;
inputEnabled_ = true;
completer_ = NULL;
affiliationEditor_ = NULL;
- theme_ = theme;
isCorrection_ = false;
labelModel_ = NULL;
correctionEnabled_ = Maybe;
- showEmoticons_ = true;
updateTitleWithUnreadCount();
#ifdef SWIFT_EXPERIMENTAL_FT
@@ -119,10 +109,10 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
logRosterSplitter_ = new QSplitter(this);
logRosterSplitter_->setAutoFillBackground(true);
layout->addWidget(logRosterSplitter_);
- messageLog_ = new QtChatView(theme, this);
+ messageLog_ = QtChatViewFactory::createChatView(theme, this, eventStream, settings_, emoticons);
logRosterSplitter_->addWidget(messageLog_);
- treeWidget_ = new QtOccupantListWidget(eventStream_, settings_, this);
+ treeWidget_ = new QtOccupantListWidget(eventStream, settings_, this);
treeWidget_->hide();
logRosterSplitter_->addWidget(treeWidget_);
logRosterSplitter_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@@ -173,43 +163,38 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
resize(400,300);
connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int)));
connect(messageLog_, SIGNAL(logCleared()), this, SLOT(handleLogCleared()));
+ connect(messageLog_, SIGNAL(onFileTransferAccept(std::string, std::string)), this, SLOT(handleFileTransferAccept(std::string, std::string)));
+ connect(messageLog_, SIGNAL(onFileTransferStart(std::string, std::string)), this, SLOT(handleFileTransferStart(std::string, std::string)));
+ connect(messageLog_, SIGNAL(onFileTransferCancel(std::string)), this, SLOT(handleFileTransferCancel(std::string)));
treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1));
treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2));
-
- jsBridge = new QtChatWindowJSBridge();
- messageLog_->addToJSEnvironment("chatwindow", jsBridge);
- connect(jsBridge, SIGNAL(buttonClicked(QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString)));
-
- settings_->onSettingChanged.connect(boost::bind(&QtChatWindow::handleSettingChanged, this, _1));
- showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS);
-
}
QtChatWindow::~QtChatWindow() {
- delete jsBridge;
if (mucConfigurationWindow_) {
delete mucConfigurationWindow_.data();
}
}
-void QtChatWindow::handleSettingChanged(const std::string& setting) {
- if (setting == QtUISettingConstants::SHOW_EMOTICONS.getKey()) {
- showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS);
- messageLog_->showEmoticons(showEmoticons_);
- }
-}
-
void QtChatWindow::handleLogCleared() {
onLogCleared();
}
-void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) {
- onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(item));
+void QtChatWindow::handleFileTransferAccept(std::string id, std::string description) {
+ onFileTransferAccept(id, description);
+}
+
+void QtChatWindow::handleFileTransferStart(std::string id, std::string description) {
+ onFileTransferStart(id, description);
+}
+
+void QtChatWindow::handleFileTransferCancel(std::string id) {
+ onFileTransferCancel(id);
}
-bool QtChatWindow::appendToPreviousCheck(QtChatWindow::PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const {
- return previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_&& previousSenderName_ == P2QSTRING(senderName)));
+void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) {
+ onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(item));
}
void QtChatWindow::handleFontResized(int fontSizeSteps) {
@@ -415,12 +400,12 @@ void QtChatWindow::convertToMUC() {
void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) {
if (isWidgetSelected()) {
- lastLineTracker_.setHasFocus(true);
+ messageLog_->setChatWindowHasFocus(true);
input_->setFocus();
onAllMessagesRead();
}
else {
- lastLineTracker_.setHasFocus(false);
+ messageLog_->setChatWindowHasFocus(false);
}
}
@@ -479,57 +464,14 @@ void QtChatWindow::updateTitleWithUnreadCount() {
}
std::string QtChatWindow::addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) {
- return addMessage(linkimoticonify(P2QSTRING(message)), senderName, senderIsSelf, label, avatarPath, "", time);
-}
-
-QString QtChatWindow::linkimoticonify(const QString& message) const {
- QString messageHTML(message);
- messageHTML = Qt::escape(messageHTML);
- QMapIterator<QString, QString> it(emoticons_);
- QString textStyle = showEmoticons_ ? "style='display:none'" : "";
- QString imageStyle = showEmoticons_ ? "" : "style='display:none'";
- if (messageHTML.length() < 500) {
- while (it.hasNext()) {
- it.next();
- messageHTML.replace(it.key(), "<span class='swift_emoticon_image' " + imageStyle + "><img src='" + it.value() + "'/></span><span class='swift_emoticon_text' " + textStyle + ">"+it.key() + "</span>");
- }
- messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML)));
- }
- messageHTML.replace("\n","<br/>");
- return messageHTML;
+ return addMessage(message, senderName, senderIsSelf, label, avatarPath, "", time);
}
-std::string QtChatWindow::addMessage(const QString &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) {
+std::string QtChatWindow::addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) {
if (isWidgetSelected()) {
onAllMessagesRead();
}
- QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str());
-
- QString htmlString;
- if (label) {
- htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \" class='swift_label'>").arg(Qt::escape(P2QSTRING(label->getForegroundColor()))).arg(Qt::escape(P2QSTRING(label->getBackgroundColor())));
- htmlString += QString("%1</span> ").arg(Qt::escape(P2QSTRING(label->getDisplayMarking())));
- }
- QString messageHTML(message);
- QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
- QString styleSpanEnd = style == "" ? "" : "</span>";
- htmlString += "<span class='swift_inner_message'>" + styleSpanStart + messageHTML + styleSpanEnd + "</span>" ;
-
- bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf);
- if (lastLineTracker_.getShouldMoveLastLine()) {
- /* should this be queued? */
- messageLog_->addLastSeenLine();
- /* if the line is added we should break the snippet */
- appendToPrevious = false;
- }
- QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();
- std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
-
- previousMessageWasSelf_ = senderIsSelf;
- previousSenderName_ = P2QSTRING(senderName);
- previousMessageKind_ = PreviousMessageWasMessage;
- return id;
+ return messageLog_->addMessage(message, senderName, senderIsSelf, label, avatarPath, style, time);
}
void QtChatWindow::flash() {
@@ -537,32 +479,11 @@ void QtChatWindow::flash() {
}
void QtChatWindow::setAckState(std::string const& id, ChatWindow::AckState state) {
- QString xml;
- switch (state) {
- case ChatWindow::Pending:
- xml = "<img src='qrc:/icons/throbber.gif' title='" + tr("This message has not been received by your server yet.") + "'/>";
- messageLog_->displayReceiptInfo(P2QSTRING(id), false);
- break;
- case ChatWindow::Received:
- xml = "";
- messageLog_->displayReceiptInfo(P2QSTRING(id), true);
- break;
- case ChatWindow::Failed: xml = "<img src='qrc:/icons/error.png' title='" + tr("This message may not have been transmitted.") + "'/>"; break;
- }
- messageLog_->setAckXML(P2QSTRING(id), xml);
+ messageLog_->setAckState(id, state);
}
void QtChatWindow::setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) {
- QString xml;
- switch (state) {
- case ChatWindow::ReceiptReceived:
- xml = "<img src='qrc:/icons/check.png' title='" + tr("The receipt for this message has been received.") + "'/>";
- break;
- case ChatWindow::ReceiptRequested:
- xml = "<img src='qrc:/icons/warn.png' title='" + tr("The receipt for this message has not yet been received. The recipient(s) might not have received this message.") + "'/>";
- break;
- }
- messageLog_->setReceiptXML(P2QSTRING(id), xml);
+ messageLog_->setMessageReceiptState(id, state);
}
int QtChatWindow::getCount() {
@@ -570,75 +491,11 @@ int QtChatWindow::getCount() {
}
std::string QtChatWindow::addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) {
- return addMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time);
-}
-
-std::string formatSize(const boost::uintmax_t bytes) {
- static const char *siPrefix[] = {"k", "M", "G", "T", "P", "E", "Z", "Y", NULL};
- int power = 0;
- double engBytes = bytes;
- while (engBytes >= 1000) {
- ++power;
- engBytes = engBytes / 1000.0;
- }
- return str( boost::format("%.1lf %sB") % engBytes % (power > 0 ? siPrefix[power-1] : "") );
-}
-
-QString encodeButtonArgument(const QString& str) {
- return Qt::escape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str)))));
-}
-
-QString decodeButtonArgument(const QString& str) {
- return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str))));
-}
-
-QString QtChatWindow::buildChatWindowButton(const QString& name, const QString& id, const QString& arg1, const QString& arg2, const QString& arg3) {
- QRegExp regex("[A-Za-z][A-Za-z0-9\\-\\_]+");
- Q_ASSERT(regex.exactMatch(id));
- QString html = QString("<input id='%2' type='submit' value='%1' onclick='chatwindow.buttonClicked(\"%2\", \"%3\", \"%4\", \"%5\");' />").arg(name).arg(id).arg(encodeButtonArgument(arg1)).arg(encodeButtonArgument(arg2)).arg(encodeButtonArgument(arg3));
- return html;
+ return messageLog_->addAction(message, senderName, senderIsSelf, label, avatarPath, time);
}
std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) {
- SWIFT_LOG(debug) << "addFileTransfer" << std::endl;
- QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));
-
- QString htmlString;
- QString formattedFileSize = P2QSTRING(formatSize(sizeInBytes));
- if (senderIsSelf) {
- // outgoing
- htmlString = tr("Send file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" +
- "<div id='" + ft_id + "'>" +
- buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) +
- buildChatWindowButton(tr("Set Description"), ButtonFileTransferSetDescription, ft_id) +
- buildChatWindowButton(tr("Send"), ButtonFileTransferSendRequest, ft_id) +
- "</div>";
- } else {
- // incoming
- htmlString = tr("Receiving file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" +
- "<div id='" + ft_id + "'>" +
- buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) +
- buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) +
- "</div>";
- }
-
- //addMessage(message, senderName, senderIsSelf, boost::shared_ptr<SecurityLabel>(), "", boost::posix_time::second_clock::local_time());
-
- bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasFileTransfer, senderName, senderIsSelf);
- if (lastLineTracker_.getShouldMoveLastLine()) {
- /* should this be queued? */
- messageLog_->addLastSeenLine();
- /* if the line is added we should break the snippet */
- appendToPrevious = false;
- }
- QString qAvatarPath = "qrc:/icons/avatar.png";
- std::string id = "ftmessage" + boost::lexical_cast<std::string>(idCounter_++);
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
-
- previousMessageWasSelf_ = senderIsSelf;
- previousSenderName_ = P2QSTRING(senderName);
- previousMessageKind_ = PreviousMessageWasFileTransfer;
- return Q2PSTRING(ft_id);
+ return messageLog_->addFileTransfer(senderName, senderIsSelf, filename, sizeInBytes);
}
void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) {
@@ -649,116 +506,45 @@ void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState
messageLog_->setFileTransferStatus(QString::fromStdString(id), state, QString::fromStdString(msg));
}
-void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3) {
- QString arg1 = decodeButtonArgument(encodedArgument1);
- QString arg2 = decodeButtonArgument(encodedArgument2);
- QString arg3 = decodeButtonArgument(encodedArgument3);
-
- if (id.startsWith(ButtonFileTransferCancel)) {
- QString ft_id = arg1;
- onFileTransferCancel(Q2PSTRING(ft_id));
- }
- else if (id.startsWith(ButtonFileTransferSetDescription)) {
- QString ft_id = arg1;
- bool ok = false;
- QString text = QInputDialog::getText(this, tr("File transfer description"),
- tr("Description:"), QLineEdit::Normal, "", &ok);
- if (ok) {
- descriptions[ft_id] = text;
- }
- }
- else if (id.startsWith(ButtonFileTransferSendRequest)) {
- QString ft_id = arg1;
- QString text = descriptions.find(ft_id) == descriptions.end() ? QString() : descriptions[ft_id];
- onFileTransferStart(Q2PSTRING(ft_id), Q2PSTRING(text));
- }
- else if (id.startsWith(ButtonFileTransferAcceptRequest)) {
- QString ft_id = arg1;
- QString filename = arg2;
-
- QString path = QFileDialog::getSaveFileName(this, tr("Save File"), filename);
- if (!path.isEmpty()) {
- onFileTransferAccept(Q2PSTRING(ft_id), Q2PSTRING(path));
- }
- }
- else if (id.startsWith(ButtonMUCInvite)) {
- QString roomJID = arg1;
- QString password = arg2;
- QString elementID = arg3;
-
- eventStream_->send(boost::make_shared<JoinMUCUIEvent>(Q2PSTRING(roomJID), Q2PSTRING(password)));
- messageLog_->setMUCInvitationJoined(elementID);
- }
- else {
- SWIFT_LOG(debug) << "Unknown HTML button! ( " << Q2PSTRING(id) << " )" << std::endl;
- }
-}
void QtChatWindow::addErrorMessage(const std::string& errorMessage) {
if (isWidgetSelected()) {
onAllMessagesRead();
}
- QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage)));
- errorMessageHTML.replace("\n","<br/>");
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_)));
-
- previousMessageWasSelf_ = false;
- previousMessageKind_ = PreviousMessageWasSystem;
+ messageLog_->addErrorMessage(errorMessage);
}
-void QtChatWindow::addSystemMessage(const std::string& message) {
+void QtChatWindow::addSystemMessage(const std::string& systemMessage) {
if (isWidgetSelected()) {
onAllMessagesRead();
}
-
- QString messageHTML(P2QSTRING(message));
- messageHTML = linkimoticonify(messageHTML);
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
-
- previousMessageKind_ = PreviousMessageWasSystem;
+ messageLog_->addSystemMessage(systemMessage);
}
void QtChatWindow::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) {
- replaceMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", id, time, "font-style:italic ");
+ messageLog_->replaceWithAction(message, id, time);
}
void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) {
- replaceMessage(linkimoticonify(P2QSTRING(message)), id, time, "");
+ replaceMessage(message, id, time, "");
}
-void QtChatWindow::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style) {
- if (!id.empty()) {
- if (isWidgetSelected()) {
- onAllMessagesRead();
- }
-
- QString messageHTML(message);
-
- QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
- QString styleSpanEnd = style == "" ? "" : "</span>";
- messageHTML = styleSpanStart + messageHTML + styleSpanEnd;
-
- messageLog_->replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time));
- }
- else {
- std::cerr << "Trying to replace a message with no id";
+void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style) {
+ if (isWidgetSelected()) {
+ onAllMessagesRead();
}
+
+ messageLog_->replaceMessage(message, P2QSTRING(id), B2QDATE(time), style);
}
void QtChatWindow::addPresenceMessage(const std::string& message) {
if (isWidgetSelected()) {
onAllMessagesRead();
}
-
- QString messageHTML(P2QSTRING(message));
- messageHTML = linkimoticonify(messageHTML);
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
-
- previousMessageKind_ = PreviousMessageWasPresence;
+ messageLog_->addPresenceMessage(message);
}
-
void QtChatWindow::returnPressed() {
if (!inputEnabled_) {
return;
@@ -829,7 +615,7 @@ void QtChatWindow::dropEvent(QDropEvent *event) {
}
void QtChatWindow::replaceLastMessage(const std::string& message) {
- messageLog_->replaceLastMessage(linkimoticonify(P2QSTRING(message)));
+ messageLog_->replaceLastMessage(P2QSTRING(message));
}
void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {
@@ -927,33 +713,7 @@ void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& ji
onAllMessagesRead();
}
- QString htmlString = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + " <br/>";
- if (!reason.empty()) {
- htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "<br/>";
- }
- if (!direct) {
- htmlString += QObject::tr("This person may not have really sent this invitation!") + "<br/>";
- }
-
- QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));
-
- htmlString += "<div id='" + id + "'>" +
- buildChatWindowButton(tr("Accept Invite"), ButtonMUCInvite, Qt::escape(P2QSTRING(jid.toString())), Qt::escape(P2QSTRING(password)), id) +
- "</div>";
-
- bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMUCInvite, senderName, false);
- if (lastLineTracker_.getShouldMoveLastLine()) {
- /* should this be queued? */
- messageLog_->addLastSeenLine();
- /* if the line is added we should break the snippet */
- appendToPrevious = false;
- }
- QString qAvatarPath = "qrc:/icons/avatar.png";
-
- messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id)));
- previousMessageWasSelf_ = false;
- previousSenderName_ = P2QSTRING(senderName);
- previousMessageKind_ = PreviousMessageWasMUCInvite;
+ messageLog_->addMUCInvitation(senderName, jid, reason, password, direct);
}
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index a703818..6871a3d 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -4,6 +4,12 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
#pragma once
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
@@ -13,8 +19,6 @@
#include <QtTabbable.h>
-#include <SwifTools/LastLineTracker.h>
-
#include <map>
#include <QPointer>
#include <QTextCursor>
@@ -34,7 +38,6 @@ namespace Swift {
class TreeWidget;
class QtTextEdit;
class UIEventStream;
- class QtChatWindowJSBridge;
class SettingsProvider;
class LabelModel : public QAbstractListModel {
@@ -73,13 +76,6 @@ namespace Swift {
Q_OBJECT
public:
- static const QString ButtonFileTransferCancel;
- static const QString ButtonFileTransferSetDescription;
- static const QString ButtonFileTransferSendRequest;
- static const QString ButtonFileTransferAcceptRequest;
- static const QString ButtonMUCInvite;
-
- public:
QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons);
~QtChatWindow();
std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
@@ -127,8 +123,6 @@ namespace Swift {
InviteToChatWindow* createInviteToChatWindow();
- static QString buildChatWindowButton(const QString& name, const QString& id, const QString& arg1 = QString(), const QString& arg2 = QString(), const QString& arg3 = QString());
-
public slots:
void handleChangeSplitterState(QByteArray state);
void handleFontResized(int fontSizeSteps);
@@ -162,36 +156,24 @@ namespace Swift {
void handleSplitterMoved(int pos, int index);
void handleAlertButtonClicked();
void handleActionButtonClicked();
+ void handleFileTransferAccept(std::string id, std::string description);
+ void handleFileTransferStart(std::string id, std::string description);
+ void handleFileTransferCancel(std::string id);
- void handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3);
void handleAffiliationEditorAccepted();
void handleCurrentLabelChanged(int);
private:
- enum PreviousMessageKind {
- PreviosuMessageWasNone,
- PreviousMessageWasMessage,
- PreviousMessageWasSystem,
- PreviousMessageWasPresence,
- PreviousMessageWasFileTransfer,
- PreviousMessageWasMUCInvite
- };
-
- private:
void updateTitleWithUnreadCount();
void tabComplete();
void beginCorrection();
void cancelCorrection();
- void handleSettingChanged(const std::string& setting);
- std::string addMessage(const QString& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time);
- void replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style);
+ std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time);
+ void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style);
void handleOccupantSelectionChanged(RosterItem* item);
- bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const;
- QString linkimoticonify(const QString& message) const;
int unreadCount_;
bool contactIsTyping_;
- LastLineTracker lastLineTracker_;
QString contact_;
QString lastSentMessage_;
QTextCursor tabCompleteCursor_;
@@ -209,25 +191,16 @@ namespace Swift {
QLineEdit* subject_;
QPushButton* actionButton_;
bool isCorrection_;
- bool previousMessageWasSelf_;
- PreviousMessageKind previousMessageKind_;
- QString previousSenderName_;
bool inputClearing_;
bool tabCompletion_;
- UIEventStream* eventStream_;
bool inputEnabled_;
QSplitter *logRosterSplitter_;
Tristate correctionEnabled_;
QString alertStyleSheet_;
- std::map<QString, QString> descriptions;
- QtChatWindowJSBridge* jsBridge;
QPointer<QtMUCConfigurationWindow> mucConfigurationWindow_;
QPointer<QtAffiliationEditor> affiliationEditor_;
- int idCounter_;
SettingsProvider* settings_;
std::vector<ChatWindow::RoomAction> availableRoomActions_;
- QMap<QString, QString> emoticons_;
- bool showEmoticons_;
QPalette defaultLabelsPalette_;
LabelModel* labelModel_;
};
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index 13b2175..e5cca01 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -22,6 +22,7 @@
#include <QtSwiftUtil.h>
#include <QtUIFactory.h>
#include <QtChatWindowFactory.h>
+#include <QtChatViewFactory.h>
#include <Swiften/Base/Log.h>
#include <Swift/Controllers/Storages/CertificateFileStorageFactory.h>
#include <Swift/Controllers/Storages/FileStoragesFactory.h>
diff --git a/Swift/QtUI/QtUISettingConstants.cpp b/Swift/QtUI/QtUISettingConstants.cpp
index 81022ec..816307a 100644
--- a/Swift/QtUI/QtUISettingConstants.cpp
+++ b/Swift/QtUI/QtUISettingConstants.cpp
@@ -4,6 +4,12 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
#include <Swift/QtUI/QtUISettingConstants.h>
namespace Swift {
@@ -13,5 +19,6 @@ const SettingsProvider::Setting<std::string> QtUISettingConstants::CLICKTHROUGH_
const SettingsProvider::Setting<int> QtUISettingConstants::CURRENT_ROSTER_TAB("currentRosterTab", 0);
const SettingsProvider::Setting<bool> QtUISettingConstants::SHOW_NICK_IN_ROSTER_HEADER("showNickInRosterHeader", true);
const SettingsProvider::Setting<int> QtUISettingConstants::CHATWINDOW_FONT_SIZE("chatWindowFontSize", 0);
+const SettingsProvider::Setting<bool> QtUISettingConstants::BARRIERS_FREE_CHAT_VIEW("barriersFreeChatView", false);
const SettingsProvider::Setting<bool> QtUISettingConstants::SHOW_EMOTICONS("showEmoticons", true);
}
diff --git a/Swift/QtUI/QtUISettingConstants.h b/Swift/QtUI/QtUISettingConstants.h
index 2740abb..048dc46 100644
--- a/Swift/QtUI/QtUISettingConstants.h
+++ b/Swift/QtUI/QtUISettingConstants.h
@@ -16,6 +16,7 @@ namespace Swift {
static const SettingsProvider::Setting<int> CURRENT_ROSTER_TAB;
static const SettingsProvider::Setting<bool> SHOW_NICK_IN_ROSTER_HEADER;
static const SettingsProvider::Setting<int> CHATWINDOW_FONT_SIZE;
+ static const SettingsProvider::Setting<bool> BARRIERS_FREE_CHAT_VIEW;
static const SettingsProvider::Setting<bool> SHOW_EMOTICONS;
};
}
diff --git a/Swift/QtUI/QtWebKitChatView.cpp b/Swift/QtUI/QtWebKitChatView.cpp
new file mode 100644
index 0000000..5e4eb33
--- /dev/null
+++ b/Swift/QtUI/QtWebKitChatView.cpp
@@ -0,0 +1,713 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+
+#include <QtDebug>
+#include <QEventLoop>
+#include <QFile>
+#include <QDesktopServices>
+#include <QVBoxLayout>
+#include <QWebFrame>
+#include <QKeyEvent>
+#include <QStackedWidget>
+#include <QTimer>
+#include <QMessageBox>
+#include <QApplication>
+#include <QTextDocument>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/StringCodecs/Base64.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
+#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
+#include <Swift/QtUI/QtUISettingConstants.h>
+
+#include "QtWebView.h"
+#include "QtChatTheme.h"
+#include "QtSwiftUtil.h"
+#include "QtScaledAvatarCache.h"
+#include "SwifTools/Linkify.h"
+#include "SystemMessageSnippet.h"
+#include "QtWebKitChatView.h"
+#include "QtChatWindowJSBridge.h"
+
+namespace Swift {
+
+const QString QtWebKitChatView::ButtonFileTransferCancel = QString("filetransfer-cancel");
+const QString QtWebKitChatView::ButtonFileTransferSetDescription = QString("filetransfer-setdescription");
+const QString QtWebKitChatView::ButtonFileTransferSendRequest = QString("filetransfer-sendrequest");
+const QString QtWebKitChatView::ButtonFileTransferAcceptRequest = QString("filetransfer-acceptrequest");
+const QString QtWebKitChatView::ButtonMUCInvite = QString("mucinvite");
+
+QString encodeButtonArgument(const QString& str) {
+ return Qt::escape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str)))));
+}
+
+QString decodeButtonArgument(const QString& str) {
+ return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str))));
+}
+
+QtWebKitChatView::QtWebKitChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtChatView(parent), fontSizeSteps_(0), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), settings_(settings), emoticons_(emoticons) {
+ theme_ = theme;
+ showEmoticons_ = true;
+
+ QVBoxLayout* mainLayout = new QVBoxLayout(this);
+ mainLayout->setSpacing(0);
+ mainLayout->setContentsMargins(0,0,0,0);
+ webView_ = new QtWebView(this);
+ connect(webView_, SIGNAL(linkClicked(const QUrl&)), SLOT(handleLinkClicked(const QUrl&)));
+ connect(webView_, SIGNAL(loadFinished(bool)), SLOT(handleViewLoadFinished(bool)));
+ connect(webView_, SIGNAL(gotFocus()), SIGNAL(gotFocus()));
+ connect(webView_, SIGNAL(clearRequested()), SLOT(handleClearRequested()));
+ connect(webView_, SIGNAL(fontGrowRequested()), SLOT(increaseFontSize()));
+ connect(webView_, SIGNAL(fontShrinkRequested()), SLOT(decreaseFontSize()));
+#ifdef Q_WS_X11
+ /* To give a border on Linux, where it looks bad without */
+ QStackedWidget* stack = new QStackedWidget(this);
+ stack->addWidget(webView_);
+ stack->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ stack->setLineWidth(2);
+ mainLayout->addWidget(stack);
+#else
+ mainLayout->addWidget(webView_);
+#endif
+
+#ifdef SWIFT_EXPERIMENTAL_FT
+ setAcceptDrops(true);
+#endif
+
+ webPage_ = new QWebPage(this);
+ webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
+ //webPage_->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
+ webView_->setPage(webPage_);
+ connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard()));
+
+ viewReady_ = false;
+ isAtBottom_ = true;
+ resetView();
+
+ idCounter_ = 0;
+
+ jsBridge_ = new QtChatWindowJSBridge();
+ addToJSEnvironment("chatwindow", jsBridge_);
+ connect(jsBridge_, SIGNAL(buttonClicked(QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString)));
+
+ settings_->onSettingChanged.connect(boost::bind(&QtWebKitChatView::handleSettingChanged, this, _1));
+ showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS);
+}
+
+QtWebKitChatView::~QtWebKitChatView() {
+ delete jsBridge_;
+}
+
+void QtWebKitChatView::handleSettingChanged(const std::string& setting) {
+ if (setting == QtUISettingConstants::SHOW_EMOTICONS.getKey()) {
+ showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS);
+ showEmoticons(showEmoticons_);
+ }
+}
+
+void QtWebKitChatView::handleClearRequested() {
+ QMessageBox messageBox(this);
+ messageBox.setWindowTitle(tr("Clear log"));
+ messageBox.setText(tr("You are about to clear the contents of your chat log."));
+ messageBox.setInformativeText(tr("Are you sure?"));
+ messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+ messageBox.setDefaultButton(QMessageBox::Yes);
+ int button = messageBox.exec();
+ if (button == QMessageBox::Yes) {
+ logCleared();
+ resetView();
+ }
+}
+
+void QtWebKitChatView::handleKeyPressEvent(QKeyEvent* event) {
+ webView_->keyPressEvent(event);
+}
+
+void QtWebKitChatView::addMessage(boost::shared_ptr<ChatSnippet> snippet) {
+ if (viewReady_) {
+ addToDOM(snippet);
+ } else {
+ /* If this asserts, the previous queuing code was necessary and should be reinstated */
+ assert(false);
+ }
+}
+
+QWebElement QtWebKitChatView::snippetToDOM(boost::shared_ptr<ChatSnippet> snippet) {
+ QWebElement newElement = newInsertPoint_.clone();
+ newElement.setInnerXml(snippet->getContent());
+ Q_ASSERT(!newElement.isNull());
+ return newElement;
+}
+
+void QtWebKitChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) {
+ rememberScrolledToBottom();
+ bool insert = snippet->getAppendToPrevious();
+ QWebElement continuationElement = lastElement_.findFirst("#insert");
+ bool fallback = insert && continuationElement.isNull();
+ boost::shared_ptr<ChatSnippet> newSnippet = (insert && fallback) ? snippet->getContinuationFallbackSnippet() : snippet;
+ QWebElement newElement = snippetToDOM(newSnippet);
+ if (insert && !fallback) {
+ Q_ASSERT(!continuationElement.isNull());
+ continuationElement.replace(newElement);
+ } else {
+ continuationElement.removeFromDocument();
+ newInsertPoint_.prependOutside(newElement);
+ }
+ lastElement_ = newElement;
+ if (fontSizeSteps_ != 0) {
+ double size = 1.0 + 0.2 * fontSizeSteps_;
+ QString sizeString(QString().setNum(size, 'g', 3) + "em");
+ const QWebElementCollection spans = lastElement_.findAll("span.swift_resizable");
+ foreach (QWebElement span, spans) {
+ span.setStyleProperty("font-size", sizeString);
+ }
+ }
+}
+
+void QtWebKitChatView::addLastSeenLine() {
+ if (lineSeparator_.isNull()) {
+ lineSeparator_ = newInsertPoint_.clone();
+ lineSeparator_.setInnerXml(QString("<hr/>"));
+ newInsertPoint_.prependOutside(lineSeparator_);
+ }
+ else {
+ QWebElement lineSeparatorC = lineSeparator_.clone();
+ lineSeparatorC.removeFromDocument();
+ }
+ newInsertPoint_.prependOutside(lineSeparator_);
+}
+
+void QtWebKitChatView::replaceLastMessage(const QString& message) {
+ QString newMessage = linkimoticonify(message);
+ assert(viewReady_);
+ rememberScrolledToBottom();
+ assert(!lastElement_.isNull());
+ QWebElement replace = lastElement_.findFirst("span.swift_message");
+ assert(!replace.isNull());
+ QString old = lastElement_.toOuterXml();
+ replace.setInnerXml(ChatSnippet::escape(newMessage));
+}
+
+void QtWebKitChatView::replaceLastMessage(const QString& newMessage, const QString& note) {
+ rememberScrolledToBottom();
+ replaceLastMessage(newMessage);
+ QWebElement replace = lastElement_.findFirst("span.swift_time");
+ assert(!replace.isNull());
+ replace.setInnerXml(ChatSnippet::escape(note));
+}
+
+QString QtWebKitChatView::getLastSentMessage() {
+ return lastElement_.toPlainText();
+}
+
+void QtWebKitChatView::addToJSEnvironment(const QString& name, QObject* obj) {
+ webView_->page()->currentFrame()->addToJavaScriptWindowObject(name, obj);
+}
+
+void QtWebKitChatView::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) {
+ replaceMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", P2QSTRING(id), B2QDATE(time), "font-style:italic ");
+}
+
+void QtWebKitChatView::replaceMessage(const std::string& newMessage, const QString& id, const QDateTime& editTime, const QString& style) {
+ replaceMessage(linkimoticonify(P2QSTRING(newMessage)), id, editTime, style);
+}
+
+void QtWebKitChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime, const QString& style) {
+ if (id.isEmpty()) {
+ qWarning() << "Trying to replace a message with no id";
+ return;
+ }
+
+ QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
+ QString styleSpanEnd = style == "" ? "" : "</span>";
+ QString messageWithStyle = styleSpanStart + newMessage + styleSpanEnd;
+
+ rememberScrolledToBottom();
+ QWebElement message = document_.findFirst("#" + id);
+ if (!message.isNull()) {
+ QWebElement replaceContent = message.findFirst("span.swift_message");
+ assert(!replaceContent.isNull());
+ QString old = replaceContent.toOuterXml();
+ replaceContent.setInnerXml(ChatSnippet::escape(messageWithStyle));
+ QWebElement replaceTime = message.findFirst("span.swift_time");
+ assert(!replaceTime.isNull());
+ old = replaceTime.toOuterXml();
+ replaceTime.setInnerXml(ChatSnippet::escape(tr("%1 edited").arg(ChatSnippet::timeToEscapedString(editTime))));
+ }
+ else {
+ qWarning() << "Trying to replace element with id " << id << " but it's not there.";
+ }
+}
+
+void QtWebKitChatView::showEmoticons(bool show) {
+ {
+ const QWebElementCollection spans = document_.findAll("span.swift_emoticon_image");
+ foreach (QWebElement span, spans) {
+ span.setStyleProperty("display", show ? "inline" : "none");
+ }
+ }
+ {
+ const QWebElementCollection spans = document_.findAll("span.swift_emoticon_text");
+ foreach (QWebElement span, spans) {
+ span.setStyleProperty("display", show ? "none" : "inline");
+ }
+ }
+}
+
+void QtWebKitChatView::copySelectionToClipboard() {
+ if (!webPage_->selectedText().isEmpty()) {
+ webPage_->triggerAction(QWebPage::Copy);
+ }
+}
+
+void QtWebKitChatView::setAckXML(const QString& id, const QString& xml) {
+ QWebElement message = document_.findFirst("#" + id);
+ /* Deliberately not asserting here, so that when we start expiring old messages it won't hit us */
+ if (message.isNull()) return;
+ QWebElement ackElement = message.findFirst("span.swift_ack");
+ assert(!ackElement.isNull());
+ ackElement.setInnerXml(xml);
+}
+
+void QtWebKitChatView::setReceiptXML(const QString& id, const QString& xml) {
+ QWebElement message = document_.findFirst("#" + id);
+ if (message.isNull()) return;
+ QWebElement receiptElement = message.findFirst("span.swift_receipt");
+ assert(!receiptElement.isNull());
+ receiptElement.setInnerXml(xml);
+}
+
+void QtWebKitChatView::displayReceiptInfo(const QString& id, bool showIt) {
+ QWebElement message = document_.findFirst("#" + id);
+ if (message.isNull()) return;
+ QWebElement receiptElement = message.findFirst("span.swift_receipt");
+ assert(!receiptElement.isNull());
+ receiptElement.setStyleProperty("display", showIt ? "inline" : "none");
+}
+
+void QtWebKitChatView::rememberScrolledToBottom() {
+ isAtBottom_ = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical);
+}
+
+void QtWebKitChatView::scrollToBottom() {
+ isAtBottom_ = true;
+ webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical));
+ webView_->update(); /* Work around redraw bug in some versions of Qt. */
+}
+
+void QtWebKitChatView::handleFrameSizeChanged() {
+ if (isAtBottom_) {
+ scrollToBottom();
+ }
+}
+
+void QtWebKitChatView::handleLinkClicked(const QUrl& url) {
+ QDesktopServices::openUrl(url);
+}
+
+void QtWebKitChatView::handleViewLoadFinished(bool ok) {
+ Q_ASSERT(ok);
+ viewReady_ = true;
+}
+
+void QtWebKitChatView::increaseFontSize(int numSteps) {
+ //qDebug() << "Increasing";
+ fontSizeSteps_ += numSteps;
+ emit fontResized(fontSizeSteps_);
+}
+
+void QtWebKitChatView::decreaseFontSize() {
+ fontSizeSteps_--;
+ if (fontSizeSteps_ < 0) {
+ fontSizeSteps_ = 0;
+ }
+ emit fontResized(fontSizeSteps_);
+}
+
+bool QtWebKitChatView::appendToPreviousCheck(QtWebKitChatView::PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const {
+ return previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
+}
+
+void QtWebKitChatView::resizeFont(int fontSizeSteps) {
+ fontSizeSteps_ = fontSizeSteps;
+ double size = 1.0 + 0.2 * fontSizeSteps_;
+ QString sizeString(QString().setNum(size, 'g', 3) + "em");
+ //qDebug() << "Setting to " << sizeString;
+ const QWebElementCollection spans = document_.findAll("span.swift_resizable");
+ foreach (QWebElement span, spans) {
+ span.setStyleProperty("font-size", sizeString);
+ }
+ webView_->setFontSizeIsMinimal(size == 1.0);
+}
+
+void QtWebKitChatView::resetView() {
+ lastElement_ = QWebElement();
+ QString pageHTML = theme_->getTemplate();
+ pageHTML.replace("==bodyBackground==", "background-color:#e3e3e3");
+ pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getBase());
+ if (pageHTML.count("%@") > 3) {
+ pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getMainCSS());
+ }
+ pageHTML.replace(pageHTML.indexOf("%@"), 2, "Variants/Blue on Green.css");
+ pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*headerSnippet.getContent()*/);
+ pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*footerSnippet.getContent()*/);
+ QEventLoop syncLoop;
+ connect(webView_, SIGNAL(loadFinished(bool)), &syncLoop, SLOT(quit()));
+ webPage_->mainFrame()->setHtml(pageHTML);
+ while (!viewReady_) {
+ QTimer t;
+ t.setSingleShot(true);
+ connect(&t, SIGNAL(timeout()), &syncLoop, SLOT(quit()));
+ t.start(50);
+ syncLoop.exec();
+ }
+ document_ = webPage_->mainFrame()->documentElement();
+ QWebElement chatElement = document_.findFirst("#Chat");
+ newInsertPoint_ = chatElement.clone();
+ newInsertPoint_.setOuterXml("<div id='swift_insert'/>");
+ chatElement.appendInside(newInsertPoint_);
+ Q_ASSERT(!newInsertPoint_.isNull());
+
+ connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection);
+}
+
+QWebElement findDivElementWithID(QWebElement document, QString id) {
+ QWebElementCollection divs = document.findAll("div");
+ foreach(QWebElement div, divs) {
+ if (div.attribute("id") == id) {
+ return div;
+ }
+ }
+ return QWebElement();
+}
+
+void QtWebKitChatView::setFileTransferProgress(QString id, const int percentageDone) {
+ QWebElement ftElement = findDivElementWithID(document_, id);
+ if (ftElement.isNull()) {
+ SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!" << std::endl;
+ return;
+ }
+ QWebElement progressBar = ftElement.findFirst("div.progressbar");
+ progressBar.setStyleProperty("width", QString::number(percentageDone) + "%");
+
+ QWebElement progressBarValue = ftElement.findFirst("div.progressbar-value");
+ progressBarValue.setInnerXml(QString::number(percentageDone) + " %");
+}
+
+void QtWebKitChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& /* msg */) {
+ QWebElement ftElement = findDivElementWithID(document_, id);
+ if (ftElement.isNull()) {
+ SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id) << std::endl;
+ return;
+ }
+
+ QString newInnerHTML = "";
+ if (state == ChatWindow::WaitingForAccept) {
+ newInnerHTML = tr("Waiting for other side to accept the transfer.") + "<br/>" +
+ QtWebKitChatView::buildChatWindowButton(tr("Cancel"), QtWebKitChatView::ButtonFileTransferCancel, id);
+ }
+ if (state == ChatWindow::Negotiating) {
+ // replace with text "Negotiaging" + Cancel button
+ newInnerHTML = tr("Negotiating...") + "<br/>" +
+ QtWebKitChatView::buildChatWindowButton(tr("Cancel"), QtWebKitChatView::ButtonFileTransferCancel, id);
+ }
+ else if (state == ChatWindow::Transferring) {
+ // progress bar + Cancel Button
+ newInnerHTML = "<div style=\"position: relative; width: 90%; height: 20px; border: 2px solid grey; -webkit-border-radius: 10px;\">"
+ "<div class=\"progressbar\" style=\"width: 0%; height: 100%; background: #AAA; -webkit-border-radius: 6px;\">"
+ "<div class=\"progressbar-value\" style=\"position: absolute; top: 0px; left: 0px; width: 100%; text-align: center; padding-top: 2px;\">"
+ "0%"
+ "</div>"
+ "</div>"
+ "</div>" +
+ QtWebKitChatView::buildChatWindowButton(tr("Cancel"), QtWebKitChatView::ButtonFileTransferCancel, id);
+ }
+ else if (state == ChatWindow::Canceled) {
+ newInnerHTML = tr("Transfer has been canceled!");
+ }
+ else if (state == ChatWindow::Finished) {
+ // text "Successful transfer"
+ newInnerHTML = tr("Transfer completed successfully.");
+ }
+ else if (state == ChatWindow::FTFailed) {
+ newInnerHTML = tr("Transfer failed.");
+ }
+
+ ftElement.setInnerXml(newInnerHTML);
+}
+
+void QtWebKitChatView::setMUCInvitationJoined(QString id) {
+ QWebElement divElement = findDivElementWithID(document_, id);
+ QWebElement buttonElement = divElement.findFirst("input#mucinvite");
+ if (!buttonElement.isNull()) {
+ buttonElement.setAttribute("value", tr("Return to room"));
+ }
+}
+void QtWebKitChatView::setAckState(std::string const& id, ChatWindow::AckState state) {
+ QString xml;
+ switch (state) {
+ case ChatWindow::Pending:
+ xml = "<img src='qrc:/icons/throbber.gif' title='" + tr("This message has not been received by your server yet.") + "'/>";
+ displayReceiptInfo(P2QSTRING(id), false);
+ break;
+ case ChatWindow::Received:
+ xml = "";
+ displayReceiptInfo(P2QSTRING(id), true);
+ break;
+ case ChatWindow::Failed: xml = "<img src='qrc:/icons/error.png' title='" + tr("This message may not have been transmitted.") + "'/>"; break;
+ }
+ setAckXML(P2QSTRING(id), xml);
+}
+
+void QtWebKitChatView::setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) {
+ QString xml;
+ switch (state) {
+ case ChatWindow::ReceiptReceived:
+ xml = "<img src='qrc:/icons/check.png' title='" + tr("The receipt for this message has been received.") + "'/>";
+ break;
+ case ChatWindow::ReceiptRequested:
+ xml = "<img src='qrc:/icons/warn.png' title='" + tr("The receipt for this message has not yet been received. The recipient(s) might not have received this message.") + "'/>";
+ break;
+ }
+ setReceiptXML(P2QSTRING(id), xml);
+}
+
+QString QtWebKitChatView::linkimoticonify(const QString& message) const {
+ QString messageHTML(message);
+ messageHTML = Qt::escape(messageHTML);
+ QMapIterator<QString, QString> it(emoticons_);
+ QString textStyle = showEmoticons_ ? "style='display:none'" : "";
+ QString imageStyle = showEmoticons_ ? "" : "style='display:none'";
+ if (messageHTML.length() < 500) {
+ while (it.hasNext()) {
+ it.next();
+ messageHTML.replace(it.key(), "<span class='swift_emoticon_image' " + imageStyle + "><img src='" + it.value() + "'/></span><span class='swift_emoticon_text' " + textStyle + ">"+it.key() + "</span>");
+ }
+ messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML)));
+ }
+ messageHTML.replace("\n","<br/>");
+ return messageHTML;
+}
+
+std::string QtWebKitChatView::addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) {
+ return addMessage(linkimoticonify(P2QSTRING(message)), senderName, senderIsSelf, label, avatarPath, style, time);
+}
+
+std::string QtWebKitChatView::addMessage(const QString &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) {
+ SWIFT_LOG(debug) << "[QtWebKitChatView::addMessage] - entered" << std::endl;
+ QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str());
+
+ QString htmlString;
+ if (label) {
+ htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \">").arg(Qt::escape(P2QSTRING(label->getForegroundColor()))).arg(Qt::escape(P2QSTRING(label->getBackgroundColor())));
+ htmlString += QString("%3</span> ").arg(Qt::escape(P2QSTRING(label->getDisplayMarking())));
+ }
+ QString messageHTML(message);
+
+ QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";
+ QString styleSpanEnd = style == "" ? "" : "</span>";
+ htmlString += styleSpanStart + messageHTML + styleSpanEnd;
+ SWIFT_LOG(debug) << "[QtWebKitChatView::addMessage] - message html string: " << Q2PSTRING(htmlString) << std::endl;
+
+ bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf);
+ if (lastLineTracker_.getShouldMoveLastLine()) {
+ /* should this be queued? */
+ addLastSeenLine();
+ /* if the line is added we should break the snippet */
+ appendToPrevious = false;
+ }
+ QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();
+ std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
+ addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+
+ previousMessageWasSelf_ = senderIsSelf;
+ previousSenderName_ = P2QSTRING(senderName);
+ previousMessageKind_ = PreviousMessageWasMessage;
+ SWIFT_LOG(debug) << "[QtWebKitChatView::addMessage] - return: " << id << std::endl;
+ return id;
+}
+
+void QtWebKitChatView::setChatWindowHasFocus(bool focus) {
+ lastLineTracker_.setHasFocus(focus);
+}
+
+std::string QtWebKitChatView::addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) {
+ return addMessage(" *" + message + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time);
+}
+
+std::string formatSize(const boost::uintmax_t bytes) {
+ static const char *siPrefix[] = {"k", "M", "G", "T", "P", "E", "Z", "Y", NULL};
+ int power = 0;
+ double engBytes = bytes;
+ while (engBytes >= 1000) {
+ ++power;
+ engBytes = engBytes / 1000.0;
+ }
+ return str( boost::format("%.1lf %sB") % engBytes % (power > 0 ? siPrefix[power-1] : "") );
+}
+
+QString QtWebKitChatView::buildChatWindowButton(const QString& name, const QString& id, const QString& arg1, const QString& arg2, const QString& arg3) {
+ QRegExp regex("[A-Za-z][A-Za-z0-9\\-\\_]+");
+ Q_ASSERT(regex.exactMatch(id));
+ QString html = QString("<input id='%2' type='submit' value='%1' onclick='chatwindow.buttonClicked(\"%2\", \"%3\", \"%4\", \"%5\");' />").arg(name).arg(id).arg(encodeButtonArgument(arg1)).arg(encodeButtonArgument(arg2)).arg(encodeButtonArgument(arg3));
+ return html;
+}
+
+std::string QtWebKitChatView::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) {
+ qDebug() << "addFileTransfer";
+ QString ft_id = "ft" + P2QSTRING(boost::lexical_cast<std::string>(idCounter_++));
+
+ QString htmlString;
+ QString formattedSize = P2QSTRING(formatSize(sizeInBytes));
+ if (senderIsSelf) {
+ // outgoing
+ htmlString = tr("Send file") + ": " + P2QSTRING(filename) + " ( " + formattedSize + ") </br>" +
+ "<div id='" + ft_id + "'>" +
+ buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) +
+ buildChatWindowButton(tr("Set Description"), ButtonFileTransferSetDescription, ft_id) +
+ buildChatWindowButton(tr("Send"), ButtonFileTransferSendRequest, ft_id) +
+ "</div>";
+ } else {
+ // incoming
+ htmlString = tr("Receiving file") + ": " + P2QSTRING(filename) + " ( " + formattedSize + ") <br/>" +
+ "<div id='" + ft_id + "'>" +
+ buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) +
+ buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) +
+ "</div>";
+ }
+
+
+ bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasFileTransfer, senderName, senderIsSelf);
+ if ( lastLineTracker_.getShouldMoveLastLine()) {
+ /* should this be queued? */
+ addLastSeenLine();
+ /* if the line is added we should break the snippet */
+ appendToPrevious = false;
+ }
+ QString qAvatarPath = "qrc:/icons/avatar.png";
+ std::string id = "ftmessage" + boost::lexical_cast<std::string>(idCounter_++);
+ addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+
+ previousMessageWasSelf_ = senderIsSelf;
+ previousSenderName_ = P2QSTRING(senderName);
+ previousMessageKind_ = PreviousMessageWasFileTransfer;
+ return Q2PSTRING(ft_id);
+}
+
+void QtWebKitChatView::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3) {
+ QString arg1 = decodeButtonArgument(encodedArgument1);
+ QString arg2 = decodeButtonArgument(encodedArgument2);
+ QString arg3 = decodeButtonArgument(encodedArgument3);
+
+ if (id.startsWith(ButtonFileTransferCancel)) {
+ QString ft_id = arg1;
+ onFileTransferCancel(Q2PSTRING(ft_id));
+ }
+ else if (id.startsWith(ButtonFileTransferSetDescription)) {
+ QString ft_id = arg1;
+ bool ok = false;
+ QString text = QInputDialog::getText(this, tr("File transfer description"),
+ tr("Description:"), QLineEdit::Normal, "", &ok);
+ if (ok) {
+ descriptions_[ft_id] = text;
+ }
+ }
+ else if (id.startsWith(ButtonFileTransferSendRequest)) {
+ QString ft_id = arg1;
+ QString text = descriptions_.find(ft_id) == descriptions_.end() ? QString() : descriptions_[ft_id];
+ onFileTransferStart(Q2PSTRING(ft_id), Q2PSTRING(text));
+ }
+ else if (id.startsWith(ButtonFileTransferAcceptRequest)) {
+ QString ft_id = arg1;
+ QString filename = arg2;
+
+ QString path = QFileDialog::getSaveFileName(this, tr("Save File"), filename);
+ if (!path.isEmpty()) {
+ onFileTransferAccept(Q2PSTRING(ft_id), Q2PSTRING(path));
+ }
+ }
+ else if (id.startsWith(ButtonMUCInvite)) {
+ QString roomJID = arg1;
+ QString password = arg2;
+ QString elementID = arg3;
+
+ eventStream_->send(boost::make_shared<JoinMUCUIEvent>(Q2PSTRING(roomJID), Q2PSTRING(password)));
+ setMUCInvitationJoined(elementID);
+ }
+ else {
+ SWIFT_LOG(debug) << "Unknown HTML button! ( " << Q2PSTRING(id) << " )" << std::endl;
+ }
+}
+
+void QtWebKitChatView::addErrorMessage(const std::string& errorMessage) {
+ QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage)));
+ errorMessageHTML.replace("\n","<br/>");
+ addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_)));
+
+ previousMessageWasSelf_ = false;
+ previousMessageKind_ = PreviousMessageWasSystem;
+}
+
+void QtWebKitChatView::addSystemMessage(const std::string& message) {
+ QString messageHTML(Qt::escape(P2QSTRING(message)));
+ messageHTML = linkimoticonify(messageHTML);
+ addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
+
+ previousMessageWasSelf_ = false;
+ previousMessageKind_ = PreviousMessageWasSystem;
+}
+
+void QtWebKitChatView::addPresenceMessage(const std::string& message) {
+ QString messageHTML(linkimoticonify(P2QSTRING(message)));
+ addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
+
+ previousMessageWasSelf_ = false;
+ previousMessageKind_ = PreviousMessageWasPresence;
+}
+
+void QtWebKitChatView::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) {
+
+ QString htmlString = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + " <br/>";
+ if (!reason.empty()) {
+ htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "<br/>";
+ }
+ if (!direct) {
+ htmlString += QObject::tr("This person may not have really sent this invitation!") + "<br/>";
+ }
+
+ QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));
+
+ htmlString += "<div id='" + id + "'>" +
+ buildChatWindowButton(tr("Accept Invite"), ButtonMUCInvite, Qt::escape(P2QSTRING(jid.toString())), Qt::escape(P2QSTRING(password)), id) +
+ "</div>";
+
+ bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMUCInvite, senderName, false);
+ if (lastLineTracker_.getShouldMoveLastLine()) {
+ /* should this be queued? */
+ addLastSeenLine();
+ /* if the line is added we should break the snippet */
+ appendToPrevious = false;
+ }
+ QString qAvatarPath = "qrc:/icons/avatar.png";
+
+ addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id)));
+
+ previousMessageWasSelf_ = false;
+ previousSenderName_ = P2QSTRING(senderName);
+ previousMessageKind_ = PreviousMessageWasMUCInvite;
+}
+
+}
diff --git a/Swift/QtUI/QtWebKitChatView.h b/Swift/QtUI/QtWebKitChatView.h
new file mode 100644
index 0000000..45585c8
--- /dev/null
+++ b/Swift/QtUI/QtWebKitChatView.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+/*
+ * Copyright (c) 2012 Thilo Cestonaro
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#ifndef SWIFT_QtWebKitChatView_H
+#define SWIFT_QtWebKitChatView_H
+
+#include <QString>
+#include <QWidget>
+#include <QList>
+#include <QWebElement>
+#include <QInputDialog>
+#include <QFileDialog>
+
+#include <boost/shared_ptr.hpp>
+
+#include "MessageSnippet.h"
+
+#include "QtChatView.h"
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include "Swift/Controllers/Settings/SettingsProvider.h"
+
+#include <SwifTools/LastLineTracker.h>
+
+class QWebPage;
+class QUrl;
+
+namespace Swift {
+ class QtWebView;
+ class QtChatTheme;
+ class UIEventStream;
+ class QtChatWindowJSBridge;
+
+ class QtWebKitChatView : public QtChatView {
+ Q_OBJECT
+
+ public:
+ static const QString ButtonFileTransferCancel;
+ static const QString ButtonFileTransferSetDescription;
+ static const QString ButtonFileTransferSendRequest;
+ static const QString ButtonFileTransferAcceptRequest;
+ static const QString ButtonMUCInvite;
+
+ public:
+ QtWebKitChatView(QtChatTheme* theme, QWidget* parent, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons);
+ virtual ~QtWebKitChatView();
+ void addMessage(boost::shared_ptr<ChatSnippet> snippet);
+ void addLastSeenLine();
+ void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time);
+ void replaceLastMessage(const QString& newMessage);
+ void replaceLastMessage(const QString& newMessage, const QString& note);
+ void replaceMessage(const std::string& newMessage, const QString& id, const QDateTime& time, const QString& style);
+ void replaceMessage(const QString& message, const QString& id, const QDateTime& time, const QString& style);
+ void rememberScrolledToBottom();
+ void setAckXML(const QString& id, const QString& xml);
+ void setReceiptXML(const QString& id, const QString& xml);
+ void displayReceiptInfo(const QString& id, bool showIt);
+ QString linkimoticonify(const QString& message) const;
+
+ QString getLastSentMessage();
+ void addToJSEnvironment(const QString&, QObject*);
+ void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg);
+ void setFileTransferProgress(QString id, const int percentageDone);
+ void setMUCInvitationJoined(QString id);
+ void showEmoticons(bool show);
+ void setAckState(std::string const& id, ChatWindow::AckState);
+ void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state);
+ std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time);
+ std::string addMessage(const QString& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time);
+ void setChatWindowHasFocus(bool focus);
+ std::string addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
+ std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes);
+ void addErrorMessage(const std::string& errorMessage);
+ void addSystemMessage(const std::string& message);
+ void addPresenceMessage(const std::string& message);
+ void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct);
+
+ static QString buildChatWindowButton(const QString& name, const QString& id, const QString& arg1 = QString(), const QString& arg2 = QString(), const QString& arg3 = QString());
+
+ public slots:
+ void copySelectionToClipboard();
+ void scrollToBottom();
+ void handleLinkClicked(const QUrl&);
+ void handleKeyPressEvent(QKeyEvent* event);
+ void resetView();
+ void increaseFontSize(int numSteps = 1);
+ void decreaseFontSize();
+ void resizeFont(int fontSizeSteps);
+
+ private slots:
+ void handleViewLoadFinished(bool);
+ void handleFrameSizeChanged();
+ void handleClearRequested();
+ void handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3);
+
+ private:
+ enum PreviousMessageKind {
+ PreviosuMessageWasNone,
+ PreviousMessageWasMessage,
+ PreviousMessageWasSystem,
+ PreviousMessageWasPresence,
+ PreviousMessageWasFileTransfer,
+ PreviousMessageWasMUCInvite
+ };
+
+ void headerEncode();
+ void messageEncode();
+ void addToDOM(boost::shared_ptr<ChatSnippet> snippet);
+ QWebElement snippetToDOM(boost::shared_ptr<ChatSnippet> snippet);
+ bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const;
+ void handleSettingChanged(const std::string& setting);
+
+ bool viewReady_;
+ bool isAtBottom_;
+ QtWebView* webView_;
+ QWebPage* webPage_;
+ int fontSizeSteps_;
+ QtChatTheme* theme_;
+ QWebElement newInsertPoint_;
+ QWebElement lineSeparator_;
+ QWebElement lastElement_;
+ QWebElement document_;
+ bool previousMessageWasSelf_;
+ QString previousSenderName_;
+ PreviousMessageKind previousMessageKind_;
+ int idCounter_;
+ LastLineTracker lastLineTracker_;
+ UIEventStream* eventStream_;
+ std::map<QString, QString> descriptions_;
+ QtChatWindowJSBridge* jsBridge_;
+ SettingsProvider* settings_;
+ QMap<QString, QString> emoticons_;
+ bool showEmoticons_;
+ };
+}
+
+#endif
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 064faab..9070291 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -73,6 +73,7 @@ sources = [
"QtAvatarWidget.cpp",
"QtUIFactory.cpp",
"QtChatWindowFactory.cpp",
+ "QtChatViewFactory.cpp",
"QtChatWindow.cpp",
"QtClickableLabel.cpp",
"QtLoginWindow.cpp",
@@ -84,7 +85,7 @@ sources = [
"QtScaledAvatarCache.cpp",
"QtSwift.cpp",
"QtURIHandler.cpp",
- "QtChatView.cpp",
+ "QtWebKitChatView.cpp",
"QtChatTheme.cpp",
"QtChatTabs.cpp",
"QtSoundPlayer.cpp",
@@ -150,7 +151,9 @@ sources = [
"QtChatWindowJSBridge.cpp",
"QtMUCConfigurationWindow.cpp",
"QtAffiliationEditor.cpp",
- "QtUISettingConstants.cpp"
+ "QtUISettingConstants.cpp",
+ "QtChatView.cpp",
+ "QtBarriersFreeChatView.cpp",
]
myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
diff --git a/Swift/QtUI/ChatView/ChatView.pro b/Swift/QtUI/WebKitChatView/WebKitChatView.pro
index 3017d35..25a6dd0 100644
--- a/Swift/QtUI/ChatView/ChatView.pro
+++ b/Swift/QtUI/WebKitChatView/WebKitChatView.pro
@@ -3,6 +3,6 @@ TARGET = ChatView
DEPENDPATH += .
INCLUDEPATH += . ../../../Swiften/3rdParty/Boost
QT += webkit network
-HEADERS += ../QtChatView.h
-SOURCES += main.cpp ../QtChatView.cpp ../ChatSnippet.cpp ../MessageSnippet.cpp ../SystemMessageSnippet.cpp
+HEADERS += ../QtWebKitChatView.h
+SOURCES += main.cpp ../QtWebKitChatView.cpp ../ChatSnippet.cpp ../MessageSnippet.cpp ../SystemMessageSnippet.cpp
RESOURCES += ../DefaultTheme.qrc ../Swift.qrc
diff --git a/Swift/QtUI/ChatView/main.cpp b/Swift/QtUI/WebKitChatView/main.cpp
index 0f53432..0f53432 100644
--- a/Swift/QtUI/ChatView/main.cpp
+++ b/Swift/QtUI/WebKitChatView/main.cpp