From 2dcdee8e9f657c7f5c5d5c507373fd328beaa568 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Wed, 28 Mar 2012 01:01:17 +0200 Subject: 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 diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index f590ffd..8523591 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace Swift { @@ -91,8 +92,8 @@ void ChatControllerBase::setAvailableServerFeatures(boost::shared_ptr void ChatControllerBase::handleAllMessagesRead() { if (!unreadMessages_.empty()) { - foreach (boost::shared_ptr messageEvent, unreadMessages_) { - messageEvent->read(); + foreach (boost::shared_ptr stanzaEvent, unreadMessages_) { + stanzaEvent->conclude(); } unreadMessages_.clear(); chatWindow_->setUnreadMessageCount(0); @@ -267,9 +268,20 @@ std::string ChatControllerBase::getErrorMessage(boost::shared_ptr return defaultMessage; } +void ChatControllerBase::handleGeneralMUCInvitation(MUCInviteEvent::ref event) { + unreadMessages_.push_back(event); + chatWindow_->show(); + chatWindow_->setUnreadMessageCount(unreadMessages_.size()); + onUnreadCountChanged(); + chatWindow_->addMUCInvitation(senderDisplayNameFromMessage(event->getInviter()), event->getRoomJID(), event->getReason(), event->getPassword(), event->getDirect()); + eventController_->handleIncomingEvent(event); +} + void ChatControllerBase::handleMUCInvitation(Message::ref message) { MUCInvitationPayload::ref invite = message->getPayload(); - chatWindow_->addMUCInvitation(invite->getJID(), invite->getReason(), invite->getPassword()); + + MUCInviteEvent::ref inviteEvent = boost::make_shared(toJID_, invite->getJID(), invite->getReason(), invite->getPassword(), true); + handleGeneralMUCInvitation(inviteEvent); } void ChatControllerBase::handleMediatedMUCInvitation(Message::ref message) { @@ -283,7 +295,9 @@ void ChatControllerBase::handleMediatedMUCInvitation(Message::ref message) { if (message->getPayload()->getPassword()) { password = *message->getPayload()->getPassword(); } - chatWindow_->addMUCInvitation(from, reason, password, false); + + MUCInviteEvent::ref inviteEvent = boost::make_shared(invite.from, from, reason, password, false); + handleGeneralMUCInvitation(inviteEvent); } diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index e1034d6..a4d23c0 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -20,6 +20,7 @@ #include #include "Swiften/Elements/DiscoInfo.h" #include "Swift/Controllers/XMPPEvents/MessageEvent.h" +#include #include "Swiften/JID/JID.h" #include "Swiften/Elements/SecurityLabelsCatalog.h" #include "Swiften/Elements/ErrorPayload.h" @@ -84,10 +85,11 @@ namespace Swift { void handleDayChangeTick(); void handleMUCInvitation(Message::ref message); void handleMediatedMUCInvitation(Message::ref message); + void handleGeneralMUCInvitation(MUCInviteEvent::ref event); protected: JID selfJID_; - std::vector > unreadMessages_; + std::vector > unreadMessages_; StanzaChannel* stanzaChannel_; IQRouter* iqRouter_; ChatWindowFactory* chatWindowFactory_; diff --git a/Swift/Controllers/EventNotifier.cpp b/Swift/Controllers/EventNotifier.cpp index e643ab3..a4edabf 100644 --- a/Swift/Controllers/EventNotifier.cpp +++ b/Swift/Controllers/EventNotifier.cpp @@ -18,6 +18,7 @@ #include "Swift/Controllers/XMPPEvents/MessageEvent.h" #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" +#include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h" #include "Swift/Controllers/Settings/SettingsProvider.h" namespace Swift { @@ -55,6 +56,12 @@ void EventNotifier::handleEventAdded(boost::shared_ptr event) { else if(boost::shared_ptr errorEvent = boost::dynamic_pointer_cast(event)) { notifier->showMessage(Notifier::SystemMessage, QT_TRANSLATE_NOOP("", "Error"), errorEvent->getText(), boost::filesystem::path(), boost::function()); } + else if (boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(event)) { + std::string title = mucInviteEvent->getInviter(); + std::string message = str(format(QT_TRANSLATE_NOOP("", "%1% has invited you to enter the %2% room")) % nickResolver->jidToNick(mucInviteEvent->getInviter()) % mucInviteEvent->getRoomJID()); + // FIXME: not show avatar or greyed out avatar for mediated invites + notifier->showMessage(Notifier::SystemMessage, title, message, avatarManager->getAvatarPath(mucInviteEvent->getInviter()), boost::bind(&EventNotifier::handleNotificationActivated, this, mucInviteEvent->getInviter())); + } } diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index e7246c6..9305314 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -57,7 +57,7 @@ namespace Swift { virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0; virtual void setFileTransferProgress(std::string, const int percentageDone) = 0; virtual void setFileTransferStatus(std::string, const FileTransferState state, const std::string& msg = "") = 0; - virtual void addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password, bool direct = true) = 0; + virtual void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true) = 0; // message receipts virtual void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) = 0; diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index dab1e90..f92bd8b 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -50,7 +50,7 @@ namespace Swift { void setAvailableOccupantActions(const std::vector&/* actions*/) {} void setSubject(const std::string& /*subject*/) {} virtual void showRoomConfigurationForm(Form::ref) {} - virtual void addMUCInvitation(const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool = true) {}; + virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool = true) {}; virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector&) {} virtual void setAvailableRoomActions(const std::vector &) {}; diff --git a/Swift/Controllers/XMPPEvents/EventController.cpp b/Swift/Controllers/XMPPEvents/EventController.cpp index 98fd634..0808aa0 100644 --- a/Swift/Controllers/XMPPEvents/EventController.cpp +++ b/Swift/Controllers/XMPPEvents/EventController.cpp @@ -13,6 +13,7 @@ #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 { @@ -29,7 +30,8 @@ void EventController::handleIncomingEvent(boost::shared_ptr sourceE boost::shared_ptr messageEvent = boost::dynamic_pointer_cast(sourceEvent); boost::shared_ptr subscriptionEvent = boost::dynamic_pointer_cast(sourceEvent); boost::shared_ptr errorEvent = boost::dynamic_pointer_cast(sourceEvent); - if ((messageEvent && messageEvent->isReadable()) || subscriptionEvent || errorEvent) { + boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(sourceEvent); + if ((messageEvent && messageEvent->isReadable()) || subscriptionEvent || errorEvent || mucInviteEvent) { events_.push_back(sourceEvent); sourceEvent->onConclusion.connect(boost::bind(&EventController::handleEventConcluded, this, sourceEvent)); onEventQueueLengthChange(events_.size()); diff --git a/Swift/Controllers/XMPPEvents/MUCInviteEvent.h b/Swift/Controllers/XMPPEvents/MUCInviteEvent.h new file mode 100644 index 0000000..0b430cd --- /dev/null +++ b/Swift/Controllers/XMPPEvents/MUCInviteEvent.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +namespace Swift { + + class MUCInviteEvent : public StanzaEvent { + public: + typedef boost::shared_ptr ref; + + public: + MUCInviteEvent(const JID& inviter, const JID& roomJID, const std::string& reason, const std::string& password, bool direct) : inviter_(inviter), roomJID_(roomJID), reason_(reason), password_(password), direct_(direct) {} + + const JID& getInviter() const { return inviter_; } + const JID& getRoomJID() const { return roomJID_; } + const std::string& getReason() const { return reason_; } + const std::string& getPassword() const { return password_; } + bool getDirect() const { return direct_; } + + private: + JID inviter_; + JID roomJID_; + std::string reason_; + std::string password_; + bool direct_; + }; +} 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 @@ -11,10 +11,11 @@ #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) { } @@ -27,6 +28,7 @@ QSize EventDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIn 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); } } @@ -41,6 +43,7 @@ void EventDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, 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); } } @@ -52,6 +55,8 @@ EventType EventDelegate::getEventType(boost::shared_ptr event) cons if (subscriptionEvent) return SubscriptionEventType; boost::shared_ptr errorEvent = boost::dynamic_pointer_cast(event); if (errorEvent) return ErrorEventType; + boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(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 @@ -12,7 +12,7 @@ #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: @@ -25,6 +25,7 @@ namespace Swift { 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 @@ -11,6 +11,7 @@ #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" @@ -47,6 +48,10 @@ QString QtEvent::sender() { if (errorEvent) { return P2QSTRING(errorEvent->getJID().toBare().toString()); } + boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(event_); + if (mucInviteEvent) { + return P2QSTRING(mucInviteEvent->getInviter().toString()); + } return ""; } @@ -71,6 +76,11 @@ QString QtEvent::text() { if (errorEvent) { return P2QSTRING(errorEvent->getText()); } + boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(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 @@ -16,6 +16,7 @@ #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" @@ -75,8 +76,9 @@ void QtEventWindow::handleItemActivated(const QModelIndex& item) { QtEvent* event = model_->getItem(item.row()); boost::shared_ptr messageEvent = boost::dynamic_pointer_cast(event->getEvent()); boost::shared_ptr subscriptionEvent = boost::dynamic_pointer_cast(event->getEvent()); + boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(event->getEvent()); boost::shared_ptr errorEvent = boost::dynamic_pointer_cast(event->getEvent()); - + if (messageEvent) { if (messageEvent->getStanza()->getType() == Message::Groupchat) { eventStream_->send(boost::shared_ptr(new JoinMUCUIEvent(messageEvent->getStanza()->getFrom().toBare(), messageEvent->getStanza()->getTo().getResource()))); @@ -86,6 +88,9 @@ void QtEventWindow::handleItemActivated(const QModelIndex& item) { } else if (subscriptionEvent) { QtSubscriptionRequestWindow* window = QtSubscriptionRequestWindow::getWindow(subscriptionEvent, this); window->show(); + } else if (mucInviteEvent) { + eventStream_->send(boost::shared_ptr(new RequestChatUIEvent(mucInviteEvent->getInviter()))); + mucInviteEvent->conclude(); } else { if (errorEvent) { errorEvent->conclude(); 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 @@ -22,6 +22,7 @@ #include "QtWebView.h" #include "QtChatTheme.h" +#include "QtChatWindow.h" #include "QtSwiftUtil.h" @@ -326,13 +327,13 @@ void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransfe QString newInnerHTML = ""; if (state == ChatWindow::WaitingForAccept) { - newInnerHTML = "Waiting for other side to accept the transfer.
" - ""; + newInnerHTML = "Waiting for other side to accept the transfer.
" + + QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id); } if (state == ChatWindow::Negotiating) { // replace with text "Negotiaging" + Cancel button - newInnerHTML = "Negotiating...
" - ""; + newInnerHTML = "Negotiating...
" + + QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id); } else if (state == ChatWindow::Transferring) { // progress bar + Cancel Button @@ -342,8 +343,8 @@ void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransfe "0%" "" "" - "" - ""; + "" + + QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id); } else if (state == ChatWindow::Canceled) { newInnerHTML = "Transfer has been canceled!"; @@ -359,4 +360,12 @@ void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransfe 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 @@ -42,6 +42,7 @@ namespace Swift { 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(); 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 @@ -22,7 +22,7 @@ #include #include #include -#include "QtFileTransferJSBridge.h" +#include "QtChatWindowJSBridge.h" #include #include @@ -50,7 +50,14 @@ #include 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; @@ -158,16 +165,13 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt 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(); } @@ -178,6 +182,10 @@ void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) { onOccupantSelectionChanged(dynamic_cast(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); } @@ -448,7 +456,7 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri QString styleSpanEnd = style == "" ? "" : ""; 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(); @@ -461,9 +469,7 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri previousMessageWasSelf_ = senderIsSelf; previousSenderName_ = P2QSTRING(senderName); - previousMessageWasSystem_ = false; - previousMessageWasPresence_ = false; - previousMessageWasFileTransfer_ = false; + previousMessageKind_ = PreviousMessageWasMessage; return id; } @@ -519,31 +525,39 @@ std::string formatSize(const boost::uintmax_t bytes) { 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("").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(idCounter_++); + QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast(idCounter_++))); - std::string htmlString; + QString htmlString; + QString formattedFileSize = P2QSTRING(formatSize(sizeInBytes)); if (senderIsSelf) { // outgoing - htmlString = "Send file: " + filename + " ( " + formatSize(sizeInBytes) + ")
" + + htmlString = tr("Send file)") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ")
" + "
" + - "" + - "" + - "" + + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + + buildChatWindowButton(tr("Set Description"), ButtonFileTransferSetDescription, ft_id) + + buildChatWindowButton(tr("Send"), ButtonFileTransferSendRequest, ft_id) + "
"; } else { // incoming - htmlString = "Receiving file: " + filename + " ( " + formatSize(sizeInBytes) + ")
" + + htmlString = "Receiving file: " + P2QSTRING(filename) + " ( " + formattedFileSize + ")
" + "
" + - "" + - "" + + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + + buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) + "
"; } //addMessage(message, senderName, senderIsSelf, boost::shared_ptr(), "", 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(); @@ -552,10 +566,12 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se } QString qAvatarPath = "qrc:/icons/avatar.png"; std::string id = "ftmessage" + boost::lexical_cast(idCounter_++); - messageLog_->addMessage(boost::shared_ptr(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(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) { @@ -566,33 +582,44 @@ void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState 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(Q2PSTRING(roomJID), Q2PSTRING(password))); + messageLog_->setMUCInvitationJoined(elementID); + } + else { + qDebug() << "Unknown HTML button! ( " << id << " )"; } } @@ -606,9 +633,7 @@ void QtChatWindow::addErrorMessage(const std::string& errorMessage) { messageLog_->addMessage(boost::shared_ptr(new SystemMessageSnippet("" + errorMessageHTML + "", QDateTime::currentDateTime(), false, theme_))); previousMessageWasSelf_ = false; - previousMessageWasSystem_ = true; - previousMessageWasPresence_ = false; - previousMessageWasFileTransfer_ = false; + previousMessageKind_ = PreviousMessageWasSystem; } void QtChatWindow::addSystemMessage(const std::string& message) { @@ -621,10 +646,7 @@ void QtChatWindow::addSystemMessage(const std::string& message) { messageHTML.replace("\n","
"); messageLog_->addMessage(boost::shared_ptr(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) { @@ -649,9 +671,7 @@ void QtChatWindow::addPresenceMessage(const std::string& message) { messageHTML.replace("\n","
"); messageLog_->addMessage(boost::shared_ptr(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); - previousMessageWasSelf_ = false; - previousMessageWasSystem_ = false; - previousMessageWasPresence_ = true; + previousMessageKind_ = PreviousMessageWasPresence; } @@ -812,35 +832,35 @@ void QtChatWindow::showRoomConfigurationForm(Form::ref form) { 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())) + "
"; if (!reason.empty()) { - reasonString = QString("\"%1\"").arg(P2QSTRING(reason)); + htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "
"; } 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(jid)); + htmlString += QObject::tr("This person may not have really sent this invitation!") + "
"; } + + QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast(idCounter_++))); + + htmlString += "
" + + buildChatWindowButton(tr("Accept Invite"), ButtonMUCInvite, Qt::escape(P2QSTRING(jid.toString())), Qt::escape(P2QSTRING(password)), id) + + "
"; + + 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(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 @@ -31,11 +31,19 @@ namespace Swift { 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(); @@ -77,10 +85,12 @@ namespace Swift { virtual void setAvailableOccupantActions(const std::vector& 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&); void setAvailableRoomActions(const std::vector &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); @@ -113,20 +123,27 @@ namespace Swift { 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 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_; @@ -148,9 +165,7 @@ namespace Swift { std::vector availableLabels_; bool isCorrection_; bool previousMessageWasSelf_; - bool previousMessageWasSystem_; - bool previousMessageWasPresence_; - bool previousMessageWasFileTransfer_; + PreviousMessageKind previousMessageKind_; QString previousSenderName_; bool inputClearing_; UIEventStream* eventStream_; @@ -159,7 +174,7 @@ namespace Swift { Tristate correctionEnabled_; QString alertStyleSheet_; std::map descriptions; - QtFileTransferJSBridge* fileTransferJS; + QtChatWindowJSBridge* jsBridge; QPointer mucConfigurationWindow_; QPointer affiliationEditor_; int idCounter_; diff --git a/Swift/QtUI/QtChatWindowJSBridge.cpp b/Swift/QtUI/QtChatWindowJSBridge.cpp new file mode 100644 index 0000000..db67d79 --- /dev/null +++ b/Swift/QtUI/QtChatWindowJSBridge.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtChatWindowJSBridge.h" + +namespace Swift { + +QtChatWindowJSBridge::QtChatWindowJSBridge() { + +} + +QtChatWindowJSBridge::~QtChatWindowJSBridge() { + +} + +} diff --git a/Swift/QtUI/QtChatWindowJSBridge.h b/Swift/QtUI/QtChatWindowJSBridge.h new file mode 100644 index 0000000..8e6f0c2 --- /dev/null +++ b/Swift/QtUI/QtChatWindowJSBridge.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include + +namespace Swift { + +class FileTransferController; + +class QtChatWindowJSBridge : public QObject { + Q_OBJECT +public: + QtChatWindowJSBridge(); + virtual ~QtChatWindowJSBridge(); +signals: + void buttonClicked(QString id, QString arg1, QString arg2, QString arg3); +}; + +} diff --git a/Swift/QtUI/QtFileTransferJSBridge.cpp b/Swift/QtUI/QtFileTransferJSBridge.cpp deleted file mode 100644 index 76c1509..0000000 --- a/Swift/QtUI/QtFileTransferJSBridge.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2011 Tobias Markmann - * Licensed under the simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include "QtFileTransferJSBridge.h" - -namespace Swift { - -QtFileTransferJSBridge::QtFileTransferJSBridge() { - -} - -QtFileTransferJSBridge::~QtFileTransferJSBridge() { - -} - -} \ No newline at end of file diff --git a/Swift/QtUI/QtFileTransferJSBridge.h b/Swift/QtUI/QtFileTransferJSBridge.h deleted file mode 100644 index bd884e5..0000000 --- a/Swift/QtUI/QtFileTransferJSBridge.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2011 Tobias Markmann - * Licensed under the simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include - -namespace Swift { - -class FileTransferController; - -class QtFileTransferJSBridge : public QObject { - Q_OBJECT -public: - QtFileTransferJSBridge(); - virtual ~QtFileTransferJSBridge(); -signals: - void discard(QString id); - void sendRequest(QString id); - void setDescription(QString id); - void acceptRequest(QString id, QString filename); - void cancel(QString id); -}; - -} diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 0971577..06c5bc4 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -144,7 +144,7 @@ sources = [ "QtWebView.cpp", "qrc_DefaultTheme.cc", "qrc_Swift.cc", - "QtFileTransferJSBridge.cpp", + "QtChatWindowJSBridge.cpp", "QtMUCConfigurationWindow.cpp", "QtAffiliationEditor.cpp", "QtUISettingConstants.cpp" -- cgit v0.10.2-6-g49f6