summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Maudsley <richard.maudsley@isode.com>2014-01-02 12:21:41 (GMT)
committerSwift Review <review@swift.im>2014-01-15 11:05:41 (GMT)
commit09108f5ac3c1a6567730b5bbd0c847f8422ff4a2 (patch)
treeb7054e421da9dd8227b3bbe11b2ea20575b760df
parent9cf1fe4ec5d3729ceb53385aeb245a4a688823e0 (diff)
downloadswift-09108f5ac3c1a6567730b5bbd0c847f8422ff4a2.zip
swift-09108f5ac3c1a6567730b5bbd0c847f8422ff4a2.tar.bz2
Improved plaintext chat view for screen-reader support.
Change-Id: Ib7c10350b56683db95f4c6be49f79f8a9d9e80ec
-rw-r--r--Swift/QtUI/QtChatWindow.cpp2
-rw-r--r--Swift/QtUI/QtChatWindow.h5
-rw-r--r--Swift/QtUI/QtFileTransferListItemModel.cpp5
-rw-r--r--Swift/QtUI/QtHighlightRulesItemModel.cpp4
-rw-r--r--Swift/QtUI/QtPlainChatView.cpp340
-rw-r--r--Swift/QtUI/QtPlainChatView.h98
-rw-r--r--Swift/QtUI/QtWebKitChatView.cpp17
-rw-r--r--Swiften/Base/FileSize.cpp23
-rw-r--r--Swiften/Base/FileSize.h17
-rw-r--r--Swiften/Base/SConscript3
10 files changed, 461 insertions, 53 deletions
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index d4b1d0f..826ec9e 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -109,7 +109,7 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
logRosterSplitter_->setAutoFillBackground(true);
layout->addWidget(logRosterSplitter_);
if (settings_->getSetting(QtUISettingConstants::USE_PLAIN_CHATS) || settings_->getSetting(QtUISettingConstants::USE_SCREENREADER)) {
- messageLog_ = new QtPlainChatView(this);
+ messageLog_ = new QtPlainChatView(this, eventStream_);
}
else {
messageLog_ = new QtWebKitChatView(this, eventStream_, theme, this); // I accept that passing the ChatWindow in so that the view can call the signals is somewhat inelegant, but it saves a lot of boilerplate. This patch is unpleasant enough already. So let's fix this soon (it at least needs fixing by the time history is sorted), but not now.
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index ca0ecad..df1e619 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2013 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -41,9 +41,6 @@ namespace Swift {
class QtChatWindowJSBridge;
class SettingsProvider;
- // FIXME: Move this to a different file
- std::string formatSize(const boost::uintmax_t bytes);
-
class LabelModel : public QAbstractListModel {
Q_OBJECT
public:
diff --git a/Swift/QtUI/QtFileTransferListItemModel.cpp b/Swift/QtUI/QtFileTransferListItemModel.cpp
index 00afacb..b9b9fd1 100644
--- a/Swift/QtUI/QtFileTransferListItemModel.cpp
+++ b/Swift/QtUI/QtFileTransferListItemModel.cpp
@@ -9,9 +9,8 @@
#include <boost/bind.hpp>
#include <boost/cstdint.hpp>
-#include "QtChatWindow.h" // for formatSize
-
#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Base/FileSize.h>
#include <Swift/Controllers/FileTransfer/FileTransferController.h>
#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
#include "QtSwiftUtil.h"
@@ -109,7 +108,7 @@ int QtFileTransferListItemModel::rowCount(const QModelIndex& /* parent */) const
}
QModelIndex QtFileTransferListItemModel::index(int row, int column, const QModelIndex& /* parent */) const {
- return createIndex(row, column, (void*) 0);
+ return createIndex(row, column, static_cast<void*>(0));
}
}
diff --git a/Swift/QtUI/QtHighlightRulesItemModel.cpp b/Swift/QtUI/QtHighlightRulesItemModel.cpp
index 4efa712..fcbaddc 100644
--- a/Swift/QtUI/QtHighlightRulesItemModel.cpp
+++ b/Swift/QtUI/QtHighlightRulesItemModel.cpp
@@ -206,7 +206,7 @@ bool QtHighlightRulesItemModel::setData(const QModelIndex &index, const QVariant
highlightManager_->setRule(index.row(), r);
emit dataChanged(index, index);
foreach (int column, changedColumns) {
- QModelIndex i = createIndex(index.row(), column, (void*) 0);
+ QModelIndex i = createIndex(index.row(), column, static_cast<void*>(0));
emit dataChanged(i, i);
}
}
@@ -227,7 +227,7 @@ int QtHighlightRulesItemModel::rowCount(const QModelIndex& /* parent */) const
QModelIndex QtHighlightRulesItemModel::index(int row, int column, const QModelIndex& /* parent */) const
{
- return createIndex(row, column, (void*) 0);
+ return createIndex(row, column, static_cast<void*>(0));
}
bool QtHighlightRulesItemModel::insertRows(int row, int count, const QModelIndex& /* parent */)
diff --git a/Swift/QtUI/QtPlainChatView.cpp b/Swift/QtUI/QtPlainChatView.cpp
index 98d2e8b..ee76438 100644
--- a/Swift/QtUI/QtPlainChatView.cpp
+++ b/Swift/QtUI/QtPlainChatView.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 Kevin Smith
+ * Copyright (c) 2013-2014 Kevin Smith and Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -7,29 +7,40 @@
#include <Swift/QtUI/QtPlainChatView.h>
#include <QTextEdit>
+#include <QScrollBar>
#include <QVBoxLayout>
+#include <QPushButton>
+#include <QLabel>
+#include <QDialog>
+#include <QProgressBar>
+#include <QFileDialog>
+#include <QInputDialog>
+#include <QMenu>
#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/FileSize.h>
+
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include <Swift/QtUI/ChatSnippet.h>
#include <Swift/QtUI/QtSwiftUtil.h>
#include <Swift/QtUI/QtUtilities.h>
-
namespace Swift {
-QtPlainChatView::QtPlainChatView(QWidget* parent) : QtChatView(parent) {
+QtPlainChatView::QtPlainChatView(QtChatWindow *window, UIEventStream* eventStream)
+: QtChatView(window), window_(window), eventStream_(eventStream), idGenerator_(0) {
QVBoxLayout* mainLayout = new QVBoxLayout(this);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0,0,0,0);
- log_ = new QTextEdit(this);
+ log_ = new LogTextEdit(this);
log_->setReadOnly(true);
log_->setAccessibleName(tr("Chat Messages"));
mainLayout->addWidget(log_);
}
QtPlainChatView::~QtPlainChatView() {
-
}
QString chatMessageToString(const ChatWindow::ChatMessage& message) {
@@ -73,8 +84,10 @@ std::string QtPlainChatView::addMessage(const ChatWindow::ChatMessage& message,
text += chatMessageToString(message);
text += "</p>";
log_->append(text);
- return "";
-};
+ const std::string idx = senderIsSelf ? "" : senderName;
+ lastMessageLabel_[idx] = label;
+ return idx;
+}
std::string QtPlainChatView::addAction(const ChatWindow::ChatMessage& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& /*avatarPath*/, const boost::posix_time::ptime& time, const HighlightAction& /*highlight*/) {
QString text = "<p>";
@@ -82,12 +95,319 @@ std::string QtPlainChatView::addAction(const ChatWindow::ChatMessage& message, c
text += P2QSTRING(label->getLabel()) + "<br/>";
}
QString name = senderIsSelf ? "you" : P2QSTRING(senderName);
- text += QString(tr("At %1 %2 ")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name);
+ text += QString(tr("At %1 <i>%2 ")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name);
+ text += chatMessageToString(message);
+ text += "</i></p>";
+ log_->append(text);
+ const std::string idx = senderIsSelf ? "" : senderName;
+ lastMessageLabel_[idx] = label;
+ return idx;
+}
+
+void QtPlainChatView::addSystemMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction /*direction*/)
+{
+ QString text = "<p><i>";
+ text += chatMessageToString(message);
+ text += "</i></p>";
+ log_->append(text);
+}
+
+void QtPlainChatView::addPresenceMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction /*direction*/)
+{
+ QString text = "<p><i>";
+ text += chatMessageToString(message);
+ text += "</i></p>";
+ log_->append(text);
+}
+
+void QtPlainChatView::addErrorMessage(const ChatWindow::ChatMessage& message)
+{
+ QString text = "<p><i>";
+ text += chatMessageToString(message);
+ text += "</i></p>";
+ log_->append(text);
+}
+
+void QtPlainChatView::replaceMessage(const ChatWindow::ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& /*highlight*/)
+{
+ QString text = "<p>";
+ if (lastMessageLabel_[id]) {
+ text += P2QSTRING(lastMessageLabel_[id]->getLabel()) + "<br/>";
+ }
+ QString name = id.empty() ? "you" : P2QSTRING(id);
+ text += QString(tr("At %1 %2 corrected the last message to:")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name) + "<br/>";
+ text += chatMessageToString(message);
+ text += "</p>";
+ log_->append(text);
+}
+
+void QtPlainChatView::replaceWithAction(const ChatWindow::ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& /*highlight*/)
+{
+ QString text = "<p>";
+ if (lastMessageLabel_[id]) {
+ text += P2QSTRING(lastMessageLabel_[id]->getLabel()) + "<br/>";
+ }
+ QString name = id.empty() ? "you" : P2QSTRING(id);
+ text += QString(tr("At %1 %2 corrected the last action to: <i>")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name);
+ text += chatMessageToString(message);
+ text += "</i></p>";
+ log_->append(text);
+}
+
+void QtPlainChatView::replaceLastMessage(const ChatWindow::ChatMessage& message)
+{
+ QString text = "<p>The last message was corrected to:<br/>";
text += chatMessageToString(message);
text += "</p>";
log_->append(text);
- return "";
-};
+}
+
+void QtPlainChatView::setAckState(const std::string& /*id*/, ChatWindow::AckState state)
+{
+ if (state == ChatWindow::Failed) {
+ addSystemMessage(ChatWindow::ChatMessage("Message delivery failed due to disconnection from server."), ChatWindow::DefaultDirection);
+ }
+}
+
+std::string QtPlainChatView::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes)
+{
+ const std::string ftId = "ft" + boost::lexical_cast<std::string>(idGenerator_++);
+ const std::string sizeString = formatSize(sizeInBytes);
+ FileTransfer* transfer;
+ if (senderIsSelf) {
+ QString description = QInputDialog::getText(this, tr("File transfer description"),
+ tr("Description:"), QLineEdit::Normal, "");
+ /* NOTE: it is not possible to abort if description is not provided, since we must always return a valid transfer id */
+ const std::string message = std::string() + "Confirm file transfer: <i>" + filename + " (" + sizeString + " bytes)</i>";
+ transfer = new FileTransfer(this, senderIsSelf, ftId, filename, ChatWindow::WaitingForAccept, Q2PSTRING(description), message, true);
+ addSystemMessage(ChatWindow::ChatMessage("Preparing to start file transfer..."), ChatWindow::DefaultDirection);
+ } else { /* incoming transfer */
+ const std::string message = std::string() + "Incoming file transfer: <i>" + filename + " (" + sizeString + " bytes)</i>";
+ transfer = new FileTransfer(this, senderIsSelf, ftId, filename, ChatWindow::WaitingForAccept, "", message, true);
+ addSystemMessage("Incoming file transfer from " + senderName + "...", ChatWindow::DefaultDirection);
+ }
+
+ fileTransfers_[ftId] = transfer;
+ layout()->addWidget(transfer->dialog_);
+
+ return ftId;
+}
+
+void QtPlainChatView::setFileTransferProgress(std::string id, const int percentageDone)
+{
+ FileTransferMap::iterator transfer = fileTransfers_.find(id);
+ if (transfer != fileTransfers_.end()) {
+ transfer->second->bar_->setValue(percentageDone);
+ }
+}
+
+void QtPlainChatView::setFileTransferStatus(std::string id, const ChatWindow::FileTransferState state, const std::string& msg)
+{
+ FileTransferMap::iterator transferIter = fileTransfers_.find(id);
+ if (transferIter == fileTransfers_.end()) {
+ return;
+ }
+
+ /* store the layout index so we can restore it to the same location */
+ FileTransfer* oldTransfer = transferIter->second;
+ const int layoutIndex = layout()->indexOf(oldTransfer->dialog_);
+ layout()->removeWidget(oldTransfer->dialog_);
+ const std::string &label = (!msg.empty() ? msg : oldTransfer->message_);
+ FileTransfer* transfer = new FileTransfer(this, oldTransfer->senderIsSelf_, oldTransfer->ftId_, oldTransfer->filename_, state, oldTransfer->description_, label, false);
+ fileTransfers_[oldTransfer->ftId_] = transfer; /* replace the transfer object for this file id */
+ delete oldTransfer;
+
+ /* insert the new dialog at the old position in the layout list */
+ QBoxLayout* parentLayout = dynamic_cast<QBoxLayout*>(layout());
+ assert(parentLayout);
+ parentLayout->insertWidget(layoutIndex, transfer->dialog_);
+
+ /* log the transfer end result as a system message */
+ if (state == ChatWindow::Finished) {
+ addSystemMessage(ChatWindow::ChatMessage("The file transfer completed successfully."), ChatWindow::DefaultDirection);
+ } else if (state == ChatWindow::Canceled) {
+ addSystemMessage(ChatWindow::ChatMessage("The file transfer was canceled."), ChatWindow::DefaultDirection);
+ } else if (state == ChatWindow::FTFailed) {
+ addSystemMessage(ChatWindow::ChatMessage("The file transfer failed."), ChatWindow::DefaultDirection);
+ }
+}
+
+void QtPlainChatView::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& /*reason*/, const std::string& password, bool /*direct*/, bool isImpromptu, bool isContinuation)
+{
+ PopupDialog *invite = new PopupDialog(this);
+
+ QLabel* statusLabel = new QLabel;
+ std::string msg = senderName + " has invited you to join " + jid.toString() + "...";
+ statusLabel->setText(P2QSTRING(msg));
+ invite->layout_->addWidget(statusLabel);
+ invite->layout_->addWidget(new QLabel); /* padding */
+
+ AcceptMUCInviteAction* accept = new AcceptMUCInviteAction(invite, "Accept", jid, senderName, password, isImpromptu, isContinuation);
+ connect(accept, SIGNAL(clicked()), SLOT(acceptMUCInvite()));
+ invite->layout_->addWidget(accept);
+
+ AcceptMUCInviteAction* reject = new AcceptMUCInviteAction(invite, "Reject", jid, senderName, password, isImpromptu, isContinuation);
+ connect(reject, SIGNAL(clicked()), SLOT(rejectMUCInvite()));
+ invite->layout_->addWidget(reject);
+ statusLabel->setText(P2QSTRING(msg));
+
+ layout()->addWidget(invite->dialog_);
+
+ addSystemMessage(ChatWindow::ChatMessage(msg), ChatWindow::DefaultDirection);
+}
+
+void QtPlainChatView::scrollToBottom()
+{
+ log_->ensureCursorVisible();
+ log_->verticalScrollBar()->setValue(log_->verticalScrollBar()->maximum());
+}
+
+void QtPlainChatView::fileTransferAccept()
+{
+ FileTransfer::Action* action = dynamic_cast<FileTransfer::Action*>(sender());
+ if (!action) {
+ return;
+ }
+
+ FileTransferMap::iterator transferIter = fileTransfers_.find(action->id_);
+ if (transferIter == fileTransfers_.end()) {
+ return;
+ }
+
+ FileTransfer* transfer = transferIter->second;
+
+ if (transfer->senderIsSelf_) { /* if we are the sender, kick of the transfer */
+ window_->onFileTransferStart(transfer->ftId_, transfer->description_);
+ } else { /* ask the user where to save the file first */
+ QString path = QFileDialog::getSaveFileName(this, tr("Save File"), P2QSTRING(transfer->filename_));
+ if (path.isEmpty()) {
+ fileTransferReject(); /* because the user did not select a desintation path */
+ return;
+ }
+ window_->onFileTransferAccept(transfer->ftId_, Q2PSTRING(path));
+ }
+
+ setFileTransferStatus(transfer->ftId_, ChatWindow::Negotiating, transfer->message_);
+}
+
+void QtPlainChatView::fileTransferReject()
+{
+ FileTransfer::Action* action = dynamic_cast<FileTransfer::Action*>(sender());
+ if (action) {
+ window_->onFileTransferCancel(action->id_);
+ fileTransferFinish();
+ }
+}
+
+void QtPlainChatView::fileTransferFinish()
+{
+ FileTransfer::Action* action = dynamic_cast<FileTransfer::Action*>(sender());
+ if (action) {
+ FileTransferMap::iterator transferIter = fileTransfers_.find(action->id_);
+ if (transferIter != fileTransfers_.end()) {
+ delete transferIter->second; /* cause the dialog to close */
+ fileTransfers_.erase(transferIter);
+ }
+ }
+}
+
+void QtPlainChatView::acceptMUCInvite()
+{
+ AcceptMUCInviteAction *action = dynamic_cast<AcceptMUCInviteAction*>(sender());
+ if (action) {
+ eventStream_->send(boost::make_shared<JoinMUCUIEvent>(action->jid_.toString(), action->password_, boost::optional<std::string>(), false, false, action->isImpromptu_, action->isContinuation_));
+ delete action->parent_;
+ }
+}
+
+void QtPlainChatView::rejectMUCInvite()
+{
+ AcceptMUCInviteAction *action = dynamic_cast<AcceptMUCInviteAction*>(sender());
+ if (action) {
+ /* NOTE: no action required to reject an invite? */
+ delete action->parent_;
+ }
+}
+
+QtPlainChatView::FileTransfer::FileTransfer(QtPlainChatView* parent, bool senderIsSelf, const std::string& ftId, const std::string& filename, const ChatWindow::FileTransferState state, const std::string &desc, const std::string& msg, bool initializing)
+: PopupDialog(parent), bar_(0), senderIsSelf_(senderIsSelf), ftId_(ftId), filename_(filename), description_(desc), message_(msg), initializing_(initializing)
+{
+ QHBoxLayout* layout = new QHBoxLayout;
+ QLabel* statusLabel = new QLabel;
+ layout_->addWidget(statusLabel);
+ layout_->addWidget(new QLabel); /* padding */
+
+ if (initializing_) {
+ FileTransfer::Action* accept = new FileTransfer::Action(senderIsSelf?"Confirm":"Accept", ftId);
+ parent->connect(accept, SIGNAL(clicked()), SLOT(fileTransferAccept()));
+ layout_->addWidget(accept);
+ FileTransfer::Action* reject = new FileTransfer::Action(senderIsSelf?"Cancel":"Reject", ftId);
+ parent->connect(reject, SIGNAL(clicked()), SLOT(fileTransferReject()));
+ layout_->addWidget(reject);
+ statusLabel->setText(P2QSTRING(msg));
+ return;
+ }
+
+ std::string status = msg;
+
+ switch (state) {
+ case ChatWindow::WaitingForAccept: {
+ status = "Waiting for user to accept <i>" + filename + "</i>...";
+ FileTransfer::Action* cancel = new FileTransfer::Action("Cancel", ftId);
+ parent->connect(cancel, SIGNAL(clicked()), SLOT(fileTransferReject()));
+ layout_->addWidget(cancel);
+ break;
+ }
+ case ChatWindow::Negotiating: {
+ status = "Preparing to transfer <i>" + filename + "</i>...";
+ FileTransfer::Action* cancel = new FileTransfer::Action("Cancel", ftId);
+ parent->connect(cancel, SIGNAL(clicked()), SLOT(fileTransferReject()));
+ layout_->addWidget(cancel);
+ break;
+ }
+ case ChatWindow::Transferring: {
+ status = "Transferring <i>" + filename + "</i>...";
+ bar_ = new QProgressBar;
+ bar_->setRange(0, 100);
+ bar_->setValue(0);
+ layout->addWidget(bar_);
+ FileTransfer::Action* cancel = new FileTransfer::Action("Cancel", ftId);
+ parent->connect(cancel, SIGNAL(clicked()), SLOT(fileTransferReject()));
+ layout_->addWidget(cancel);
+ break;
+ }
+ case ChatWindow::Canceled: {
+ status = "File <i>" + filename + "</i> was canceled.";
+ FileTransfer::Action* finish = new FileTransfer::Action("Hide", ftId);
+ parent->connect(finish, SIGNAL(clicked()), SLOT(fileTransferFinish()));
+ layout_->addWidget(finish);
+ break;
+ }
+ case ChatWindow::Finished: {
+ status = "File <i>" + filename + "</i> was transfered successfully.";
+ FileTransfer::Action* finish = new FileTransfer::Action("Hide", ftId);
+ parent->connect(finish, SIGNAL(clicked()), SLOT(fileTransferFinish()));
+ layout_->addWidget(finish);
+ break;
+ }
+ case ChatWindow::FTFailed: {
+ status = "File transfer failed: <i>" + filename + "</i>";
+ FileTransfer::Action* finish = new FileTransfer::Action("Hide", ftId);
+ parent->connect(finish, SIGNAL(clicked()), SLOT(fileTransferFinish()));
+ layout_->addWidget(finish);
+ break;
+ }
+ }
+
+ statusLabel->setText(P2QSTRING(status));
+}
+
+void QtPlainChatView::LogTextEdit::contextMenuEvent(QContextMenuEvent *event)
+{
+ QMenu *menu = createStandardContextMenu();
+ menu->exec(event->globalPos());
+ delete menu;
+}
}
diff --git a/Swift/QtUI/QtPlainChatView.h b/Swift/QtUI/QtPlainChatView.h
index c475862..01b5925 100644
--- a/Swift/QtUI/QtPlainChatView.h
+++ b/Swift/QtUI/QtPlainChatView.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 Kevin Smith
+ * Copyright (c) 2013-2014 Kevin Smith and Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -11,12 +11,15 @@
#include <boost/date_time/posix_time/posix_time.hpp>
#include <QWidget>
+#include <QTextEdit>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
#include <Swift/QtUI/QtChatView.h>
+#include <Swift/QtUI/QtChatWindow.h>
class QTextEdit;
+class QProgressBar;
namespace Swift {
class HighlightAction;
@@ -25,7 +28,7 @@ namespace Swift {
class QtPlainChatView : public QtChatView {
Q_OBJECT
public:
- QtPlainChatView(QWidget* parent);
+ QtPlainChatView(QtChatWindow *window, UIEventStream* eventStream);
virtual ~QtPlainChatView();
/** Add message to window.
@@ -37,19 +40,19 @@ namespace Swift {
*/
virtual std::string addAction(const ChatWindow::ChatMessage& /*message*/, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/);
- virtual void addSystemMessage(const ChatWindow::ChatMessage& /*message*/, ChatWindow::Direction /*direction*/) {};
- virtual void addPresenceMessage(const ChatWindow::ChatMessage& /*message*/, ChatWindow::Direction /*direction*/) {};
-
- virtual void addErrorMessage(const ChatWindow::ChatMessage& /*message*/) {};
- virtual void replaceMessage(const ChatWindow::ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {};
- virtual void replaceWithAction(const ChatWindow::ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {};
- virtual void replaceLastMessage(const ChatWindow::ChatMessage& /*message*/) {};
- virtual void setAckState(const std::string& /*id*/, ChatWindow::AckState /*state*/) {};
-
- virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/, const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/) {return "";};
- virtual void setFileTransferProgress(std::string, const int /*percentageDone*/) {};
- virtual void setFileTransferStatus(std::string, const ChatWindow::FileTransferState /*state*/, const std::string& /*msg*/ = "") {};
- virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool /*direct*/, bool /*isImpromptu*/, bool /*isContinuation*/) {};
+ virtual void addSystemMessage(const ChatWindow::ChatMessage& /*message*/, ChatWindow::Direction /*direction*/);
+ virtual void addPresenceMessage(const ChatWindow::ChatMessage& /*message*/, ChatWindow::Direction /*direction*/);
+ virtual void addErrorMessage(const ChatWindow::ChatMessage& /*message*/);
+
+ virtual void replaceMessage(const ChatWindow::ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/);
+ virtual void replaceWithAction(const ChatWindow::ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/);
+ virtual void replaceLastMessage(const ChatWindow::ChatMessage& /*message*/);
+ virtual void setAckState(const std::string& /*id*/, ChatWindow::AckState /*state*/);
+
+ virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/, const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/);
+ virtual void setFileTransferProgress(std::string, const int /*percentageDone*/);
+ virtual void setFileTransferStatus(std::string, const ChatWindow::FileTransferState /*state*/, const std::string& /*msg*/ = "");
+ virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool /*direct*/, bool /*isImpromptu*/, bool /*isContinuation*/);
virtual std::string addWhiteboardRequest(const QString& /*contact*/, bool /*senderIsSelf*/) {return "";};
virtual void setWhiteboardSessionStatus(const std::string& /*id*/, const ChatWindow::WhiteboardSessionState /*state*/) {};
virtual void setMessageReceiptState(const std::string& /*id*/, ChatWindow::ReceiptState /*state*/) {};
@@ -59,11 +62,72 @@ namespace Swift {
public slots:
virtual void resizeFont(int /*fontSizeSteps*/) {};
- virtual void scrollToBottom() {};
+ virtual void scrollToBottom();
virtual void handleKeyPressEvent(QKeyEvent* /*event*/) {};
+ virtual void fileTransferAccept();
+ virtual void fileTransferReject();
+ virtual void fileTransferFinish();
+ virtual void acceptMUCInvite();
+ virtual void rejectMUCInvite();
private:
- QTextEdit* log_;
+ struct PopupDialog {
+ PopupDialog(QtPlainChatView* parent) {
+ dialog_ = new QFrame(parent);
+ dialog_->setFrameShape(QFrame::Panel);
+ dialog_->setFrameShadow(QFrame::Raised);
+ layout_ = new QHBoxLayout;
+ dialog_->setLayout(layout_);
+ }
+ virtual ~PopupDialog() {
+ delete dialog_;
+ }
+ QFrame* dialog_;
+ QHBoxLayout* layout_;
+ };
+
+ struct AcceptMUCInviteAction : public QPushButton {
+ AcceptMUCInviteAction(PopupDialog* parent, const std::string& text, const JID& jid, const std::string& senderName, const std::string& password, bool isImpromptu, bool isContinuation)
+ : QPushButton(P2QSTRING(text)), parent_(parent), jid_(jid), senderName_(senderName), password_(password), isImpromptu_(isImpromptu), isContinuation_(isContinuation) {}
+ PopupDialog *parent_;
+ JID jid_;
+ std::string senderName_;
+ std::string password_;
+ bool isImpromptu_;
+ bool isContinuation_;
+ };
+
+ struct FileTransfer : public PopupDialog {
+ struct Action : QPushButton {
+ Action(const std::string& text, const std::string& id)
+ : QPushButton(P2QSTRING(text)), id_(id) {}
+ std::string id_;
+ };
+ FileTransfer(QtPlainChatView* parent, bool senderIsSelf, const std::string& ftId, const std::string& filename, const ChatWindow::FileTransferState state, const std::string& desc, const std::string& msg, bool initializing);
+ QProgressBar* bar_;
+ bool senderIsSelf_;
+ std::string ftId_;
+ std::string filename_;
+ std::string description_;
+ std::string message_;
+ bool initializing_;
+ };
+
+ class LogTextEdit : public QTextEdit {
+ public:
+ LogTextEdit(QWidget* parent) : QTextEdit(parent) {}
+ virtual ~LogTextEdit() {}
+ virtual void contextMenuEvent(QContextMenuEvent *event);
+ };
+
+ typedef std::map<std::string, FileTransfer*> FileTransferMap;
+ QtChatWindow* window_;
+ UIEventStream* eventStream_;
+ LogTextEdit* log_;
+ QMenu* logMenu_;
+ FileTransferMap fileTransfers_;
+ std::map<std::string, boost::shared_ptr<SecurityLabel> > lastMessageLabel_;
+ int idGenerator_;
};
}
diff --git a/Swift/QtUI/QtWebKitChatView.cpp b/Swift/QtUI/QtWebKitChatView.cpp
index af21609..d1e250c 100644
--- a/Swift/QtUI/QtWebKitChatView.cpp
+++ b/Swift/QtUI/QtWebKitChatView.cpp
@@ -1,13 +1,11 @@
/*
- * Copyright (c) 2010-2013 Remko Tronçon
+ * Copyright (c) 2010-2014 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include "QtWebKitChatView.h"
-#include <boost/format.hpp>
-
#include <QtDebug>
#include <QEventLoop>
#include <QFile>
@@ -23,6 +21,7 @@
#include <QFileDialog>
#include <Swiften/Base/Log.h>
+#include <Swiften/Base/FileSize.h>
#include <Swiften/StringCodecs/Base64.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
@@ -621,18 +620,6 @@ std::string QtWebKitChatView::addAction(const ChatWindow::ChatMessage& message,
return addMessage(" *" + chatMessageToHTML(message) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time, highlight, ChatSnippet::getDirection(message));
}
-// FIXME: Move this to a different file
-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] : "") );
-}
-
static QString encodeButtonArgument(const QString& str) {
return QtUtilities::htmlEscape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str)))));
}
diff --git a/Swiften/Base/FileSize.cpp b/Swiften/Base/FileSize.cpp
new file mode 100644
index 0000000..90fdc9a
--- /dev/null
+++ b/Swiften/Base/FileSize.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2010-2014 Kevin Smith and Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/Base/FileSize.h>
+#include <boost/format.hpp>
+
+namespace Swift {
+
+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] : "") );
+}
+
+}
diff --git a/Swiften/Base/FileSize.h b/Swiften/Base/FileSize.h
new file mode 100644
index 0000000..c9ed5fe
--- /dev/null
+++ b/Swiften/Base/FileSize.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2010-2014 Kevin Smith and Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/API.h>
+#include <boost/cstdint.hpp>
+#include <string>
+
+namespace Swift {
+
+SWIFTEN_API std::string formatSize(const boost::uintmax_t bytes);
+
+}
diff --git a/Swiften/Base/SConscript b/Swiften/Base/SConscript
index 094059a..d7fd7cc 100644
--- a/Swiften/Base/SConscript
+++ b/Swiften/Base/SConscript
@@ -16,6 +16,7 @@ objects = swiften_env.SwiftenObject([
"BoostRandomGenerator.cpp",
"sleep.cpp",
"URL.cpp",
- "Regex.cpp"
+ "Regex.cpp",
+ "FileSize.cpp"
])
swiften_env.Append(SWIFTEN_OBJECTS = [objects])