From 4d0c1d3b8a40cb74cfb30ed27a294afaa6ced56f Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 10 Feb 2010 11:14:50 +0000 Subject: Move the Chat stuff in Controllers into Chat folder. It was starting to get a bit unwieldly in Controllers/ diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp new file mode 100644 index 0000000..e4031f2 --- /dev/null +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -0,0 +1,72 @@ +#include "Swift/Controllers/Chat/ChatController.h" + +#include + +#include "Swiften/Avatars/AvatarManager.h" +#include "Swift/Controllers/UIInterfaces/ChatWindow.h" +#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" +#include "Swift/Controllers/NickResolver.h" + +namespace Swift { + +/** + * The controller does not gain ownership of the stanzaChannel, nor the factory. + */ +ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager) + : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager) { + nickResolver_ = nickResolver; + presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1, _2)); + chatWindow_->setName(nickResolver_->jidToNick(toJID_)); +} + +bool ChatController::isIncomingMessageFromMe(boost::shared_ptr) { + return false; +} + +void ChatController::preHandleIncomingMessage(boost::shared_ptr message) { + JID from = message->getFrom(); + if (!from.equals(toJID_, JID::WithResource)) { + if (toJID_.equals(from, JID::WithoutResource) && toJID_.isBare()){ + toJID_ = from; + } + } +} + +void ChatController::postSendMessage(const String& body) { + addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional(), String(avatarManager_->getAvatarPath(selfJID_).string())); +} + +String ChatController::senderDisplayNameFromMessage(const JID& from) { + return nickResolver_->jidToNick(from); +} + +String ChatController::getStatusChangeString(boost::shared_ptr presence) { + String nick = senderDisplayNameFromMessage(presence->getFrom()); + if (presence->getType() == Presence::Unavailable) { + return nick + " has gone offline."; + } else if (presence->getType() == Presence::Available) { + StatusShow::Type show = presence->getShow(); + if (show == StatusShow::Online || show == StatusShow::FFC) { + return nick + " has become available."; + } else if (show == StatusShow::Away || show == StatusShow::XA) { + return nick + " has gone away."; + } else if (show == StatusShow::DND) { + return nick + " is now busy."; + } + } + + return ""; +} + +void ChatController::handlePresenceChange(boost::shared_ptr newPresence, boost::shared_ptr previousPresence) { + if (!(toJID_.isBare() && newPresence->getFrom().equals(toJID_, JID::WithoutResource)) && newPresence->getFrom() != toJID_) { + return; + } + String newStatusChangeString = getStatusChangeString(newPresence); + if (!previousPresence || newStatusChangeString != getStatusChangeString(previousPresence)) { + chatWindow_->addSystemMessage(newStatusChangeString); + } +} + + +} diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h new file mode 100644 index 0000000..58ea797 --- /dev/null +++ b/Swift/Controllers/Chat/ChatController.h @@ -0,0 +1,27 @@ +#ifndef SWIFTEN_ChatController_H +#define SWIFTEN_ChatController_H + +#include "Swift/Controllers/Chat/ChatControllerBase.h" + +namespace Swift { + class AvatarManager; + class NickResolver; + class ChatController : public ChatControllerBase { + public: + ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager*); + + private: + void handlePresenceChange(boost::shared_ptr newPresence, boost::shared_ptr previousPresence); + String getStatusChangeString(boost::shared_ptr presence); + bool isIncomingMessageFromMe(boost::shared_ptr message); + void postSendMessage(const String &body); + void preHandleIncomingMessage(boost::shared_ptr message); + String senderDisplayNameFromMessage(const JID& from); + + private: + NickResolver* nickResolver_; + JID contact_; + }; +} +#endif + diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp new file mode 100644 index 0000000..5f78795 --- /dev/null +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -0,0 +1,154 @@ +#include "Swift/Controllers/Chat/ChatControllerBase.h" + +#include +#include + +#include "Swiften/Client/StanzaChannel.h" +#include "Swiften/Base/foreach.h" +#include "Swift/Controllers/UIInterfaces/ChatWindow.h" +#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" +#include "Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h" +#include "Swiften/Avatars/AvatarManager.h" + +namespace Swift { + +ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager) { + chatWindow_ = chatWindowFactory_->createChatWindow(toJID); + chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); + chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1)); + setEnabled(stanzaChannel->isAvailable() && iqRouter->isAvailable()); +} + +ChatControllerBase::~ChatControllerBase() { + delete chatWindow_; +} + +void ChatControllerBase::setEnabled(bool enabled) { + chatWindow_->setInputEnabled(enabled); +} + +void ChatControllerBase::setAvailableServerFeatures(boost::shared_ptr info) { + if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::SecurityLabels)) { + chatWindow_->setSecurityLabelsEnabled(true); + chatWindow_->setSecurityLabelsError(); + boost::shared_ptr request(new GetSecurityLabelsCatalogRequest(JID(toJID_.toBare()), iqRouter_)); + request->onResponse.connect(boost::bind(&ChatControllerBase::handleSecurityLabelsCatalogResponse, this, _1, _2)); + request->send(); + labelsEnabled_ = true; + } else { + chatWindow_->setSecurityLabelsEnabled(false); + labelsEnabled_ = false; + } +} + +void ChatControllerBase::handleAllMessagesRead() { + foreach (boost::shared_ptr messageEvent, unreadMessages_) { + messageEvent->read(); + } + unreadMessages_.clear(); + chatWindow_->setUnreadMessageCount(0); +} + +void ChatControllerBase::handleSendMessageRequest(const String &body) { + if (!stanzaChannel_->isAvailable() || body.isEmpty()) { + return; + } + boost::shared_ptr message(new Message()); + message->setTo(toJID_); + message->setType(Swift::Message::Chat); + message->setBody(body); + boost::optional label; + if (labelsEnabled_) { + message->addPayload(boost::shared_ptr(new SecurityLabel(chatWindow_->getSelectedSecurityLabel()))); + label = boost::optional(chatWindow_->getSelectedSecurityLabel()); + } + preSendMessageRequest(message); + stanzaChannel_->sendMessage(message); + postSendMessage(message->getBody()); +} + +void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr catalog, const boost::optional& error) { + if (!error) { + if (catalog->getLabels().size() == 0) { + chatWindow_->setSecurityLabelsEnabled(false); + labelsEnabled_ = false; + } else { + chatWindow_->setAvailableSecurityLabels(catalog->getLabels()); + chatWindow_->setSecurityLabelsEnabled(true); + } + } else { + chatWindow_->setSecurityLabelsError(); + } +} + +void ChatControllerBase::showChatWindow() { + chatWindow_->show(); +} + +void ChatControllerBase::activateChatWindow() { + chatWindow_->activate(); +} + +void ChatControllerBase::addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional& label, const String& avatarPath) { + if (message.beginsWith("/me ")) { + chatWindow_->addMessage(message, senderName, senderIsSelf, label, avatarPath); + } else { + chatWindow_->addAction(message, senderName, senderIsSelf, label, avatarPath); + } +} + +void ChatControllerBase::handleIncomingMessage(boost::shared_ptr messageEvent) { + unreadMessages_.push_back(messageEvent); + chatWindow_->setUnreadMessageCount(unreadMessages_.size()); + + boost::shared_ptr message = messageEvent->getStanza(); + preHandleIncomingMessage(message); + String body = message->getBody(); + if (message->isError()) { + String errorMessage = getErrorMessage(message->getPayload()); + chatWindow_->addErrorMessage(errorMessage); + } + else { + showChatWindow(); + boost::shared_ptr label = message->getPayload(); + boost::optional maybeLabel = label ? boost::optional(*label) : boost::optional(); + JID from = message->getFrom(); + addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), maybeLabel, String(avatarManager_->getAvatarPath(from).string())); + } +} + +String ChatControllerBase::getErrorMessage(boost::shared_ptr error) { + String defaultMessage = "Error sending message"; + if (!error->getText().isEmpty()) { + return error->getText(); + } + else { + switch (error->getCondition()) { + case ErrorPayload::BadRequest: return defaultMessage; break; + case ErrorPayload::Conflict: return defaultMessage; break; + case ErrorPayload::FeatureNotImplemented: return defaultMessage; break; + case ErrorPayload::Forbidden: return defaultMessage; break; + case ErrorPayload::Gone: return "Recipient can no longer be contacted"; break; + case ErrorPayload::InternalServerError: return "Internal server error"; break; + case ErrorPayload::ItemNotFound: return defaultMessage; break; + case ErrorPayload::JIDMalformed: return defaultMessage; break; + case ErrorPayload::NotAcceptable: return "Message was rejected"; break; + case ErrorPayload::NotAllowed: return defaultMessage; break; + case ErrorPayload::NotAuthorized: return defaultMessage; break; + case ErrorPayload::PaymentRequired: return defaultMessage; break; + case ErrorPayload::RecipientUnavailable: return "Recipient is unavailable."; break; + case ErrorPayload::Redirect: return defaultMessage; break; + case ErrorPayload::RegistrationRequired: return defaultMessage; break; + case ErrorPayload::RemoteServerNotFound: return "Recipient's server not found."; break; + case ErrorPayload::RemoteServerTimeout: return defaultMessage; break; + case ErrorPayload::ResourceConstraint: return defaultMessage; break; + case ErrorPayload::ServiceUnavailable: return defaultMessage; break; + case ErrorPayload::SubscriptionRequired: return defaultMessage; break; + case ErrorPayload::UndefinedCondition: return defaultMessage; break; + case ErrorPayload::UnexpectedRequest: return defaultMessage; break; + } + } + return defaultMessage; +} + +} diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h new file mode 100644 index 0000000..abf0116 --- /dev/null +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -0,0 +1,66 @@ +#ifndef SWIFTEN_ChatControllerBase_H +#define SWIFTEN_ChatControllerBase_H + +#include +#include +#include +#include +#include + +#include "Swiften/Base/String.h" +#include "Swiften/Elements/DiscoInfo.h" +#include "Swiften/Events/MessageEvent.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" + +namespace Swift { + class IQRouter; + class StanzaChannel; + class ChatWindow; + class ChatWindowFactory; + class AvatarManager; + + class ChatControllerBase { + public: + virtual ~ChatControllerBase(); + void showChatWindow(); + void activateChatWindow(); + void setAvailableServerFeatures(boost::shared_ptr info); + void handleIncomingMessage(boost::shared_ptr message); + void addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional& label, const String& avatarPath); + void setEnabled(bool enabled); + void setToJID(const JID& jid) {toJID_ = jid;}; + protected: + ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager); + + virtual void postSendMessage(const String&) {}; + virtual String senderDisplayNameFromMessage(const JID& from) = 0; + virtual bool isIncomingMessageFromMe(boost::shared_ptr) = 0; + virtual void preHandleIncomingMessage(boost::shared_ptr) {}; + virtual void preSendMessageRequest(boost::shared_ptr) {}; + + + private: + void handleSendMessageRequest(const String &body); + void handleAllMessagesRead(); + void handleSecurityLabelsCatalogResponse(boost::shared_ptr, const boost::optional& error); + String getErrorMessage(boost::shared_ptr); + + protected: + JID selfJID_; + std::vector > unreadMessages_; + StanzaChannel* stanzaChannel_; + IQRouter* iqRouter_; + ChatWindowFactory* chatWindowFactory_; + ChatWindow* chatWindow_; + JID toJID_; + bool labelsEnabled_; + PresenceOracle* presenceOracle_; + AvatarManager* avatarManager_; + }; +} + +#endif diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp new file mode 100644 index 0000000..0efd3e1 --- /dev/null +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -0,0 +1,164 @@ +#include "Swift/Controllers/Chat/ChatsManager.h" + +#include + +#include "Swiften/Client/Client.h" + +#include "Swift/Controllers/Chat/ChatController.h" +#include "Swift/Controllers/EventController.h" +#include "Swift/Controllers/Chat/MUCController.h" +#include "Swiften/Presence/PresenceSender.h" + +namespace Swift { + +typedef std::pair JIDChatControllerPair; +typedef std::pair JIDMUCControllerPair; + +ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr serverDiscoInfo, PresenceSender* presenceSender) : jid_(jid) { + eventController_ = eventController; + stanzaChannel_ = stanzaChannel; + iqRouter_ = iqRouter; + chatWindowFactory_ = chatWindowFactory; + treeWidgetFactory_ = treeWidgetFactory; + nickResolver_ = nickResolver; + presenceOracle_ = presenceOracle; + avatarManager_ = NULL; + serverDiscoInfo_ = serverDiscoInfo; + presenceSender_ = presenceSender; + presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1, _2)); +} + +ChatsManager::~ChatsManager() { + foreach (JIDChatControllerPair controllerPair, chatControllers_) { + delete controllerPair.second; + } + foreach (JIDMUCControllerPair controllerPair, mucControllers_) { + delete controllerPair.second; + } + +} + +/** + * If a resource goes offline, release bound chatdialog to that resource. + */ +void ChatsManager::handlePresenceChange(boost::shared_ptr newPresence, boost::shared_ptr /*lastPresence*/) { + if (newPresence->getType() != Presence::Unavailable) return; + JID fullJID(newPresence->getFrom()); + std::map::iterator it = chatControllers_.find(fullJID); + if (it == chatControllers_.end()) return; + JID bareJID(fullJID.toBare()); + //It doesn't make sense to have two unbound dialogs. + if (chatControllers_.find(bareJID) != chatControllers_.end()) return; + rebindControllerJID(fullJID, bareJID); +} + +void ChatsManager::setAvatarManager(AvatarManager* avatarManager) { + avatarManager_ = avatarManager; +} + +// void ChatsManager::handleUIEvent(boost::shared_ptr rawEvent) { +// { +// boost::shared_ptr event = boost::dynamic_pointer_cast(rawEvent); +// if (event != NULL) { +// handleChatRequest(event->getContact()); +// return; +// } +// } +// { +// boost::shared_ptr event = boost::dynamic_pointer_cast(rawEvent); +// if (event != NULL) { +// handleJoinMUCRequest(event->getRoom(), event->getNick()); +// } +// } +// } + + +void ChatsManager::setServerDiscoInfo(boost::shared_ptr info) { + foreach (JIDChatControllerPair pair, chatControllers_) { + pair.second->setAvailableServerFeatures(info); + } + foreach (JIDMUCControllerPair pair, mucControllers_) { + pair.second->setAvailableServerFeatures(info); + } +} + +void ChatsManager::setEnabled(bool enabled) { + foreach (JIDChatControllerPair controllerPair, chatControllers_) { + //printf("Setting enabled on %d to %d\n", controllerPair.second, enabled); + controllerPair.second->setEnabled(enabled); + } + foreach (JIDMUCControllerPair controllerPair, mucControllers_) { + controllerPair.second->setEnabled(enabled); + } + +} + +void ChatsManager::handleChatRequest(const String &contact) { + ChatController* controller = getChatController(JID(contact)); + controller->showChatWindow(); + controller->activateChatWindow(); +} + +ChatController* ChatsManager::getChatController(const JID &contact) { + if (chatControllers_.find(contact) == chatControllers_.end()) { + //Need to look for an unboud window to bind first + JID bare(contact.toBare()); + if (chatControllers_.find(bare) != chatControllers_.end()) { + rebindControllerJID(bare, contact); + } else { + chatControllers_[contact] = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_); + chatControllers_[contact]->setAvailableServerFeatures(serverDiscoInfo_); + } + } + return chatControllers_[contact]; +} + +void ChatsManager::rebindControllerJID(const JID& from, const JID& to) { + chatControllers_[to] = chatControllers_[from]; + chatControllers_.erase(from); + chatControllers_[to]->setToJID(to); +} + +void ChatsManager::handleJoinMUCRequest(const JID &muc, const String &nick) { + std::map::iterator it = mucControllers_.find(muc); + if (it != mucControllers_.end()) { + //FIXME: What's correct behaviour here? + } else { + mucControllers_[muc] = new MUCController(jid_, muc, nick, stanzaChannel_, presenceSender_, iqRouter_, chatWindowFactory_, treeWidgetFactory_, presenceOracle_, avatarManager_); + mucControllers_[muc]->setAvailableServerFeatures(serverDiscoInfo_); + } + mucControllers_[muc]->activateChatWindow(); +} + +void ChatsManager::handleIncomingMessage(boost::shared_ptr message) { + JID jid = message->getFrom(); + boost::shared_ptr event(new MessageEvent(message)); + if (!event->isReadable()) { + return; + } + + // Try to deliver it to a MUC + if (message->getType() == Message::Groupchat || message->getType() == Message::Error) { + std::map::iterator i = mucControllers_.find(jid.toBare()); + if (i != mucControllers_.end()) { + i->second->handleIncomingMessage(event); + return; + } + else if (message->getType() == Message::Groupchat) { + //FIXME: Error handling - groupchat messages from an unknown muc. + return; + } + } + + //if not a mucroom + eventController_->handleIncomingEvent(event); + getChatController(jid)->handleIncomingMessage(event); +} + +bool ChatsManager::isMUC(const JID& jid) const { + return mucControllers_.find(jid.toBare()) != mucControllers_.end(); +} + + + +} diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h new file mode 100644 index 0000000..dd80d95 --- /dev/null +++ b/Swift/Controllers/Chat/ChatsManager.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include + +#include "Swiften/Base/String.h" +#include "Swiften/Elements/DiscoInfo.h" +#include "Swiften/Elements/Message.h" +#include "Swiften/Elements/Presence.h" +#include "Swiften/JID/JID.h" +#include "Swiften/MUC/MUCRegistry.h" + +namespace Swift { + class EventController; + class ChatController; + class MUCController; + class ChatWindowFactory; + class TreeWidgetFactory; + class NickResolver; + class PresenceOracle; + class AvatarManager; + class StanzaChannel; + class IQRouter; + class PresenceSender; + class ChatsManager : public MUCRegistry { + public: + ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr serverDiscoInfo, PresenceSender* presenceSender); + ~ChatsManager(); + void setAvatarManager(AvatarManager* avatarManager); + void setEnabled(bool enabled); + void setServerDiscoInfo(boost::shared_ptr info); + void handleIncomingMessage(boost::shared_ptr message); + void handleChatRequest(const String& contact); + void handleJoinMUCRequest(const JID& muc, const String& nick); + private: + void rebindControllerJID(const JID& from, const JID& to); + void handlePresenceChange(boost::shared_ptr newPresence, boost::shared_ptr lastPresence); + ChatController* getChatController(const JID &contact); + virtual bool isMUC(const JID& muc) const; + + std::map mucControllers_; + std::map chatControllers_; + EventController* eventController_; + JID jid_; + StanzaChannel* stanzaChannel_; + IQRouter* iqRouter_;; + ChatWindowFactory* chatWindowFactory_; + TreeWidgetFactory* treeWidgetFactory_; + NickResolver* nickResolver_; + PresenceOracle* presenceOracle_; + AvatarManager* avatarManager_; + PresenceSender* presenceSender_; + boost::shared_ptr serverDiscoInfo_; + }; +} diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp new file mode 100644 index 0000000..7736aec --- /dev/null +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -0,0 +1,92 @@ +#include "Swift/Controllers/Chat/MUCController.h" + +#include + +#include "Swiften/Base/foreach.h" +#include "Swift/Controllers/UIInterfaces/ChatWindow.h" +#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" +#include "Swiften/Avatars/AvatarManager.h" +#include "Swiften/MUC/MUC.h" +#include "Swiften/Client/StanzaChannel.h" +#include "Swiften/Roster/Roster.h" +#include "Swiften/Roster/SetAvatar.h" +#include "Swiften/Roster/SetPresence.h" +#include "Swiften/Roster/TreeWidgetFactory.h" + +namespace Swift { + +/** + * The controller does not gain ownership of the stanzaChannel, nor the factory. + */ +MUCController::MUCController ( + const JID& self, + const JID &muc, + const String &nick, + StanzaChannel* stanzaChannel, + PresenceSender* presenceSender, + IQRouter* iqRouter, + ChatWindowFactory* chatWindowFactory, + TreeWidgetFactory *treeWidgetFactory, + PresenceOracle* presenceOracle, + AvatarManager* avatarManager) : + ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager), + muc_(new MUC(stanzaChannel, presenceSender, muc)), + nick_(nick), + treeWidgetFactory_(treeWidgetFactory) { + roster_ = new Roster(chatWindow_->getTreeWidget(), treeWidgetFactory_); + chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this)); + muc_->joinAs(nick); + muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1)); + muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1)); + muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3)); + chatWindow_->convertToMUC(); + chatWindow_->show(); + if (avatarManager_ != NULL) { + avatarManager_->onAvatarChanged.connect(boost::bind(&MUCController::handleAvatarChanged, this, _1, _2)); + } +} + +MUCController::~MUCController() { + delete muc_; + delete roster_; +} + +void MUCController::handleAvatarChanged(const JID& jid, const String&) { + String path = avatarManager_->getAvatarPath(jid).string(); + roster_->applyOnItems(SetAvatar(jid, path, JID::WithResource)); +} + +void MUCController::handleWindowClosed() { + muc_->part(); +} + +void MUCController::handleOccupantJoined(const MUCOccupant& occupant) { + JID jid(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick())); + roster_->addContact(jid, occupant.getNick(), "Occupants"); + if (avatarManager_ != NULL) { + handleAvatarChanged(jid, "dummy"); + } +} + +void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType, const String& /*reason*/) { + roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick())); +} + +void MUCController::handleOccupantPresenceChange(boost::shared_ptr presence) { + roster_->applyOnItems(SetPresence(presence, JID::WithResource)); +} + +bool MUCController::isIncomingMessageFromMe(boost::shared_ptr message) { + JID from = message->getFrom(); + return nick_ == from.getResource(); +} + +String MUCController::senderDisplayNameFromMessage(const JID& from) { + return from.getResource(); +} + +void MUCController::preSendMessageRequest(boost::shared_ptr message) { + message->setType(Swift::Message::Groupchat); +} + +} diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h new file mode 100644 index 0000000..bafe3db --- /dev/null +++ b/Swift/Controllers/Chat/MUCController.h @@ -0,0 +1,48 @@ +#ifndef SWIFTEN_MUCController_H +#define SWIFTEN_MUCController_H + +#include + +#include "Swiften/Base/String.h" +#include "Swift/Controllers/Chat/ChatControllerBase.h" +#include "Swiften/Elements/Message.h" +#include "Swiften/Elements/DiscoInfo.h" +#include "Swiften/JID/JID.h" +#include "Swiften/MUC/MUC.h" +#include "Swiften/MUC/MUCOccupant.h" + +namespace Swift { + class StanzaChannel; + class IQRouter; + class ChatWindow; + class ChatWindowFactory; + class Roster; + class TreeWidgetFactory; + class AvatarManager; + + class MUCController : public ChatControllerBase { + public: + MUCController(const JID& self, const JID &muc, const String &nick, StanzaChannel* stanzaChannel, PresenceSender* presenceSender, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory *treeWidgetFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager); + ~MUCController(); + + protected: + void preSendMessageRequest(boost::shared_ptr message); + bool isIncomingMessageFromMe(boost::shared_ptr message); + String senderDisplayNameFromMessage(const JID& from); + + private: + void handleWindowClosed(); + void handleAvatarChanged(const JID& jid, const String&); + void handleOccupantJoined(const MUCOccupant& occupant); + void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const String& reason); + void handleOccupantPresenceChange(boost::shared_ptr presence); + + private: + MUC *muc_; + String nick_; + TreeWidgetFactory *treeWidgetFactory_; + Roster *roster_; + }; +} +#endif + diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp new file mode 100644 index 0000000..ab1c03b --- /dev/null +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -0,0 +1,269 @@ +#include +#include +#include "3rdParty/hippomocks.h" + +#include "Swift/Controllers/Chat/ChatsManager.h" + +#include "Swift/Controllers/UIInterfaces/ChatWindow.h" +#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" +#include "Swiften/Roster/TreeWidgetFactory.h" +#include "Swiften/Client/Client.h" +#include "Swift/Controllers/Chat/ChatController.h" +#include "Swift/Controllers/EventController.h" +#include "Swift/Controllers/Chat/MUCController.h" +#include "Swiften/Presence/PresenceSender.h" +#include "Swiften/Avatars/UnitTest/MockAvatarManager.h" +#include "Swift/Controllers/NickResolver.h" +#include "Swiften/Roster/XMPPRoster.h" +#include "Swift/Controllers/UnitTest/MockChatWindow.h" +#include "Swiften/Client/DummyStanzaChannel.h" +#include "Swiften/Queries/DummyIQChannel.h" +#include "Swiften/Presence/PresenceOracle.h" + + +using namespace Swift; + +class ChatsManagerTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(ChatsManagerTest); + CPPUNIT_TEST(testFirstOpenWindowIncoming); + CPPUNIT_TEST(testSecondOpenWindowIncoming); + CPPUNIT_TEST(testFirstOpenWindowOutgoing); + CPPUNIT_TEST(testFirstOpenWindowBareToFull); + CPPUNIT_TEST(testSecondWindow); + CPPUNIT_TEST(testUnbindRebind); + CPPUNIT_TEST(testNoDuplicateUnbind); + CPPUNIT_TEST_SUITE_END(); + +public: + ChatsManagerTest() {}; + + void setUp() { + mocks_ = new MockRepository(); + jid_ = JID("test@test.com/resource"); + stanzaChannel_ = new DummyStanzaChannel(); + iqChannel_ = new DummyIQChannel(); + iqRouter_ = new IQRouter(iqChannel_); + eventController_ = new EventController(); + chatWindowFactory_ = mocks_->InterfaceMock(); + treeWidgetFactory_ = NULL; + xmppRoster_ = boost::shared_ptr(new XMPPRoster()); + nickResolver_ = new NickResolver(xmppRoster_); + presenceOracle_ = new PresenceOracle(stanzaChannel_); + serverDiscoInfo_ = boost::shared_ptr(new DiscoInfo()); + presenceSender_ = NULL; + manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, treeWidgetFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_); + avatarManager_ = new MockAvatarManager(); + manager_->setAvatarManager(avatarManager_); + }; + + void tearDown() { + delete manager_; + delete presenceSender_; + delete avatarManager_; + delete presenceOracle_; + delete nickResolver_; + delete treeWidgetFactory_; + delete stanzaChannel_; + delete iqChannel_; + delete iqRouter_; + delete mocks_; + } + + void testFirstOpenWindowIncoming() { + JID messageJID("testling@test.com/resource1"); + + MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID).Return(window); + + boost::shared_ptr message(new Message()); + message->setFrom(messageJID); + String body("This is a legible message. >HEH@)oeueu"); + message->setBody(body); + manager_->handleIncomingMessage(message); + CPPUNIT_ASSERT_EQUAL(body, window->lastMessageBody_); + } + + void testSecondOpenWindowIncoming() { + JID messageJID1("testling@test.com/resource1"); + + MockChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1).Return(window1); + + boost::shared_ptr message1(new Message()); + message1->setFrom(messageJID1); + String body1("This is a legible message. >HEH@)oeueu"); + message1->setBody(body1); + manager_->handleIncomingMessage(message1); + CPPUNIT_ASSERT_EQUAL(body1, window1->lastMessageBody_); + + JID messageJID2("testling@test.com/resource2"); + + MockChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID2).Return(window2); + + boost::shared_ptr message2(new Message()); + message2->setFrom(messageJID2); + String body2("This is a legible message. .cmaulm.chul"); + message2->setBody(body2); + manager_->handleIncomingMessage(message2); + CPPUNIT_ASSERT_EQUAL(body2, window2->lastMessageBody_); + } + + void testFirstOpenWindowOutgoing() { + String messageJIDString("testling@test.com"); + + ChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString)).Return(window); + + manager_->handleChatRequest(messageJIDString); + } + + + void testFirstOpenWindowBareToFull() { + String bareJIDString("testling@test.com"); + String fullJIDString("testling@test.com/resource1"); + + MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString)).Return(window); + manager_->handleChatRequest(bareJIDString); + + boost::shared_ptr message(new Message()); + message->setFrom(JID(fullJIDString)); + String body("This is a legible message. mjuga3089gm8G(*>M)@*("); + message->setBody(body); + manager_->handleIncomingMessage(message); + CPPUNIT_ASSERT_EQUAL(body, window->lastMessageBody_); + } + + void testSecondWindow() { + String messageJIDString1("testling1@test.com"); + ChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString1)).Return(window1); + manager_->handleChatRequest(messageJIDString1); + + String messageJIDString2("testling2@test.com"); + ChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString2)).Return(window2); + + manager_->handleChatRequest(messageJIDString2); + } + + /** Complete cycle. + Create unbound window. + Bind it. + Unbind it. + Rebind it. + */ + void testUnbindRebind() { + String bareJIDString("testling@test.com"); + String fullJIDString1("testling@test.com/resource1"); + String fullJIDString2("testling@test.com/resource2"); + + MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString)).Return(window); + manager_->handleChatRequest(bareJIDString); + + boost::shared_ptr message1(new Message()); + message1->setFrom(JID(fullJIDString1)); + String messageBody1("This is a legible message."); + message1->setBody(messageBody1); + manager_->handleIncomingMessage(message1); + CPPUNIT_ASSERT_EQUAL(messageBody1, window->lastMessageBody_); + + boost::shared_ptr jid1Online(new Presence()); + jid1Online->setFrom(JID(fullJIDString1)); + boost::shared_ptr jid1Offline(new Presence()); + jid1Offline->setFrom(JID(fullJIDString1)); + jid1Offline->setType(Presence::Unavailable); + presenceOracle_->onPresenceChange(jid1Offline, jid1Online); + + boost::shared_ptr message2(new Message()); + message2->setFrom(JID(fullJIDString2)); + String messageBody2("This is another legible message."); + message2->setBody(messageBody2); + manager_->handleIncomingMessage(message2); + CPPUNIT_ASSERT_EQUAL(messageBody2, window->lastMessageBody_); + } + + /** + Test that a second window isn't unbound where there's already an unbound one. + Bind 1 + Bind 2 + Unbind 1 + Unbind 2 (but it doesn't) + Sent to bound 2 + Rebind 1 + */ + void testNoDuplicateUnbind() { + JID messageJID1("testling@test.com/resource1"); + + MockChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1).Return(window1); + + boost::shared_ptr message1(new Message()); + message1->setFrom(messageJID1); + message1->setBody("This is a legible message1."); + manager_->handleIncomingMessage(message1); + + JID messageJID2("testling@test.com/resource2"); + + MockChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID2).Return(window2); + + boost::shared_ptr message2(new Message()); + message2->setFrom(messageJID2); + message2->setBody("This is a legible message2."); + manager_->handleIncomingMessage(message2); + + boost::shared_ptr jid1Online(new Presence()); + jid1Online->setFrom(JID(messageJID1)); + boost::shared_ptr jid1Offline(new Presence()); + jid1Offline->setFrom(JID(messageJID1)); + jid1Offline->setType(Presence::Unavailable); + presenceOracle_->onPresenceChange(jid1Offline, jid1Online); + + boost::shared_ptr jid2Online(new Presence()); + jid2Online->setFrom(JID(messageJID2)); + boost::shared_ptr jid2Offline(new Presence()); + jid2Offline->setFrom(JID(messageJID2)); + jid2Offline->setType(Presence::Unavailable); + presenceOracle_->onPresenceChange(jid2Offline, jid2Online); + + JID messageJID3("testling@test.com/resource3"); + + boost::shared_ptr message3(new Message()); + message3->setFrom(messageJID3); + String body3("This is a legible message3."); + message3->setBody(body3); + manager_->handleIncomingMessage(message3); + CPPUNIT_ASSERT_EQUAL(body3, window1->lastMessageBody_); + + boost::shared_ptr message2b(new Message()); + message2b->setFrom(messageJID2); + String body2b("This is a legible message2b."); + message2b->setBody(body2b); + manager_->handleIncomingMessage(message2b); + CPPUNIT_ASSERT_EQUAL(body2b, window2->lastMessageBody_); + } + +private: + JID jid_; + ChatsManager* manager_; + StanzaChannel* stanzaChannel_; + IQChannel* iqChannel_; + IQRouter* iqRouter_; + EventController* eventController_; + ChatWindowFactory* chatWindowFactory_; + TreeWidgetFactory* treeWidgetFactory_; + NickResolver* nickResolver_; + PresenceOracle* presenceOracle_; + AvatarManager* avatarManager_; + boost::shared_ptr serverDiscoInfo_; + boost::shared_ptr xmppRoster_; + PresenceSender* presenceSender_; + MockRepository* mocks_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); + diff --git a/Swift/Controllers/ChatController.cpp b/Swift/Controllers/ChatController.cpp deleted file mode 100644 index c4f45d8..0000000 --- a/Swift/Controllers/ChatController.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "Swift/Controllers/ChatController.h" - -#include - -#include "Swiften/Avatars/AvatarManager.h" -#include "Swift/Controllers/UIInterfaces/ChatWindow.h" -#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" -#include "Swift/Controllers/NickResolver.h" - -namespace Swift { - -/** - * The controller does not gain ownership of the stanzaChannel, nor the factory. - */ -ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager) - : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager) { - nickResolver_ = nickResolver; - presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1, _2)); - chatWindow_->setName(nickResolver_->jidToNick(toJID_)); -} - -bool ChatController::isIncomingMessageFromMe(boost::shared_ptr) { - return false; -} - -void ChatController::preHandleIncomingMessage(boost::shared_ptr message) { - JID from = message->getFrom(); - if (!from.equals(toJID_, JID::WithResource)) { - if (toJID_.equals(from, JID::WithoutResource) && toJID_.isBare()){ - toJID_ = from; - } - } -} - -void ChatController::postSendMessage(const String& body) { - addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional(), String(avatarManager_->getAvatarPath(selfJID_).string())); -} - -String ChatController::senderDisplayNameFromMessage(const JID& from) { - return nickResolver_->jidToNick(from); -} - -String ChatController::getStatusChangeString(boost::shared_ptr presence) { - String nick = senderDisplayNameFromMessage(presence->getFrom()); - if (presence->getType() == Presence::Unavailable) { - return nick + " has gone offline."; - } else if (presence->getType() == Presence::Available) { - StatusShow::Type show = presence->getShow(); - if (show == StatusShow::Online || show == StatusShow::FFC) { - return nick + " has become available."; - } else if (show == StatusShow::Away || show == StatusShow::XA) { - return nick + " has gone away."; - } else if (show == StatusShow::DND) { - return nick + " is now busy."; - } - } - - return ""; -} - -void ChatController::handlePresenceChange(boost::shared_ptr newPresence, boost::shared_ptr previousPresence) { - if (!(toJID_.isBare() && newPresence->getFrom().equals(toJID_, JID::WithoutResource)) && newPresence->getFrom() != toJID_) { - return; - } - String newStatusChangeString = getStatusChangeString(newPresence); - if (!previousPresence || newStatusChangeString != getStatusChangeString(previousPresence)) { - chatWindow_->addSystemMessage(newStatusChangeString); - } -} - - -} diff --git a/Swift/Controllers/ChatController.h b/Swift/Controllers/ChatController.h deleted file mode 100644 index ea844ad..0000000 --- a/Swift/Controllers/ChatController.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef SWIFTEN_ChatController_H -#define SWIFTEN_ChatController_H - -#include "Swift/Controllers/ChatControllerBase.h" - -namespace Swift { - class AvatarManager; - class NickResolver; - class ChatController : public ChatControllerBase { - public: - ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager*); - - private: - void handlePresenceChange(boost::shared_ptr newPresence, boost::shared_ptr previousPresence); - String getStatusChangeString(boost::shared_ptr presence); - bool isIncomingMessageFromMe(boost::shared_ptr message); - void postSendMessage(const String &body); - void preHandleIncomingMessage(boost::shared_ptr message); - String senderDisplayNameFromMessage(const JID& from); - - private: - NickResolver* nickResolver_; - JID contact_; - }; -} -#endif - diff --git a/Swift/Controllers/ChatControllerBase.cpp b/Swift/Controllers/ChatControllerBase.cpp deleted file mode 100644 index 3e1ce5e..0000000 --- a/Swift/Controllers/ChatControllerBase.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include "Swift/Controllers/ChatControllerBase.h" - -#include -#include - -#include "Swiften/Client/StanzaChannel.h" -#include "Swiften/Base/foreach.h" -#include "Swift/Controllers/UIInterfaces/ChatWindow.h" -#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" -#include "Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h" -#include "Swiften/Avatars/AvatarManager.h" - -namespace Swift { - -ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager) { - chatWindow_ = chatWindowFactory_->createChatWindow(toJID); - chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); - chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1)); - setEnabled(stanzaChannel->isAvailable() && iqRouter->isAvailable()); -} - -ChatControllerBase::~ChatControllerBase() { - delete chatWindow_; -} - -void ChatControllerBase::setEnabled(bool enabled) { - chatWindow_->setInputEnabled(enabled); -} - -void ChatControllerBase::setAvailableServerFeatures(boost::shared_ptr info) { - if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::SecurityLabels)) { - chatWindow_->setSecurityLabelsEnabled(true); - chatWindow_->setSecurityLabelsError(); - boost::shared_ptr request(new GetSecurityLabelsCatalogRequest(JID(toJID_.toBare()), iqRouter_)); - request->onResponse.connect(boost::bind(&ChatControllerBase::handleSecurityLabelsCatalogResponse, this, _1, _2)); - request->send(); - labelsEnabled_ = true; - } else { - chatWindow_->setSecurityLabelsEnabled(false); - labelsEnabled_ = false; - } -} - -void ChatControllerBase::handleAllMessagesRead() { - foreach (boost::shared_ptr messageEvent, unreadMessages_) { - messageEvent->read(); - } - unreadMessages_.clear(); - chatWindow_->setUnreadMessageCount(0); -} - -void ChatControllerBase::handleSendMessageRequest(const String &body) { - if (!stanzaChannel_->isAvailable() || body.isEmpty()) { - return; - } - boost::shared_ptr message(new Message()); - message->setTo(toJID_); - message->setType(Swift::Message::Chat); - message->setBody(body); - boost::optional label; - if (labelsEnabled_) { - message->addPayload(boost::shared_ptr(new SecurityLabel(chatWindow_->getSelectedSecurityLabel()))); - label = boost::optional(chatWindow_->getSelectedSecurityLabel()); - } - preSendMessageRequest(message); - stanzaChannel_->sendMessage(message); - postSendMessage(message->getBody()); -} - -void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr catalog, const boost::optional& error) { - if (!error) { - if (catalog->getLabels().size() == 0) { - chatWindow_->setSecurityLabelsEnabled(false); - labelsEnabled_ = false; - } else { - chatWindow_->setAvailableSecurityLabels(catalog->getLabels()); - chatWindow_->setSecurityLabelsEnabled(true); - } - } else { - chatWindow_->setSecurityLabelsError(); - } -} - -void ChatControllerBase::showChatWindow() { - chatWindow_->show(); -} - -void ChatControllerBase::activateChatWindow() { - chatWindow_->activate(); -} - -void ChatControllerBase::addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional& label, const String& avatarPath) { - if (message.beginsWith("/me ")) { - chatWindow_->addMessage(message, senderName, senderIsSelf, label, avatarPath); - } else { - chatWindow_->addAction(message, senderName, senderIsSelf, label, avatarPath); - } -} - -void ChatControllerBase::handleIncomingMessage(boost::shared_ptr messageEvent) { - unreadMessages_.push_back(messageEvent); - chatWindow_->setUnreadMessageCount(unreadMessages_.size()); - - boost::shared_ptr message = messageEvent->getStanza(); - preHandleIncomingMessage(message); - String body = message->getBody(); - if (message->isError()) { - String errorMessage = getErrorMessage(message->getPayload()); - chatWindow_->addErrorMessage(errorMessage); - } - else { - showChatWindow(); - boost::shared_ptr label = message->getPayload(); - boost::optional maybeLabel = label ? boost::optional(*label) : boost::optional(); - JID from = message->getFrom(); - addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), maybeLabel, String(avatarManager_->getAvatarPath(from).string())); - } -} - -String ChatControllerBase::getErrorMessage(boost::shared_ptr error) { - String defaultMessage = "Error sending message"; - if (!error->getText().isEmpty()) { - return error->getText(); - } - else { - switch (error->getCondition()) { - case ErrorPayload::BadRequest: return defaultMessage; break; - case ErrorPayload::Conflict: return defaultMessage; break; - case ErrorPayload::FeatureNotImplemented: return defaultMessage; break; - case ErrorPayload::Forbidden: return defaultMessage; break; - case ErrorPayload::Gone: return "Recipient can no longer be contacted"; break; - case ErrorPayload::InternalServerError: return "Internal server error"; break; - case ErrorPayload::ItemNotFound: return defaultMessage; break; - case ErrorPayload::JIDMalformed: return defaultMessage; break; - case ErrorPayload::NotAcceptable: return "Message was rejected"; break; - case ErrorPayload::NotAllowed: return defaultMessage; break; - case ErrorPayload::NotAuthorized: return defaultMessage; break; - case ErrorPayload::PaymentRequired: return defaultMessage; break; - case ErrorPayload::RecipientUnavailable: return "Recipient is unavailable."; break; - case ErrorPayload::Redirect: return defaultMessage; break; - case ErrorPayload::RegistrationRequired: return defaultMessage; break; - case ErrorPayload::RemoteServerNotFound: return "Recipient's server not found."; break; - case ErrorPayload::RemoteServerTimeout: return defaultMessage; break; - case ErrorPayload::ResourceConstraint: return defaultMessage; break; - case ErrorPayload::ServiceUnavailable: return defaultMessage; break; - case ErrorPayload::SubscriptionRequired: return defaultMessage; break; - case ErrorPayload::UndefinedCondition: return defaultMessage; break; - case ErrorPayload::UnexpectedRequest: return defaultMessage; break; - } - } - return defaultMessage; -} - -} diff --git a/Swift/Controllers/ChatControllerBase.h b/Swift/Controllers/ChatControllerBase.h deleted file mode 100644 index abf0116..0000000 --- a/Swift/Controllers/ChatControllerBase.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef SWIFTEN_ChatControllerBase_H -#define SWIFTEN_ChatControllerBase_H - -#include -#include -#include -#include -#include - -#include "Swiften/Base/String.h" -#include "Swiften/Elements/DiscoInfo.h" -#include "Swiften/Events/MessageEvent.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" - -namespace Swift { - class IQRouter; - class StanzaChannel; - class ChatWindow; - class ChatWindowFactory; - class AvatarManager; - - class ChatControllerBase { - public: - virtual ~ChatControllerBase(); - void showChatWindow(); - void activateChatWindow(); - void setAvailableServerFeatures(boost::shared_ptr info); - void handleIncomingMessage(boost::shared_ptr message); - void addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional& label, const String& avatarPath); - void setEnabled(bool enabled); - void setToJID(const JID& jid) {toJID_ = jid;}; - protected: - ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager); - - virtual void postSendMessage(const String&) {}; - virtual String senderDisplayNameFromMessage(const JID& from) = 0; - virtual bool isIncomingMessageFromMe(boost::shared_ptr) = 0; - virtual void preHandleIncomingMessage(boost::shared_ptr) {}; - virtual void preSendMessageRequest(boost::shared_ptr) {}; - - - private: - void handleSendMessageRequest(const String &body); - void handleAllMessagesRead(); - void handleSecurityLabelsCatalogResponse(boost::shared_ptr, const boost::optional& error); - String getErrorMessage(boost::shared_ptr); - - protected: - JID selfJID_; - std::vector > unreadMessages_; - StanzaChannel* stanzaChannel_; - IQRouter* iqRouter_; - ChatWindowFactory* chatWindowFactory_; - ChatWindow* chatWindow_; - JID toJID_; - bool labelsEnabled_; - PresenceOracle* presenceOracle_; - AvatarManager* avatarManager_; - }; -} - -#endif diff --git a/Swift/Controllers/ChatsManager.cpp b/Swift/Controllers/ChatsManager.cpp deleted file mode 100644 index a805802..0000000 --- a/Swift/Controllers/ChatsManager.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "Swift/Controllers/ChatsManager.h" - -#include - -#include "Swiften/Client/Client.h" - -#include "Swift/Controllers/ChatController.h" -#include "Swift/Controllers/EventController.h" -#include "Swift/Controllers/MUCController.h" -#include "Swiften/Presence/PresenceSender.h" - -namespace Swift { - -typedef std::pair JIDChatControllerPair; -typedef std::pair JIDMUCControllerPair; - -ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr serverDiscoInfo, PresenceSender* presenceSender) : jid_(jid) { - eventController_ = eventController; - stanzaChannel_ = stanzaChannel; - iqRouter_ = iqRouter; - chatWindowFactory_ = chatWindowFactory; - treeWidgetFactory_ = treeWidgetFactory; - nickResolver_ = nickResolver; - presenceOracle_ = presenceOracle; - avatarManager_ = NULL; - serverDiscoInfo_ = serverDiscoInfo; - presenceSender_ = presenceSender; - presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1, _2)); -} - -ChatsManager::~ChatsManager() { - foreach (JIDChatControllerPair controllerPair, chatControllers_) { - delete controllerPair.second; - } - foreach (JIDMUCControllerPair controllerPair, mucControllers_) { - delete controllerPair.second; - } - -} - -/** - * If a resource goes offline, release bound chatdialog to that resource. - */ -void ChatsManager::handlePresenceChange(boost::shared_ptr newPresence, boost::shared_ptr /*lastPresence*/) { - if (newPresence->getType() != Presence::Unavailable) return; - JID fullJID(newPresence->getFrom()); - std::map::iterator it = chatControllers_.find(fullJID); - if (it == chatControllers_.end()) return; - JID bareJID(fullJID.toBare()); - //It doesn't make sense to have two unbound dialogs. - if (chatControllers_.find(bareJID) != chatControllers_.end()) return; - rebindControllerJID(fullJID, bareJID); -} - -void ChatsManager::setAvatarManager(AvatarManager* avatarManager) { - avatarManager_ = avatarManager; -} - -// void ChatsManager::handleUIEvent(boost::shared_ptr rawEvent) { -// { -// boost::shared_ptr event = boost::dynamic_pointer_cast(rawEvent); -// if (event != NULL) { -// handleChatRequest(event->getContact()); -// return; -// } -// } -// { -// boost::shared_ptr event = boost::dynamic_pointer_cast(rawEvent); -// if (event != NULL) { -// handleJoinMUCRequest(event->getRoom(), event->getNick()); -// } -// } -// } - - -void ChatsManager::setServerDiscoInfo(boost::shared_ptr info) { - foreach (JIDChatControllerPair pair, chatControllers_) { - pair.second->setAvailableServerFeatures(info); - } - foreach (JIDMUCControllerPair pair, mucControllers_) { - pair.second->setAvailableServerFeatures(info); - } -} - -void ChatsManager::setEnabled(bool enabled) { - foreach (JIDChatControllerPair controllerPair, chatControllers_) { - //printf("Setting enabled on %d to %d\n", controllerPair.second, enabled); - controllerPair.second->setEnabled(enabled); - } - foreach (JIDMUCControllerPair controllerPair, mucControllers_) { - controllerPair.second->setEnabled(enabled); - } - -} - -void ChatsManager::handleChatRequest(const String &contact) { - ChatController* controller = getChatController(JID(contact)); - controller->showChatWindow(); - controller->activateChatWindow(); -} - -ChatController* ChatsManager::getChatController(const JID &contact) { - if (chatControllers_.find(contact) == chatControllers_.end()) { - //Need to look for an unboud window to bind first - JID bare(contact.toBare()); - if (chatControllers_.find(bare) != chatControllers_.end()) { - rebindControllerJID(bare, contact); - } else { - chatControllers_[contact] = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_); - chatControllers_[contact]->setAvailableServerFeatures(serverDiscoInfo_); - } - } - return chatControllers_[contact]; -} - -void ChatsManager::rebindControllerJID(const JID& from, const JID& to) { - chatControllers_[to] = chatControllers_[from]; - chatControllers_.erase(from); - chatControllers_[to]->setToJID(to); -} - -void ChatsManager::handleJoinMUCRequest(const JID &muc, const String &nick) { - std::map::iterator it = mucControllers_.find(muc); - if (it != mucControllers_.end()) { - //FIXME: What's correct behaviour here? - } else { - mucControllers_[muc] = new MUCController(jid_, muc, nick, stanzaChannel_, presenceSender_, iqRouter_, chatWindowFactory_, treeWidgetFactory_, presenceOracle_, avatarManager_); - mucControllers_[muc]->setAvailableServerFeatures(serverDiscoInfo_); - } - mucControllers_[muc]->activateChatWindow(); -} - -void ChatsManager::handleIncomingMessage(boost::shared_ptr message) { - JID jid = message->getFrom(); - boost::shared_ptr event(new MessageEvent(message)); - if (!event->isReadable()) { - return; - } - - // Try to deliver it to a MUC - if (message->getType() == Message::Groupchat || message->getType() == Message::Error) { - std::map::iterator i = mucControllers_.find(jid.toBare()); - if (i != mucControllers_.end()) { - i->second->handleIncomingMessage(event); - return; - } - else if (message->getType() == Message::Groupchat) { - //FIXME: Error handling - groupchat messages from an unknown muc. - return; - } - } - - //if not a mucroom - eventController_->handleIncomingEvent(event); - getChatController(jid)->handleIncomingMessage(event); -} - -bool ChatsManager::isMUC(const JID& jid) const { - return mucControllers_.find(jid.toBare()) != mucControllers_.end(); -} - - - -} diff --git a/Swift/Controllers/ChatsManager.h b/Swift/Controllers/ChatsManager.h deleted file mode 100644 index dd80d95..0000000 --- a/Swift/Controllers/ChatsManager.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include - -#include - -#include "Swiften/Base/String.h" -#include "Swiften/Elements/DiscoInfo.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/Elements/Presence.h" -#include "Swiften/JID/JID.h" -#include "Swiften/MUC/MUCRegistry.h" - -namespace Swift { - class EventController; - class ChatController; - class MUCController; - class ChatWindowFactory; - class TreeWidgetFactory; - class NickResolver; - class PresenceOracle; - class AvatarManager; - class StanzaChannel; - class IQRouter; - class PresenceSender; - class ChatsManager : public MUCRegistry { - public: - ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr serverDiscoInfo, PresenceSender* presenceSender); - ~ChatsManager(); - void setAvatarManager(AvatarManager* avatarManager); - void setEnabled(bool enabled); - void setServerDiscoInfo(boost::shared_ptr info); - void handleIncomingMessage(boost::shared_ptr message); - void handleChatRequest(const String& contact); - void handleJoinMUCRequest(const JID& muc, const String& nick); - private: - void rebindControllerJID(const JID& from, const JID& to); - void handlePresenceChange(boost::shared_ptr newPresence, boost::shared_ptr lastPresence); - ChatController* getChatController(const JID &contact); - virtual bool isMUC(const JID& muc) const; - - std::map mucControllers_; - std::map chatControllers_; - EventController* eventController_; - JID jid_; - StanzaChannel* stanzaChannel_; - IQRouter* iqRouter_;; - ChatWindowFactory* chatWindowFactory_; - TreeWidgetFactory* treeWidgetFactory_; - NickResolver* nickResolver_; - PresenceOracle* presenceOracle_; - AvatarManager* avatarManager_; - PresenceSender* presenceSender_; - boost::shared_ptr serverDiscoInfo_; - }; -} diff --git a/Swift/Controllers/MUCController.cpp b/Swift/Controllers/MUCController.cpp deleted file mode 100644 index c85fffa..0000000 --- a/Swift/Controllers/MUCController.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "Swift/Controllers/MUCController.h" - -#include - -#include "Swiften/Base/foreach.h" -#include "Swift/Controllers/UIInterfaces/ChatWindow.h" -#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" -#include "Swiften/Avatars/AvatarManager.h" -#include "Swiften/MUC/MUC.h" -#include "Swiften/Client/StanzaChannel.h" -#include "Swiften/Roster/Roster.h" -#include "Swiften/Roster/SetAvatar.h" -#include "Swiften/Roster/SetPresence.h" -#include "Swiften/Roster/TreeWidgetFactory.h" - -namespace Swift { - -/** - * The controller does not gain ownership of the stanzaChannel, nor the factory. - */ -MUCController::MUCController ( - const JID& self, - const JID &muc, - const String &nick, - StanzaChannel* stanzaChannel, - PresenceSender* presenceSender, - IQRouter* iqRouter, - ChatWindowFactory* chatWindowFactory, - TreeWidgetFactory *treeWidgetFactory, - PresenceOracle* presenceOracle, - AvatarManager* avatarManager) : - ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager), - muc_(new MUC(stanzaChannel, presenceSender, muc)), - nick_(nick), - treeWidgetFactory_(treeWidgetFactory) { - roster_ = new Roster(chatWindow_->getTreeWidget(), treeWidgetFactory_); - chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this)); - muc_->joinAs(nick); - muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1)); - muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1)); - muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3)); - chatWindow_->convertToMUC(); - chatWindow_->show(); - if (avatarManager_ != NULL) { - avatarManager_->onAvatarChanged.connect(boost::bind(&MUCController::handleAvatarChanged, this, _1, _2)); - } -} - -MUCController::~MUCController() { - delete muc_; - delete roster_; -} - -void MUCController::handleAvatarChanged(const JID& jid, const String&) { - String path = avatarManager_->getAvatarPath(jid).string(); - roster_->applyOnItems(SetAvatar(jid, path, JID::WithResource)); -} - -void MUCController::handleWindowClosed() { - muc_->part(); -} - -void MUCController::handleOccupantJoined(const MUCOccupant& occupant) { - JID jid(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick())); - roster_->addContact(jid, occupant.getNick(), "Occupants"); - if (avatarManager_ != NULL) { - handleAvatarChanged(jid, "dummy"); - } -} - -void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType, const String& /*reason*/) { - roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick())); -} - -void MUCController::handleOccupantPresenceChange(boost::shared_ptr presence) { - roster_->applyOnItems(SetPresence(presence, JID::WithResource)); -} - -bool MUCController::isIncomingMessageFromMe(boost::shared_ptr message) { - JID from = message->getFrom(); - return nick_ == from.getResource(); -} - -String MUCController::senderDisplayNameFromMessage(const JID& from) { - return from.getResource(); -} - -void MUCController::preSendMessageRequest(boost::shared_ptr message) { - message->setType(Swift::Message::Groupchat); -} - -} diff --git a/Swift/Controllers/MUCController.h b/Swift/Controllers/MUCController.h deleted file mode 100644 index aa5a274..0000000 --- a/Swift/Controllers/MUCController.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef SWIFTEN_MUCController_H -#define SWIFTEN_MUCController_H - -#include - -#include "Swiften/Base/String.h" -#include "Swift/Controllers/ChatControllerBase.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/Elements/DiscoInfo.h" -#include "Swiften/JID/JID.h" -#include "Swiften/MUC/MUC.h" -#include "Swiften/MUC/MUCOccupant.h" - -namespace Swift { - class StanzaChannel; - class IQRouter; - class ChatWindow; - class ChatWindowFactory; - class Roster; - class TreeWidgetFactory; - class AvatarManager; - - class MUCController : public ChatControllerBase { - public: - MUCController(const JID& self, const JID &muc, const String &nick, StanzaChannel* stanzaChannel, PresenceSender* presenceSender, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory *treeWidgetFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager); - ~MUCController(); - - protected: - void preSendMessageRequest(boost::shared_ptr message); - bool isIncomingMessageFromMe(boost::shared_ptr message); - String senderDisplayNameFromMessage(const JID& from); - - private: - void handleWindowClosed(); - void handleAvatarChanged(const JID& jid, const String&); - void handleOccupantJoined(const MUCOccupant& occupant); - void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const String& reason); - void handleOccupantPresenceChange(boost::shared_ptr presence); - - private: - MUC *muc_; - String nick_; - TreeWidgetFactory *treeWidgetFactory_; - Roster *roster_; - }; -} -#endif - diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 3fd0764..074369e 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -7,15 +7,15 @@ #include "Swiften/Application/Application.h" #include "Swiften/Application/ApplicationMessageDisplay.h" -#include "Swift/Controllers/ChatController.h" +#include "Swift/Controllers/Chat/ChatController.h" #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" -#include "Swift/Controllers/ChatsManager.h" +#include "Swift/Controllers/Chat/ChatsManager.h" #include "Swift/Controllers/EventController.h" #include "Swift/Controllers/UIInterfaces/LoginWindow.h" #include "Swift/Controllers/UIInterfaces/LoginWindowFactory.h" #include "Swift/Controllers/MainWindow.h" #include "Swift/Controllers/MainWindowFactory.h" -#include "Swift/Controllers/MUCController.h" +#include "Swift/Controllers/Chat/MUCController.h" #include "Swift/Controllers/NickResolver.h" #include "Swift/Controllers/ProfileSettingsProvider.h" #include "Swift/Controllers/RosterController.h" diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index 82bacaa..6bd742b 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -8,14 +8,14 @@ env["SWIFT_CONTROLLERS_FLAGS"] = { myenv = env.Clone() myenv.MergeFlags(env["BOOST_FLAGS"]) myenv.StaticLibrary("SwiftControllers", [ - "ChatController.cpp", - "ChatControllerBase.cpp", - "ChatsManager.cpp", + "Chat/ChatController.cpp", + "Chat/ChatControllerBase.cpp", + "Chat/ChatsManager.cpp", "MainController.cpp", "NickResolver.cpp", "RosterController.cpp", "XMPPRosterController.cpp", - "MUCController.cpp", + "Chat/MUCController.cpp", "EventController.cpp", "SoundEventController.cpp", "SystemTrayController.cpp", @@ -30,6 +30,6 @@ env.Append(UNITTEST_SOURCES = [ File("UnitTest/RosterControllerTest.cpp"), File("UnitTest/XMPPRosterControllerTest.cpp"), File("UnitTest/PreviousStatusStoreTest.cpp"), - File("UnitTest/ChatsManagerTest.cpp"), + File("Chat/UnitTest/ChatsManagerTest.cpp"), File("UnitTest/MockChatWindow.cpp"), ]) diff --git a/Swift/Controllers/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/UnitTest/ChatsManagerTest.cpp deleted file mode 100644 index 219b9b0..0000000 --- a/Swift/Controllers/UnitTest/ChatsManagerTest.cpp +++ /dev/null @@ -1,269 +0,0 @@ -#include -#include -#include "3rdParty/hippomocks.h" - -#include "Swift/Controllers/ChatsManager.h" - -#include "Swift/Controllers/UIInterfaces/ChatWindow.h" -#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" -#include "Swiften/Roster/TreeWidgetFactory.h" -#include "Swiften/Client/Client.h" -#include "Swift/Controllers/ChatController.h" -#include "Swift/Controllers/EventController.h" -#include "Swift/Controllers/MUCController.h" -#include "Swiften/Presence/PresenceSender.h" -#include "Swiften/Avatars/UnitTest/MockAvatarManager.h" -#include "Swift/Controllers/NickResolver.h" -#include "Swiften/Roster/XMPPRoster.h" -#include "Swift/Controllers/UnitTest/MockChatWindow.h" -#include "Swiften/Client/DummyStanzaChannel.h" -#include "Swiften/Queries/DummyIQChannel.h" -#include "Swiften/Presence/PresenceOracle.h" - - -using namespace Swift; - -class ChatsManagerTest : public CppUnit::TestFixture -{ - CPPUNIT_TEST_SUITE(ChatsManagerTest); - CPPUNIT_TEST(testFirstOpenWindowIncoming); - CPPUNIT_TEST(testSecondOpenWindowIncoming); - CPPUNIT_TEST(testFirstOpenWindowOutgoing); - CPPUNIT_TEST(testFirstOpenWindowBareToFull); - CPPUNIT_TEST(testSecondWindow); - CPPUNIT_TEST(testUnbindRebind); - CPPUNIT_TEST(testNoDuplicateUnbind); - CPPUNIT_TEST_SUITE_END(); - -public: - ChatsManagerTest() {}; - - void setUp() { - mocks_ = new MockRepository(); - jid_ = JID("test@test.com/resource"); - stanzaChannel_ = new DummyStanzaChannel(); - iqChannel_ = new DummyIQChannel(); - iqRouter_ = new IQRouter(iqChannel_); - eventController_ = new EventController(); - chatWindowFactory_ = mocks_->InterfaceMock(); - treeWidgetFactory_ = NULL; - xmppRoster_ = boost::shared_ptr(new XMPPRoster()); - nickResolver_ = new NickResolver(xmppRoster_); - presenceOracle_ = new PresenceOracle(stanzaChannel_); - serverDiscoInfo_ = boost::shared_ptr(new DiscoInfo()); - presenceSender_ = NULL; - manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, treeWidgetFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_); - avatarManager_ = new MockAvatarManager(); - manager_->setAvatarManager(avatarManager_); - }; - - void tearDown() { - delete manager_; - delete presenceSender_; - delete avatarManager_; - delete presenceOracle_; - delete nickResolver_; - delete treeWidgetFactory_; - delete stanzaChannel_; - delete iqChannel_; - delete iqRouter_; - delete mocks_; - } - - void testFirstOpenWindowIncoming() { - JID messageJID("testling@test.com/resource1"); - - MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID).Return(window); - - boost::shared_ptr message(new Message()); - message->setFrom(messageJID); - String body("This is a legible message. >HEH@)oeueu"); - message->setBody(body); - manager_->handleIncomingMessage(message); - CPPUNIT_ASSERT_EQUAL(body, window->lastMessageBody_); - } - - void testSecondOpenWindowIncoming() { - JID messageJID1("testling@test.com/resource1"); - - MockChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1).Return(window1); - - boost::shared_ptr message1(new Message()); - message1->setFrom(messageJID1); - String body1("This is a legible message. >HEH@)oeueu"); - message1->setBody(body1); - manager_->handleIncomingMessage(message1); - CPPUNIT_ASSERT_EQUAL(body1, window1->lastMessageBody_); - - JID messageJID2("testling@test.com/resource2"); - - MockChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID2).Return(window2); - - boost::shared_ptr message2(new Message()); - message2->setFrom(messageJID2); - String body2("This is a legible message. .cmaulm.chul"); - message2->setBody(body2); - manager_->handleIncomingMessage(message2); - CPPUNIT_ASSERT_EQUAL(body2, window2->lastMessageBody_); - } - - void testFirstOpenWindowOutgoing() { - String messageJIDString("testling@test.com"); - - ChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString)).Return(window); - - manager_->handleChatRequest(messageJIDString); - } - - - void testFirstOpenWindowBareToFull() { - String bareJIDString("testling@test.com"); - String fullJIDString("testling@test.com/resource1"); - - MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString)).Return(window); - manager_->handleChatRequest(bareJIDString); - - boost::shared_ptr message(new Message()); - message->setFrom(JID(fullJIDString)); - String body("This is a legible message. mjuga3089gm8G(*>M)@*("); - message->setBody(body); - manager_->handleIncomingMessage(message); - CPPUNIT_ASSERT_EQUAL(body, window->lastMessageBody_); - } - - void testSecondWindow() { - String messageJIDString1("testling1@test.com"); - ChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString1)).Return(window1); - manager_->handleChatRequest(messageJIDString1); - - String messageJIDString2("testling2@test.com"); - ChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString2)).Return(window2); - - manager_->handleChatRequest(messageJIDString2); - } - - /** Complete cycle. - Create unbound window. - Bind it. - Unbind it. - Rebind it. - */ - void testUnbindRebind() { - String bareJIDString("testling@test.com"); - String fullJIDString1("testling@test.com/resource1"); - String fullJIDString2("testling@test.com/resource2"); - - MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString)).Return(window); - manager_->handleChatRequest(bareJIDString); - - boost::shared_ptr message1(new Message()); - message1->setFrom(JID(fullJIDString1)); - String messageBody1("This is a legible message."); - message1->setBody(messageBody1); - manager_->handleIncomingMessage(message1); - CPPUNIT_ASSERT_EQUAL(messageBody1, window->lastMessageBody_); - - boost::shared_ptr jid1Online(new Presence()); - jid1Online->setFrom(JID(fullJIDString1)); - boost::shared_ptr jid1Offline(new Presence()); - jid1Offline->setFrom(JID(fullJIDString1)); - jid1Offline->setType(Presence::Unavailable); - presenceOracle_->onPresenceChange(jid1Offline, jid1Online); - - boost::shared_ptr message2(new Message()); - message2->setFrom(JID(fullJIDString2)); - String messageBody2("This is another legible message."); - message2->setBody(messageBody2); - manager_->handleIncomingMessage(message2); - CPPUNIT_ASSERT_EQUAL(messageBody2, window->lastMessageBody_); - } - - /** - Test that a second window isn't unbound where there's already an unbound one. - Bind 1 - Bind 2 - Unbind 1 - Unbind 2 (but it doesn't) - Sent to bound 2 - Rebind 1 - */ - void testNoDuplicateUnbind() { - JID messageJID1("testling@test.com/resource1"); - - MockChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1).Return(window1); - - boost::shared_ptr message1(new Message()); - message1->setFrom(messageJID1); - message1->setBody("This is a legible message1."); - manager_->handleIncomingMessage(message1); - - JID messageJID2("testling@test.com/resource2"); - - MockChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID2).Return(window2); - - boost::shared_ptr message2(new Message()); - message2->setFrom(messageJID2); - message2->setBody("This is a legible message2."); - manager_->handleIncomingMessage(message2); - - boost::shared_ptr jid1Online(new Presence()); - jid1Online->setFrom(JID(messageJID1)); - boost::shared_ptr jid1Offline(new Presence()); - jid1Offline->setFrom(JID(messageJID1)); - jid1Offline->setType(Presence::Unavailable); - presenceOracle_->onPresenceChange(jid1Offline, jid1Online); - - boost::shared_ptr jid2Online(new Presence()); - jid2Online->setFrom(JID(messageJID2)); - boost::shared_ptr jid2Offline(new Presence()); - jid2Offline->setFrom(JID(messageJID2)); - jid2Offline->setType(Presence::Unavailable); - presenceOracle_->onPresenceChange(jid2Offline, jid2Online); - - JID messageJID3("testling@test.com/resource3"); - - boost::shared_ptr message3(new Message()); - message3->setFrom(messageJID3); - String body3("This is a legible message3."); - message3->setBody(body3); - manager_->handleIncomingMessage(message3); - CPPUNIT_ASSERT_EQUAL(body3, window1->lastMessageBody_); - - boost::shared_ptr message2b(new Message()); - message2b->setFrom(messageJID2); - String body2b("This is a legible message2b."); - message2b->setBody(body2b); - manager_->handleIncomingMessage(message2b); - CPPUNIT_ASSERT_EQUAL(body2b, window2->lastMessageBody_); - } - -private: - JID jid_; - ChatsManager* manager_; - StanzaChannel* stanzaChannel_; - IQChannel* iqChannel_; - IQRouter* iqRouter_; - EventController* eventController_; - ChatWindowFactory* chatWindowFactory_; - TreeWidgetFactory* treeWidgetFactory_; - NickResolver* nickResolver_; - PresenceOracle* presenceOracle_; - AvatarManager* avatarManager_; - boost::shared_ptr serverDiscoInfo_; - boost::shared_ptr xmppRoster_; - PresenceSender* presenceSender_; - MockRepository* mocks_; -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); - diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index 5802e48..be2d291 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -18,7 +18,6 @@ #include "Swiften/Base/Platform.h" #include "Swiften/Elements/Presence.h" #include "Swiften/Client/Client.h" -#include "Swift/Controllers/ChatController.h" #include "Swift/Controllers/MainController.h" #include "Swift/QtUI/BuildVersion.h" #include "SwifTools/AutoUpdater/AutoUpdater.h" -- cgit v0.10.2-6-g49f6