diff options
author | Tobias Markmann <tm@ayena.de> | 2012-03-27 23:01:17 (GMT) |
---|---|---|
committer | Kevin Smith <git@kismith.co.uk> | 2012-04-12 14:09:34 (GMT) |
commit | 2dcdee8e9f657c7f5c5d5c507373fd328beaa568 (patch) | |
tree | f6c0516afefdcf1561e32b45a415a387bc71f9d3 /Swift/QtUI | |
parent | 0bf6afc5c01b9eb3024a8cfd04bfd743890db4f6 (diff) | |
download | swift-contrib-2dcdee8e9f657c7f5c5d5c507373fd328beaa568.zip swift-contrib-2dcdee8e9f657c7f5c5d5c507373fd328beaa568.tar.bz2 |
Refactoring incoming MUC invites UI.
Making MUC invites non-modal by moving them into the chat view.
Adding event classes for invites so they turn up in 'Notices'-tab and generate notifications.
License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php
Diffstat (limited to 'Swift/QtUI')
-rw-r--r-- | Swift/QtUI/EventViewer/EventDelegate.cpp | 7 | ||||
-rw-r--r-- | Swift/QtUI/EventViewer/EventDelegate.h | 3 | ||||
-rw-r--r-- | Swift/QtUI/EventViewer/QtEvent.cpp | 10 | ||||
-rw-r--r-- | Swift/QtUI/EventViewer/QtEventWindow.cpp | 7 | ||||
-rw-r--r-- | Swift/QtUI/QtChatView.cpp | 21 | ||||
-rw-r--r-- | Swift/QtUI/QtChatView.h | 1 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 190 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindow.h | 37 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindowJSBridge.cpp (renamed from Swift/QtUI/QtFileTransferJSBridge.cpp) | 8 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindowJSBridge.h (renamed from Swift/QtUI/QtFileTransferJSBridge.h) | 12 | ||||
-rw-r--r-- | Swift/QtUI/SConscript | 2 |
11 files changed, 180 insertions, 118 deletions
diff --git a/Swift/QtUI/EventViewer/EventDelegate.cpp b/Swift/QtUI/EventViewer/EventDelegate.cpp index 79b8854..9ecdd34 100644 --- a/Swift/QtUI/EventViewer/EventDelegate.cpp +++ b/Swift/QtUI/EventViewer/EventDelegate.cpp @@ -5,57 +5,62 @@ */ #include "EventDelegate.h" #include <QDebug> #include "Swift/Controllers/XMPPEvents/MessageEvent.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" +#include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h" namespace Swift { -EventDelegate::EventDelegate() : QStyledItemDelegate(), messageDelegate_(QtEvent::SenderRole, Qt::DisplayRole, false), subscriptionDelegate_(QtEvent::SenderRole, Qt::DisplayRole, true), errorDelegate_(QtEvent::SenderRole, Qt::DisplayRole, true) { +EventDelegate::EventDelegate() : QStyledItemDelegate(), messageDelegate_(QtEvent::SenderRole, Qt::DisplayRole, false), subscriptionDelegate_(QtEvent::SenderRole, Qt::DisplayRole, true), errorDelegate_(QtEvent::SenderRole, Qt::DisplayRole, true), mucInviteDelegate_(QtEvent::SenderRole, Qt::DisplayRole, false) { } QSize EventDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const { QtEvent* item = static_cast<QtEvent*>(index.internalPointer()); if (!item) { return QStyledItemDelegate::sizeHint(option, index); } switch (getEventType(item->getEvent())) { case MessageEventType: return messageDelegate_.sizeHint(option, item); case SubscriptionEventType: return subscriptionDelegate_.sizeHint(option, item); case ErrorEventType: return errorDelegate_.sizeHint(option, item); + case MUCInviteEventType: return mucInviteDelegate_.sizeHint(option, item); default: return QStyledItemDelegate::sizeHint(option, index); } } void EventDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QtEvent* item = static_cast<QtEvent*>(index.internalPointer()); if (!item) { QStyledItemDelegate::paint(painter, option, index); return; } switch (getEventType(item->getEvent())) { case MessageEventType: messageDelegate_.paint(painter, option, item);break; case SubscriptionEventType: subscriptionDelegate_.paint(painter, option, item);break; case ErrorEventType: errorDelegate_.paint(painter, option, item);break; + case MUCInviteEventType: mucInviteDelegate_.paint(painter, option, item);break; default: QStyledItemDelegate::paint(painter, option, index); } } EventType EventDelegate::getEventType(boost::shared_ptr<StanzaEvent> event) const { boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event); if (messageEvent) return MessageEventType; boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event); if (subscriptionEvent) return SubscriptionEventType; boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event); if (errorEvent) return ErrorEventType; + boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event); + if (mucInviteEvent) return MUCInviteEventType; //I don't know what this is. assert(false); return MessageEventType; } } diff --git a/Swift/QtUI/EventViewer/EventDelegate.h b/Swift/QtUI/EventViewer/EventDelegate.h index 2ad741c..1ebaff2 100644 --- a/Swift/QtUI/EventViewer/EventDelegate.h +++ b/Swift/QtUI/EventViewer/EventDelegate.h @@ -6,25 +6,26 @@ #pragma once #include <QStyledItemDelegate> #include "Swift/QtUI/Roster/DelegateCommons.h" #include "Swift/QtUI/EventViewer/TwoLineDelegate.h" namespace Swift { - enum EventType {MessageEventType, SubscriptionEventType, ErrorEventType}; + enum EventType {MessageEventType, SubscriptionEventType, ErrorEventType, MUCInviteEventType}; class EventDelegate : public QStyledItemDelegate { Q_OBJECT public: EventDelegate(); QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; private: EventType getEventType(boost::shared_ptr<StanzaEvent> event) const; DelegateCommons common_; TwoLineDelegate messageDelegate_; TwoLineDelegate subscriptionDelegate_; TwoLineDelegate errorDelegate_; + TwoLineDelegate mucInviteDelegate_; }; } diff --git a/Swift/QtUI/EventViewer/QtEvent.cpp b/Swift/QtUI/EventViewer/QtEvent.cpp index e7ea473..3c6f16c 100644 --- a/Swift/QtUI/EventViewer/QtEvent.cpp +++ b/Swift/QtUI/EventViewer/QtEvent.cpp @@ -5,18 +5,19 @@ */ #include "Swift/QtUI/EventViewer/QtEvent.h" #include <QDateTime> #include "Swift/Controllers/XMPPEvents/MessageEvent.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" +#include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h" #include "Swift/QtUI/QtSwiftUtil.h" namespace Swift { QtEvent::QtEvent(boost::shared_ptr<StanzaEvent> event, bool active) : event_(event) { active_ = active; } @@ -41,18 +42,22 @@ QString QtEvent::sender() { } boost::shared_ptr<SubscriptionRequestEvent> subscriptionRequestEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event_); if (subscriptionRequestEvent) { return P2QSTRING(subscriptionRequestEvent->getJID().toBare().toString()); } boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event_); if (errorEvent) { return P2QSTRING(errorEvent->getJID().toBare().toString()); } + boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event_); + if (mucInviteEvent) { + return P2QSTRING(mucInviteEvent->getInviter().toString()); + } return ""; } QString QtEvent::text() { boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event_); if (messageEvent) { return P2QSTRING(messageEvent->getStanza()->getBody()); } boost::shared_ptr<SubscriptionRequestEvent> subscriptionRequestEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event_); @@ -65,13 +70,18 @@ QString QtEvent::text() { else { message = QString(QObject::tr("%1 would like to add you to their contact list, saying '%2'")).arg(subscriptionRequestEvent->getJID().toBare().toString().c_str()).arg(reason.c_str()); } return message; } boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event_); if (errorEvent) { return P2QSTRING(errorEvent->getText()); } + boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event_); + if (mucInviteEvent) { + QString message = QString(QObject::tr("%1 has invited you to enter the %2 room.")).arg(P2QSTRING(mucInviteEvent->getInviter().toBare().toString())).arg(P2QSTRING(mucInviteEvent->getRoomJID().toString())); + return message; + } return ""; } } diff --git a/Swift/QtUI/EventViewer/QtEventWindow.cpp b/Swift/QtUI/EventViewer/QtEventWindow.cpp index fdc0194..35473b6 100644 --- a/Swift/QtUI/EventViewer/QtEventWindow.cpp +++ b/Swift/QtUI/EventViewer/QtEventWindow.cpp @@ -10,18 +10,19 @@ #include <QtDebug> #include <QBoxLayout> #include <QPushButton> #include <QMessageBox> #include "Swift/Controllers/XMPPEvents/MessageEvent.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" #include "Swift/QtUI/QtSubscriptionRequestWindow.h" #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" +#include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h" #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" #include "Swiften/Base/Platform.h" namespace Swift { QtEventWindow::QtEventWindow(UIEventStream* eventStream) : EventWindow(false) { @@ -69,29 +70,33 @@ void QtEventWindow::handleReadClicked() { return; } handleItemActivated(index); } void QtEventWindow::handleItemActivated(const QModelIndex& item) { QtEvent* event = model_->getItem(item.row()); boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event->getEvent()); boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event->getEvent()); + boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event->getEvent()); boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event->getEvent()); - + if (messageEvent) { if (messageEvent->getStanza()->getType() == Message::Groupchat) { eventStream_->send(boost::shared_ptr<UIEvent>(new JoinMUCUIEvent(messageEvent->getStanza()->getFrom().toBare(), messageEvent->getStanza()->getTo().getResource()))); } else { eventStream_->send(boost::shared_ptr<UIEvent>(new RequestChatUIEvent(messageEvent->getStanza()->getFrom()))); } } else if (subscriptionEvent) { QtSubscriptionRequestWindow* window = QtSubscriptionRequestWindow::getWindow(subscriptionEvent, this); window->show(); + } else if (mucInviteEvent) { + eventStream_->send(boost::shared_ptr<UIEvent>(new RequestChatUIEvent(mucInviteEvent->getInviter()))); + mucInviteEvent->conclude(); } else { if (errorEvent) { errorEvent->conclude(); } QMessageBox msgBox; msgBox.setText(model_->data(item, Qt::DisplayRole).toString()); msgBox.exec(); } diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp index db00ba0..b0c4e09 100644 --- a/Swift/QtUI/QtChatView.cpp +++ b/Swift/QtUI/QtChatView.cpp @@ -16,18 +16,19 @@ #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" namespace Swift { QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent) : QWidget(parent), fontSizeSteps_(0) { theme_ = theme; QVBoxLayout* mainLayout = new QVBoxLayout(this); @@ -320,43 +321,51 @@ void QtChatView::setFileTransferProgress(QString id, const int 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 = "Waiting for other side to accept the transfer.<br/>" - "<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">"; + newInnerHTML = "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 = "Negotiating...<br/>" - "<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">"; + newInnerHTML = "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>" - "<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">"; + "</div>" + + QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id); } else if (state == ChatWindow::Canceled) { newInnerHTML = "Transfer has been canceled!"; } else if (state == ChatWindow::Finished) { // text "Successful transfer" newInnerHTML = "Transfer completed successfully."; } else if (state == ChatWindow::FTFailed) { newInnerHTML = "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")); + } +} + } diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h index 0cc521a..6b40c05 100644 --- a/Swift/QtUI/QtChatView.h +++ b/Swift/QtUI/QtChatView.h @@ -36,18 +36,19 @@ namespace Swift { 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); signals: void gotFocus(); void fontResized(int); public slots: void copySelectionToClipboard(); void scrollToBottom(); void handleLinkClicked(const QUrl&); diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index c6519ba..cf520ee 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -16,19 +16,19 @@ #include "SystemMessageSnippet.h" #include "QtTextEdit.h" #include "QtSettingsProvider.h" #include "QtScaledAvatarCache.h" #include "SwifTools/TabComplete.h" #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIEvents/SendFileUIEvent.h> #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> -#include "QtFileTransferJSBridge.h" +#include "QtChatWindowJSBridge.h" #include <boost/cstdint.hpp> #include <boost/format.hpp> #include <boost/lexical_cast.hpp> #include <QLabel> #include <QMessageBox> #include <QInputDialog> #include <QApplication> @@ -44,19 +44,26 @@ #include <QUrl> #include <QPushButton> #include <QFileDialog> #include <QMenu> #include <Swift/Controllers/Settings/SettingsProvider.h> #include <QDebug> namespace Swift { -QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageWasSystem_(false), previousMessageWasPresence_(false), previousMessageWasFileTransfer_(false), eventStream_(eventStream) { + +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) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream) { settings_ = settings; unreadCount_ = 0; idCounter_ = 0; inputEnabled_ = true; completer_ = NULL; affiliationEditor_ = NULL; theme_ = theme; isCorrection_ = false; correctionEnabled_ = Maybe; @@ -152,38 +159,39 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt messageLog_->setFocusProxy(input_); connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(qAppFocusChanged(QWidget*, QWidget*))); connect(messageLog_, SIGNAL(gotFocus()), input_, SLOT(setFocus())); resize(400,300); connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int))); treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1)); treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2)); - fileTransferJS = new QtFileTransferJSBridge(); - messageLog_->addToJSEnvironment("filetransfer", fileTransferJS); - connect(fileTransferJS, SIGNAL(setDescription(QString)), this, SLOT(handleFileTransferSetDescription(QString))); - connect(fileTransferJS, SIGNAL(sendRequest(QString)), this, SLOT(handleFileTransferStart(QString))); - connect(fileTransferJS, SIGNAL(acceptRequest(QString, QString)), this, SLOT(handleFileTransferAccept(QString, QString))); - connect(fileTransferJS, SIGNAL(cancel(QString)), this, SLOT(handleFileTransferCancel(QString))); + jsBridge = new QtChatWindowJSBridge(); + messageLog_->addToJSEnvironment("chatwindow", jsBridge); + connect(jsBridge, SIGNAL(buttonClicked(QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString))); } QtChatWindow::~QtChatWindow() { - delete fileTransferJS; + delete jsBridge; if (mucConfigurationWindow_) { delete mucConfigurationWindow_.data(); } } void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) { onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(item)); } +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::handleFontResized(int fontSizeSteps) { messageLog_->resizeFont(fontSizeSteps); } void QtChatWindow::handleAlertButtonClicked() { onAlertButtonClicked(); } void QtChatWindow::setAlert(const std::string& alertText, const std::string& buttonText) { @@ -442,34 +450,32 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri htmlString += QString("%3</span> ").arg(Qt::escape(P2QSTRING(label->getDisplayMarking()))); } QString messageHTML(Qt::escape(P2QSTRING(message))); messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); messageHTML.replace("\n","<br/>"); QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">"; QString styleSpanEnd = style == "" ? "" : "</span>"; htmlString += styleSpanStart + messageHTML + styleSpanEnd; - bool appendToPrevious = !previousMessageWasFileTransfer_ && !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName))); + 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); - previousMessageWasSystem_ = false; - previousMessageWasPresence_ = false; - previousMessageWasFileTransfer_ = false; + previousMessageKind_ = PreviousMessageWasMessage; return id; } void QtChatWindow::flash() { emit requestFlash(); } void QtChatWindow::setAckState(std::string const& id, ChatWindow::AckState state) { QString xml; @@ -513,124 +519,140 @@ std::string formatSize(const boost::uintmax_t bytes) { 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 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(arg1).arg(arg2).arg(arg3); + return html; +} + std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) { qDebug() << "addFileTransfer"; - std::string ft_id = "ft" + boost::lexical_cast<std::string>(idCounter_++); + QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); - std::string htmlString; + QString htmlString; + QString formattedFileSize = P2QSTRING(formatSize(sizeInBytes)); if (senderIsSelf) { // outgoing - htmlString = "Send file: " + filename + " ( " + formatSize(sizeInBytes) + ") </br>" + + htmlString = tr("Send file)") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") </br>" + "<div id='" + ft_id + "'>" + - "<input id='discard' type='submit' value='Cancel' onclick='filetransfer.cancel(\"" + ft_id + "\");' />" + - "<input id='description' type='submit' value='Set Description' onclick='filetransfer.setDescription(\"" + ft_id + "\");' />" + - "<input id='send' type='submit' value='Send' onclick='filetransfer.sendRequest(\"" + 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 = "Receiving file: " + filename + " ( " + formatSize(sizeInBytes) + ") </br>" + + htmlString = "Receiving file: " + P2QSTRING(filename) + " ( " + formattedFileSize + ") </br>" + "<div id='" + ft_id + "'>" + - "<input id='discard' type='submit' value='Cancel' onclick='filetransfer.cancel(\"" + ft_id + "\");' />" + - "<input id='accept' type='submit' value='Accept' onclick='filetransfer.acceptRequest(\"" + ft_id + "\", \"" + filename + "\");' />" + + 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 = !previousMessageWasFileTransfer_ && !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName))); + 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(QString::fromStdString(htmlString), Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); - + 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)))); - return ft_id; + previousMessageWasSelf_ = senderIsSelf; + previousSenderName_ = P2QSTRING(senderName); + previousMessageKind_ = PreviousMessageWasFileTransfer; + return Q2PSTRING(ft_id); } void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) { messageLog_->setFileTransferProgress(QString::fromStdString(id), percentageDone); } void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg) { messageLog_->setFileTransferStatus(QString::fromStdString(id), state, QString::fromStdString(msg)); } -void QtChatWindow::handleFileTransferCancel(QString id) { - qDebug() << "QtChatWindow::handleFileTransferCancel(" << id << ")"; - onFileTransferCancel(Q2PSTRING(id)); -} - -void QtChatWindow::handleFileTransferSetDescription(QString id) { - bool ok = false; - QString text = QInputDialog::getText(this, tr("File transfer description"), - tr("Description:"), QLineEdit::Normal, "", &ok); - if (ok) { - descriptions[id] = text; +void QtChatWindow::handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3) { + if (id.startsWith(ButtonFileTransferCancel)) { + QString ft_id = arg1; + onFileTransferCancel(Q2PSTRING(ft_id)); } -} - -void QtChatWindow::handleFileTransferStart(QString id) { - qDebug() << "QtChatWindow::handleFileTransferStart(" << id << ")"; - - QString text = descriptions.find(id) == descriptions.end() ? QString() : descriptions[id]; - onFileTransferStart(Q2PSTRING(id), Q2PSTRING(text)); -} + 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; -void QtChatWindow::handleFileTransferAccept(QString id, QString filename) { - qDebug() << "QtChatWindow::handleFileTransferAccept(" << id << ", " << filename << ")"; + 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; - QString path = QFileDialog::getSaveFileName(this, tr("Save File"), filename); - if (!path.isEmpty()) { - onFileTransferAccept(Q2PSTRING(id), Q2PSTRING(path)); + eventStream_->send(boost::make_shared<JoinMUCUIEvent>(Q2PSTRING(roomJID), Q2PSTRING(password))); + messageLog_->setMUCInvitationJoined(elementID); + } + else { + qDebug() << "Unknown HTML button! ( " << id << " )"; } } 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; - previousMessageWasSystem_ = true; - previousMessageWasPresence_ = false; - previousMessageWasFileTransfer_ = false; + previousMessageKind_ = PreviousMessageWasSystem; } void QtChatWindow::addSystemMessage(const std::string& message) { if (isWidgetSelected()) { onAllMessagesRead(); } QString messageHTML(Qt::escape(P2QSTRING(message))); messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); messageHTML.replace("\n","<br/>"); messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); - previousMessageWasSelf_ = false; - previousMessageWasSystem_ = true; - previousMessageWasPresence_ = false; - previousMessageWasFileTransfer_ = false; + previousMessageKind_ = PreviousMessageWasSystem; } void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { if (!id.empty()) { QString messageHTML(Qt::escape(P2QSTRING(message))); messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); messageHTML.replace("\n","<br/>"); messageLog_->replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time)); } @@ -643,21 +665,19 @@ void QtChatWindow::addPresenceMessage(const std::string& message) { if (isWidgetSelected()) { onAllMessagesRead(); } QString messageHTML(Qt::escape(P2QSTRING(message))); messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); messageHTML.replace("\n","<br/>"); messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); - previousMessageWasSelf_ = false; - previousMessageWasSystem_ = false; - previousMessageWasPresence_ = true; + previousMessageKind_ = PreviousMessageWasPresence; } void QtChatWindow::returnPressed() { if (!inputEnabled_) { return; } messageLog_->scrollToBottom(); lastSentMessage_ = QString(input_->toPlainText()); @@ -806,41 +826,41 @@ void QtChatWindow::setAvailableRoomActions(const std::vector<RoomAction> &action void QtChatWindow::showRoomConfigurationForm(Form::ref form) { if (mucConfigurationWindow_) { delete mucConfigurationWindow_.data(); } mucConfigurationWindow_ = new QtMUCConfigurationWindow(form); mucConfigurationWindow_->onFormComplete.connect(boost::bind(boost::ref(onConfigureRequest), _1)); mucConfigurationWindow_->onFormCancelled.connect(boost::bind(boost::ref(onConfigurationFormCancelled))); } -void QtChatWindow::addMUCInvitation(const JID& jid, const std::string& reason, const std::string& /*password*/, bool direct) { - bool accepted = false; - QMessageBox msgBox; - //FIXME: horrid modal untranslated popup. Fix before release. - msgBox.setText(QString("You have been invited to the room %1 by %2.").arg(P2QSTRING(jid.toString())).arg(contact_)); - QString reasonString; +void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) { + if (isWidgetSelected()) { + onAllMessagesRead(); + } + + QString htmlString = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + " </br>"; if (!reason.empty()) { - reasonString = QString("\"%1\"").arg(P2QSTRING(reason)); + htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "</br>"; } if (!direct) { - if (!reasonString.isEmpty()) { - reasonString += " "; - } - reasonString += QString(" (%1 may not have really sent this invitation)").arg(contact_); - } - msgBox.setInformativeText(QString("Accept invitation from %1 to enter %2?\n%3").arg(contact_).arg(P2QSTRING(jid.toString())).arg(reasonString)); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::Yes); - int ret = msgBox.exec(); - switch (ret) { - case QMessageBox::Yes: - accepted = true; - break; - default: - break; - } - if (accepted) { - eventStream_->send(boost::make_shared<JoinMUCUIEvent>(jid)); + 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))); } } diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index 189a12a..18eb092 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -25,23 +25,31 @@ class QSplitter; class QPushButton; namespace Swift { class QtChatView; class QtOccupantListWidget; class QtChatTheme; class TreeWidget; class QtTextEdit; class UIEventStream; - class QtFileTransferJSBridge; + class QtChatWindowJSBridge; class SettingsProvider; class QtChatWindow : public QtTabbable, public ChatWindow { 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); ~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); 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); void addSystemMessage(const std::string& message); void addPresenceMessage(const std::string& message); void addErrorMessage(const std::string& errorMessage); void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time); @@ -71,22 +79,24 @@ namespace Swift { // message receipts void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state); void flash(); QByteArray getSplitterState(); virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions); void setSubject(const std::string& subject); void showRoomConfigurationForm(Form::ref); - void addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password, bool direct = true); + void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true); void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&); void setAvailableRoomActions(const std::vector<RoomAction> &actions); + 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); void setAlert(const std::string& alertText, const std::string& buttonText = ""); void cancelAlert(); void setCorrectionEnabled(Tristate enabled); signals: void geometryChanged(); @@ -107,32 +117,39 @@ namespace Swift { private slots: void returnPressed(); void handleInputChanged(); void handleKeyPressEvent(QKeyEvent* event); void handleSplitterMoved(int pos, int index); void handleAlertButtonClicked(); void handleActionButtonClicked(); - - void handleFileTransferCancel(QString id); - void handleFileTransferSetDescription(QString id); - void handleFileTransferStart(QString id); - void handleFileTransferAccept(QString id, QString filename); + void handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3); void handleAffiliationEditorAccepted(); private: + enum PreviousMessageKind { + PreviosuMessageWasNone, + PreviousMessageWasMessage, + PreviousMessageWasSystem, + PreviousMessageWasPresence, + PreviousMessageWasFileTransfer, + PreviousMessageWasMUCInvite + }; + + private: void updateTitleWithUnreadCount(); void tabComplete(); void beginCorrection(); void cancelCorrection(); 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 handleOccupantSelectionChanged(RosterItem* item); + bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const; int unreadCount_; bool contactIsTyping_; LastLineTracker lastLineTracker_; QString contact_; QString lastSentMessage_; QtChatView* messageLog_; QtChatTheme* theme_; QtTextEdit* input_; @@ -142,28 +159,26 @@ namespace Swift { QLabel* alertLabel_; QWidget* alertWidget_; QPushButton* alertButton_; TabComplete* completer_; QLineEdit* subject_; QPushButton* actionButton_; std::vector<SecurityLabelsCatalog::Item> availableLabels_; bool isCorrection_; bool previousMessageWasSelf_; - bool previousMessageWasSystem_; - bool previousMessageWasPresence_; - bool previousMessageWasFileTransfer_; + PreviousMessageKind previousMessageKind_; QString previousSenderName_; bool inputClearing_; UIEventStream* eventStream_; bool inputEnabled_; QSplitter *logRosterSplitter_; Tristate correctionEnabled_; QString alertStyleSheet_; std::map<QString, QString> descriptions; - QtFileTransferJSBridge* fileTransferJS; + QtChatWindowJSBridge* jsBridge; QPointer<QtMUCConfigurationWindow> mucConfigurationWindow_; QPointer<QtAffiliationEditor> affiliationEditor_; int idCounter_; SettingsProvider* settings_; std::vector<ChatWindow::RoomAction> availableRoomActions_; }; } diff --git a/Swift/QtUI/QtFileTransferJSBridge.cpp b/Swift/QtUI/QtChatWindowJSBridge.cpp index 76c1509..db67d79 100644 --- a/Swift/QtUI/QtFileTransferJSBridge.cpp +++ b/Swift/QtUI/QtChatWindowJSBridge.cpp @@ -1,19 +1,19 @@ /* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ -#include "QtFileTransferJSBridge.h" +#include "QtChatWindowJSBridge.h" namespace Swift { -QtFileTransferJSBridge::QtFileTransferJSBridge() { +QtChatWindowJSBridge::QtChatWindowJSBridge() { } -QtFileTransferJSBridge::~QtFileTransferJSBridge() { +QtChatWindowJSBridge::~QtChatWindowJSBridge() { } -}
\ No newline at end of file +} diff --git a/Swift/QtUI/QtFileTransferJSBridge.h b/Swift/QtUI/QtChatWindowJSBridge.h index bd884e5..8e6f0c2 100644 --- a/Swift/QtUI/QtFileTransferJSBridge.h +++ b/Swift/QtUI/QtChatWindowJSBridge.h @@ -8,23 +8,19 @@ #include <QObject> #include <map> namespace Swift { class FileTransferController; -class QtFileTransferJSBridge : public QObject { +class QtChatWindowJSBridge : public QObject { Q_OBJECT public: - QtFileTransferJSBridge(); - virtual ~QtFileTransferJSBridge(); + QtChatWindowJSBridge(); + virtual ~QtChatWindowJSBridge(); signals: - void discard(QString id); - void sendRequest(QString id); - void setDescription(QString id); - void acceptRequest(QString id, QString filename); - void cancel(QString id); + void buttonClicked(QString id, QString arg1, QString arg2, QString arg3); }; } diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 0971577..06c5bc4 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -138,19 +138,19 @@ sources = [ "UserSearch/QtUserSearchDetailsPage.cpp", "UserSearch/QtUserSearchWindow.cpp", "UserSearch/UserSearchModel.cpp", "UserSearch/UserSearchDelegate.cpp", "QtSubscriptionRequestWindow.cpp", "QtRosterHeader.cpp", "QtWebView.cpp", "qrc_DefaultTheme.cc", "qrc_Swift.cc", - "QtFileTransferJSBridge.cpp", + "QtChatWindowJSBridge.cpp", "QtMUCConfigurationWindow.cpp", "QtAffiliationEditor.cpp", "QtUISettingConstants.cpp" ] myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift") if env["PLATFORM"] == "win32" : res = myenv.RES("#/Swift/resources/Windows/Swift.rc") |