From 09108f5ac3c1a6567730b5bbd0c847f8422ff4a2 Mon Sep 17 00:00:00 2001 From: Richard Maudsley Date: Thu, 2 Jan 2014 12:21:41 +0000 Subject: Improved plaintext chat view for screen-reader support. Change-Id: Ib7c10350b56683db95f4c6be49f79f8a9d9e80ec 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 #include -#include "QtChatWindow.h" // for formatSize - #include +#include #include #include #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(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(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(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 #include +#include #include +#include +#include +#include +#include +#include +#include +#include #include +#include + +#include +#include #include #include #include - 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 += "

"; 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 label, const std::string& /*avatarPath*/, const boost::posix_time::ptime& time, const HighlightAction& /*highlight*/) { QString text = "

"; @@ -82,12 +95,319 @@ std::string QtPlainChatView::addAction(const ChatWindow::ChatMessage& message, c text += P2QSTRING(label->getLabel()) + "
"; } QString name = senderIsSelf ? "you" : P2QSTRING(senderName); - text += QString(tr("At %1 %2 ")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name); + text += QString(tr("At %1 %2 ")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name); + text += chatMessageToString(message); + text += "

"; + 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 = "

"; + text += chatMessageToString(message); + text += "

"; + log_->append(text); +} + +void QtPlainChatView::addPresenceMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction /*direction*/) +{ + QString text = "

"; + text += chatMessageToString(message); + text += "

"; + log_->append(text); +} + +void QtPlainChatView::addErrorMessage(const ChatWindow::ChatMessage& message) +{ + QString text = "

"; + text += chatMessageToString(message); + text += "

"; + 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 = "

"; + if (lastMessageLabel_[id]) { + text += P2QSTRING(lastMessageLabel_[id]->getLabel()) + "
"; + } + 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) + "
"; + text += chatMessageToString(message); + text += "

"; + 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 = "

"; + if (lastMessageLabel_[id]) { + text += P2QSTRING(lastMessageLabel_[id]->getLabel()) + "
"; + } + QString name = id.empty() ? "you" : P2QSTRING(id); + text += QString(tr("At %1 %2 corrected the last action to: ")).arg(ChatSnippet::timeToEscapedString(B2QDATE(time))).arg(name); + text += chatMessageToString(message); + text += "

"; + log_->append(text); +} + +void QtPlainChatView::replaceLastMessage(const ChatWindow::ChatMessage& message) +{ + QString text = "

The last message was corrected to:
"; text += chatMessageToString(message); text += "

"; 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(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: " + filename + " (" + sizeString + " bytes)"; + 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: " + filename + " (" + sizeString + " bytes)"; + 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(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(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(sender()); + if (action) { + window_->onFileTransferCancel(action->id_); + fileTransferFinish(); + } +} + +void QtPlainChatView::fileTransferFinish() +{ + FileTransfer::Action* action = dynamic_cast(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(sender()); + if (action) { + eventStream_->send(boost::make_shared(action->jid_.toString(), action->password_, boost::optional(), false, false, action->isImpromptu_, action->isContinuation_)); + delete action->parent_; + } +} + +void QtPlainChatView::rejectMUCInvite() +{ + AcceptMUCInviteAction *action = dynamic_cast(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 " + filename + "..."; + 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 " + filename + "..."; + FileTransfer::Action* cancel = new FileTransfer::Action("Cancel", ftId); + parent->connect(cancel, SIGNAL(clicked()), SLOT(fileTransferReject())); + layout_->addWidget(cancel); + break; + } + case ChatWindow::Transferring: { + status = "Transferring " + filename + "..."; + 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 " + filename + " 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 " + filename + " 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: " + filename + ""; + 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 #include +#include #include #include +#include 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 /*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 FileTransferMap; + QtChatWindow* window_; + UIEventStream* eventStream_; + LogTextEdit* log_; + QMenu* logMenu_; + FileTransferMap fileTransfers_; + std::map > 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 - #include #include #include @@ -23,6 +21,7 @@ #include #include +#include #include #include @@ -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 +#include + +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 +#include +#include + +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]) -- cgit v0.10.2-6-g49f6