diff options
-rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.cpp | 22 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.h | 4 | ||||
-rw-r--r-- | Swift/Controllers/EventNotifier.cpp | 7 | ||||
-rw-r--r-- | Swift/Controllers/UIInterfaces/ChatWindow.h | 2 | ||||
-rw-r--r-- | Swift/Controllers/UnitTest/MockChatWindow.h | 2 | ||||
-rw-r--r-- | Swift/Controllers/XMPPEvents/EventController.cpp | 4 | ||||
-rw-r--r-- | Swift/Controllers/XMPPEvents/MUCInviteEvent.h | 31 | ||||
-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 |
18 files changed, 244 insertions, 126 deletions
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 @@ -23,18 +23,19 @@ #include <Swiften/Elements/MUCInvitationPayload.h> #include <Swiften/Elements/MUCUserPayload.h> #include <Swiften/Base/foreach.h> #include <Swift/Controllers/XMPPEvents/EventController.h> #include <Swiften/Disco/EntityCapsProvider.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> #include <Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h> #include <Swiften/Avatars/AvatarManager.h> +#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h> namespace Swift { ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory), entityCapsProvider_(entityCapsProvider) { chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream); chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2)); entityCapsProvider_->onCapsChanged.connect(boost::bind(&ChatControllerBase::handleCapsChanged, this, _1)); setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable()); @@ -85,20 +86,20 @@ void ChatControllerBase::setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> request->send(); } else { chatWindow_->setSecurityLabelsEnabled(false); labelsEnabled_ = false; } } void ChatControllerBase::handleAllMessagesRead() { if (!unreadMessages_.empty()) { - foreach (boost::shared_ptr<MessageEvent> messageEvent, unreadMessages_) { - messageEvent->read(); + foreach (boost::shared_ptr<StanzaEvent> stanzaEvent, unreadMessages_) { + stanzaEvent->conclude(); } unreadMessages_.clear(); chatWindow_->setUnreadMessageCount(0); onUnreadCountChanged(); } } int ChatControllerBase::getUnreadCount() { return unreadMessages_.size(); @@ -261,31 +262,44 @@ std::string ChatControllerBase::getErrorMessage(boost::shared_ptr<ErrorPayload> case ErrorPayload::ServiceUnavailable: return QT_TRANSLATE_NOOP("", "The service is unavailable"); break; case ErrorPayload::SubscriptionRequired: return QT_TRANSLATE_NOOP("", "A subscription is required"); break; case ErrorPayload::UndefinedCondition: return QT_TRANSLATE_NOOP("", "Undefined condition"); break; case ErrorPayload::UnexpectedRequest: return QT_TRANSLATE_NOOP("", "Unexpected request"); break; } } 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<MUCInvitationPayload>(); - chatWindow_->addMUCInvitation(invite->getJID(), invite->getReason(), invite->getPassword()); + + MUCInviteEvent::ref inviteEvent = boost::make_shared<MUCInviteEvent>(toJID_, invite->getJID(), invite->getReason(), invite->getPassword(), true); + handleGeneralMUCInvitation(inviteEvent); } void ChatControllerBase::handleMediatedMUCInvitation(Message::ref message) { MUCUserPayload::Invite invite = *message->getPayload<MUCUserPayload>()->getInvite(); JID from = message->getFrom(); std::string reason; if (!invite.reason.empty()) { reason = invite.reason; } std::string password; if (message->getPayload<MUCUserPayload>()->getPassword()) { password = *message->getPayload<MUCUserPayload>()->getPassword(); } - chatWindow_->addMUCInvitation(from, reason, password, false); + + MUCInviteEvent::ref inviteEvent = boost::make_shared<MUCInviteEvent>(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 @@ -14,18 +14,19 @@ #include <boost/optional.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include "Swiften/Network/Timer.h" #include "Swiften/Network/TimerFactory.h" #include "Swiften/Elements/Stanza.h" #include <string> #include "Swiften/Elements/DiscoInfo.h" #include "Swift/Controllers/XMPPEvents/MessageEvent.h" +#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h> #include "Swiften/JID/JID.h" #include "Swiften/Elements/SecurityLabelsCatalog.h" #include "Swiften/Elements/ErrorPayload.h" #include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Queries/IQRouter.h" #include "Swiften/Base/IDGenerator.h" namespace Swift { class IQRouter; @@ -78,22 +79,23 @@ namespace Swift { IDGenerator idGenerator_; std::string lastSentMessageStanzaID_; void createDayChangeTimer(); void handleSendMessageRequest(const std::string &body, bool isCorrectionMessage); void handleAllMessagesRead(); void handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog>, ErrorPayload::ref error); void handleDayChangeTick(); void handleMUCInvitation(Message::ref message); void handleMediatedMUCInvitation(Message::ref message); + void handleGeneralMUCInvitation(MUCInviteEvent::ref event); protected: JID selfJID_; - std::vector<boost::shared_ptr<MessageEvent> > unreadMessages_; + std::vector<boost::shared_ptr<StanzaEvent> > unreadMessages_; StanzaChannel* stanzaChannel_; IQRouter* iqRouter_; ChatWindowFactory* chatWindowFactory_; ChatWindow* chatWindow_; JID toJID_; bool labelsEnabled_; std::map<JID, std::string> lastMessagesUIID_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; 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 @@ -12,18 +12,19 @@ #include <Swiften/Base/format.h> #include "Swift/Controllers/XMPPEvents/EventController.h" #include "SwifTools/Notifier/Notifier.h" #include "Swiften/Avatars/AvatarManager.h" #include "Swiften/Client/NickResolver.h" #include "Swiften/JID/JID.h" #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 { EventNotifier::EventNotifier(EventController* eventController, Notifier* notifier, AvatarManager* avatarManager, NickResolver* nickResolver) : eventController(eventController), notifier(notifier), avatarManager(avatarManager), nickResolver(nickResolver) { eventController->onEventQueueEventAdded.connect(boost::bind(&EventNotifier::handleEventAdded, this, _1)); } EventNotifier::~EventNotifier() { @@ -49,17 +50,23 @@ void EventNotifier::handleEventAdded(boost::shared_ptr<StanzaEvent> event) { else if(boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event)) { JID jid = subscriptionEvent->getJID(); std::string title = jid; std::string message = str(format(QT_TRANSLATE_NOOP("", "%1% wants to add you to his/her contact list")) % nickResolver->jidToNick(jid)); notifier->showMessage(Notifier::SystemMessage, title, message, boost::filesystem::path(), boost::function<void()>()); } else if(boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event)) { notifier->showMessage(Notifier::SystemMessage, QT_TRANSLATE_NOOP("", "Error"), errorEvent->getText(), boost::filesystem::path(), boost::function<void()>()); } + else if (boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(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())); + } } void EventNotifier::handleNotificationActivated(JID jid) { onNotificationActivated(jid); } } 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 @@ -51,19 +51,19 @@ namespace Swift { virtual void addSystemMessage(const std::string& message) = 0; virtual void addPresenceMessage(const std::string& message) = 0; virtual void addErrorMessage(const std::string& message) = 0; virtual void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0; // File transfer related stuff 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; virtual void setContactChatState(ChatState::ChatStateType state) = 0; virtual void setName(const std::string& name) = 0; virtual void show() = 0; virtual void activate() = 0; virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) = 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 @@ -44,19 +44,19 @@ namespace Swift { virtual void replaceMessage(const std::string&, const std::string&, const boost::posix_time::ptime&) {}; void setAckState(const std::string& /*id*/, AckState /*state*/) {}; virtual void flash() {}; virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {}; virtual void cancelAlert() {}; virtual void setCorrectionEnabled(Tristate /*enabled*/) {} void setAvailableOccupantActions(const std::vector<OccupantAction>&/* 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<JID>&) {} virtual void setAvailableRoomActions(const std::vector<RoomAction> &) {}; std::string name_; std::string lastMessageBody_; std::vector<SecurityLabelsCatalog::Item> labels_; bool labelsEnabled_; SecurityLabelsCatalog::Item label_; }; 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 @@ -7,35 +7,37 @@ #include "Swift/Controllers/XMPPEvents/EventController.h" #include <boost/bind.hpp> #include <algorithm> #include <Swiften/Base/foreach.h> #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 { EventController::EventController() { } EventController::~EventController() { foreach(boost::shared_ptr<StanzaEvent> event, events_) { event->onConclusion.disconnect(boost::bind(&EventController::handleEventConcluded, this, event)); } } void EventController::handleIncomingEvent(boost::shared_ptr<StanzaEvent> sourceEvent) { boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(sourceEvent); boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(sourceEvent); boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(sourceEvent); - if ((messageEvent && messageEvent->isReadable()) || subscriptionEvent || errorEvent) { + boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(sourceEvent); + if ((messageEvent && messageEvent->isReadable()) || subscriptionEvent || errorEvent || mucInviteEvent) { events_.push_back(sourceEvent); sourceEvent->onConclusion.connect(boost::bind(&EventController::handleEventConcluded, this, sourceEvent)); onEventQueueLengthChange(events_.size()); onEventQueueEventAdded(sourceEvent); if (sourceEvent->getConcluded()) { handleEventConcluded(sourceEvent); } } } 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<MUCInviteEvent> 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 @@ -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") |