diff options
Diffstat (limited to 'Swift/Controllers/Chat/ChatsManager.cpp')
-rw-r--r-- | Swift/Controllers/Chat/ChatsManager.cpp | 298 |
1 files changed, 261 insertions, 37 deletions
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index 94d4b9a..47a65a8 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -7,31 +7,40 @@ #include "Swift/Controllers/Chat/ChatsManager.h" #include <boost/bind.hpp> - -#include "Swift/Controllers/Chat/ChatController.h" -#include "Swift/Controllers/Chat/MUCSearchController.h" -#include "Swift/Controllers/XMPPEvents/EventController.h" -#include "Swift/Controllers/Chat/MUCController.h" -#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" -#include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" -#include "Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h" -#include "Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h" -#include "Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h" -#include "Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h" -#include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h" -#include "Swift/Controllers/UIInterfaces/JoinMUCWindow.h" -#include "Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h" -#include "Swiften/Presence/PresenceSender.h" -#include "Swiften/Client/NickResolver.h" -#include "Swiften/MUC/MUCManager.h" -#include "Swiften/Elements/ChatState.h" -#include "Swiften/MUC/MUCBookmarkManager.h" +#include <boost/algorithm/string.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swift/Controllers/Chat/ChatController.h> +#include <Swift/Controllers/Chat/ChatControllerBase.h> +#include <Swift/Controllers/Chat/MUCSearchController.h> +#include <Swift/Controllers/XMPPEvents/EventController.h> +#include <Swift/Controllers/Chat/MUCController.h> +#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h> +#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h> +#include <Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h> +#include <Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h> +#include <Swift/Controllers/UIInterfaces/ChatListWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/JoinMUCWindow.h> +#include <Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h> +#include <Swiften/Presence/PresenceSender.h> +#include <Swiften/Client/NickResolver.h> +#include <Swiften/MUC/MUCManager.h> +#include <Swiften/Elements/ChatState.h> +#include <Swiften/MUC/MUCBookmarkManager.h> +#include <Swift/Controllers/FileTransfer/FileTransferController.h> +#include <Swift/Controllers/FileTransfer/FileTransferOverview.h> +#include <Swift/Controllers/ProfileSettingsProvider.h> +#include <Swiften/Avatars/AvatarManager.h> namespace Swift { typedef std::pair<JID, ChatController*> JIDChatControllerPair; typedef std::pair<JID, MUCController*> JIDMUCControllerPair; +#define RECENT_CHATS "recent_chats" + ChatsManager::ChatsManager( JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, @@ -49,13 +58,15 @@ ChatsManager::ChatsManager( EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, - SettingsProvider* settings) : + ProfileSettingsProvider* settings, + FileTransferOverview* ftOverview) : jid_(jid), joinMUCWindowFactory_(joinMUCWindowFactory), useDelayForLatency_(useDelayForLatency), mucRegistry_(mucRegistry), entityCapsProvider_(entityCapsProvider), - mucManager(mucManager) { + mucManager(mucManager), + ftOverview_(ftOverview) { timerFactory_ = timerFactory; eventController_ = eventController; stanzaChannel_ = stanzaChannel; @@ -68,13 +79,21 @@ ChatsManager::ChatsManager( presenceSender_ = presenceSender; uiEventStream_ = uiEventStream; mucBookmarkManager_ = NULL; + profileSettings_ = settings; presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1)); uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&ChatsManager::handleUIEvent, this, _1)); + chatListWindow_ = chatListWindowFactory->createChatListWindow(uiEventStream_); + chatListWindow_->onMUCBookmarkActivated.connect(boost::bind(&ChatsManager::handleMUCBookmarkActivated, this, _1)); + chatListWindow_->onRecentActivated.connect(boost::bind(&ChatsManager::handleRecentActivated, this, _1)); + chatListWindow_->onClearRecentsRequested.connect(boost::bind(&ChatsManager::handleClearRecentsRequested, this)); + joinMUCWindow_ = NULL; mucSearchController_ = new MUCSearchController(jid_, mucSearchWindowFactory, iqRouter, settings); mucSearchController_->onMUCSelected.connect(boost::bind(&ChatsManager::handleMUCSelectedAfterSearch, this, _1)); + ftOverview_->onNewFileTransferController.connect(boost::bind(&ChatsManager::handleNewFileTransferController, this, _1)); setupBookmarks(); + loadRecents(); } ChatsManager::~ChatsManager() { @@ -89,6 +108,72 @@ ChatsManager::~ChatsManager() { delete mucSearchController_; } +void ChatsManager::saveRecents() { + std::string recents; + int i = 1; + foreach (ChatListWindow::Chat chat, recentChats_) { + std::vector<std::string> activity; + boost::split(activity, chat.activity, boost::is_any_of("\t\n")); + if (activity.empty()) { + /* Work around Boost bug https://svn.boost.org/trac/boost/ticket/4751 */ + activity.push_back(""); + } + std::string recent = chat.jid.toString() + "\t" + activity[0] + "\t" + (chat.isMUC ? "true" : "false") + "\t" + chat.nick; + recents += recent + "\n"; + if (i++ > 25) { + break; + } + } + profileSettings_->storeString(RECENT_CHATS, recents); +} + +void ChatsManager::handleClearRecentsRequested() { + recentChats_.clear(); + saveRecents(); + handleUnreadCountChanged(NULL); +} + +void ChatsManager::loadRecents() { + std::string recentsString(profileSettings_->getStringSetting(RECENT_CHATS)); + std::vector<std::string> recents; + boost::split(recents, recentsString, boost::is_any_of("\n")); + int i = 0; + foreach (std::string recentString, recents) { + if (i++ > 30) { + break; + } + std::vector<std::string> recent; + boost::split(recent, recentString, boost::is_any_of("\t")); + if (recent.size() < 4) { + continue; + } + JID jid(recent[0]); + if (!jid.isValid()) { + continue; + } + std::string activity(recent[1]); + bool isMUC = recent[2] == "true"; + std::string nick(recent[3]); + StatusShow::Type type = StatusShow::None; + boost::filesystem::path path; + if (isMUC) { + if (mucControllers_.find(jid.toBare()) != mucControllers_.end()) { + type = StatusShow::Online; + } + } else { + if (avatarManager_) { + path = avatarManager_->getAvatarPath(jid); + } + Presence::ref presence = presenceOracle_->getHighestPriorityPresence(jid.toBare()); + type = presence ? presence->getShow() : StatusShow::None; + } + + ChatListWindow::Chat chat(jid, nickResolver_->jidToNick(jid), activity, 0, type, path, isMUC, nick); + prependRecent(chat); + } + handleUnreadCountChanged(NULL); +} + void ChatsManager::setupBookmarks() { if (!mucBookmarkManager_) { mucBookmarkManager_ = new MUCBookmarkManager(iqRouter_); @@ -98,7 +183,7 @@ void ChatsManager::setupBookmarks() { if (chatListWindow_) { chatListWindow_->setBookmarksEnabled(false); - chatListWindow_->clear(); + chatListWindow_->clearBookmarks(); } } } @@ -122,10 +207,90 @@ void ChatsManager::handleMUCBookmarkRemoved(const MUCBookmark& bookmark) { chatListWindow_->removeMUCBookmark(bookmark); } +ChatListWindow::Chat ChatsManager::createChatListChatItem(const JID& jid, const std::string& activity) { + int unreadCount = 0; + if (mucRegistry_->isMUC(jid)) { + MUCController* controller = mucControllers_[jid.toBare()]; + StatusShow::Type type = StatusShow::None; + std::string nick = ""; + if (controller) { + unreadCount = controller->getUnreadCount(); + if (controller->isJoined()) { + type = StatusShow::Online; + } + nick = controller->getNick(); + } + return ChatListWindow::Chat(jid, jid.toString(), activity, unreadCount, type, boost::filesystem::path(), true, nick); + + } else { + ChatController* controller = getChatControllerIfExists(jid, false); + if (controller) { + unreadCount = controller->getUnreadCount(); + } + JID bareishJID = mucRegistry_->isMUC(jid.toBare()) ? jid : jid.toBare(); + Presence::ref presence = presenceOracle_->getHighestPriorityPresence(bareishJID); + StatusShow::Type type = presence ? presence->getShow() : StatusShow::None; + boost::filesystem::path avatarPath = avatarManager_ ? avatarManager_->getAvatarPath(bareishJID) : boost::filesystem::path(); + return ChatListWindow::Chat(bareishJID, nickResolver_->jidToNick(bareishJID), activity, unreadCount, type, avatarPath, false); + } +} + +void ChatsManager::handleChatActivity(const JID& jid, const std::string& activity, bool isMUC) { + if (mucRegistry_->isMUC(jid.toBare()) && !isMUC) { + /* Don't include PMs in MUC rooms.*/ + return; + } + ChatListWindow::Chat chat = createChatListChatItem(jid, activity); + /* FIXME: handle nick changes */ + appendRecent(chat); + handleUnreadCountChanged(NULL); + saveRecents(); +} + +void ChatsManager::handleUnreadCountChanged(ChatControllerBase* controller) { + int unreadTotal = 0; + bool controllerIsMUC = dynamic_cast<MUCController*>(controller); + bool isPM = controller && !controllerIsMUC && mucRegistry_->isMUC(controller->getToJID().toBare()); + foreach (ChatListWindow::Chat& chatItem, recentChats_) { + bool match = false; + if (controller) { + /* Matching MUC item */ + match |= chatItem.isMUC == controllerIsMUC && chatItem.jid.toBare() == controller->getToJID().toBare(); + /* Matching PM */ + match |= isPM && chatItem.jid == controller->getToJID(); + /* Matching non-PM */ + match |= !isPM && !controllerIsMUC && chatItem.jid.toBare() == controller->getToJID().toBare(); + } + if (match) { + chatItem.setUnreadCount(controller->getUnreadCount()); + } + unreadTotal += chatItem.unreadCount; + } + chatListWindow_->setRecents(recentChats_); + chatListWindow_->setUnreadCount(unreadTotal); +} + +void ChatsManager::appendRecent(const ChatListWindow::Chat& chat) { + recentChats_.erase(std::remove(recentChats_.begin(), recentChats_.end(), chat), recentChats_.end()); + recentChats_.push_front(chat); +} + +void ChatsManager::prependRecent(const ChatListWindow::Chat& chat) { + recentChats_.erase(std::remove(recentChats_.begin(), recentChats_.end(), chat), recentChats_.end()); + recentChats_.push_back(chat); +} + void ChatsManager::handleUserLeftMUC(MUCController* mucController) { std::map<JID, MUCController*>::iterator it; - for (it = mucControllers_.begin(); it != mucControllers_.end(); it++) { + for (it = mucControllers_.begin(); it != mucControllers_.end(); ++it) { if ((*it).second == mucController) { + foreach (ChatListWindow::Chat& chat, recentChats_) { + if (chat.isMUC && chat.jid == (*it).first) { + chat.statusType = StatusShow::None; + chatListWindow_->setRecents(recentChats_); + break; + } + } mucControllers_.erase(it); delete mucController; return; @@ -155,15 +320,15 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) { mucBookmarkManager_->replaceBookmark(editMUCBookmarkEvent->getOldBookmark(), editMUCBookmarkEvent->getNewBookmark()); } else if (JoinMUCUIEvent::ref joinEvent = boost::dynamic_pointer_cast<JoinMUCUIEvent>(event)) { - handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getNick(), false); + handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getNick(), joinEvent->getShouldJoinAutomatically()); + mucControllers_[joinEvent->getJID()]->activateChatWindow(); } - else if (boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) { + else if (boost::shared_ptr<RequestJoinMUCUIEvent> joinEvent = boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) { if (!joinMUCWindow_) { - joinMUCWindow_ = joinMUCWindowFactory_->createJoinMUCWindow(); - joinMUCWindow_->onJoinMUC.connect(boost::bind(&ChatsManager::handleJoinMUCRequest, this, _1, _2, _3)); + joinMUCWindow_ = joinMUCWindowFactory_->createJoinMUCWindow(uiEventStream_); joinMUCWindow_->onSearchMUC.connect(boost::bind(&ChatsManager::handleSearchMUCRequest, this)); } - joinMUCWindow_->setMUC(""); + joinMUCWindow_->setMUC(joinEvent->getRoom()); joinMUCWindow_->setNick(nickResolver_->jidToNick(jid_)); joinMUCWindow_->show(); } @@ -174,6 +339,16 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) { */ void ChatsManager::handlePresenceChange(boost::shared_ptr<Presence> newPresence) { if (mucRegistry_->isMUC(newPresence->getFrom().toBare())) return; + + foreach (ChatListWindow::Chat& chat, recentChats_) { + if (newPresence->getFrom().toBare() == chat.jid.toBare() && !chat.isMUC) { + Presence::ref presence = presenceOracle_->getHighestPriorityPresence(chat.jid.toBare()); + chat.setStatusType(presence ? presence->getShow() : StatusShow::None); + chatListWindow_->setRecents(recentChats_); + break; + } + } + //if (newPresence->getType() != Presence::Unavailable) return; JID fullJID(newPresence->getFrom()); std::map<JID, ChatController*>::iterator it = chatControllers_.find(fullJID); @@ -185,7 +360,25 @@ void ChatsManager::handlePresenceChange(boost::shared_ptr<Presence> newPresence) } void ChatsManager::setAvatarManager(AvatarManager* avatarManager) { + if (avatarManager_) { + avatarManager_->onAvatarChanged.disconnect(boost::bind(&ChatsManager::handleAvatarChanged, this, _1)); + } avatarManager_ = avatarManager; + foreach (ChatListWindow::Chat& chat, recentChats_) { + if (!chat.isMUC) { + chat.setAvatarPath(avatarManager_->getAvatarPath(chat.jid)); + } + } + avatarManager_->onAvatarChanged.connect(boost::bind(&ChatsManager::handleAvatarChanged, this, _1)); +} + +void ChatsManager::handleAvatarChanged(const JID& jid) { + foreach (ChatListWindow::Chat& chat, recentChats_) { + if (!chat.isMUC && jid.toBare() == chat.jid.toBare()) { + chat.setAvatarPath(avatarManager_->getAvatarPath(jid)); + break; + } + } } void ChatsManager::setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info) { @@ -244,6 +437,8 @@ ChatController* ChatsManager::createNewChatController(const JID& contact) { ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_); chatControllers_[contact] = controller; controller->setAvailableServerFeatures(serverDiscoInfo_); + controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1, false)); + controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller)); return controller; } @@ -252,7 +447,7 @@ ChatController* ChatsManager::getChatControllerOrCreate(const JID &contact) { return controller ? controller : createNewChatController(contact); } -ChatController* ChatsManager::getChatControllerIfExists(const JID &contact) { +ChatController* ChatsManager::getChatControllerIfExists(const JID &contact, bool rebindIfNeeded) { if (chatControllers_.find(contact) == chatControllers_.end()) { if (mucRegistry_->isMUC(contact.toBare())) { return NULL; @@ -264,8 +459,13 @@ ChatController* ChatsManager::getChatControllerIfExists(const JID &contact) { } else { foreach (JIDChatControllerPair pair, chatControllers_) { if (pair.first.toBare() == contact.toBare()) { - rebindControllerJID(pair.first, contact); - return chatControllers_[contact]; + if (rebindIfNeeded) { + rebindControllerJID(pair.first, contact); + return chatControllers_[contact]; + } else { + return pair.second; + } + } } return NULL; @@ -280,8 +480,8 @@ void ChatsManager::rebindControllerJID(const JID& from, const JID& to) { chatControllers_[to]->setToJID(to); } -void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<std::string>& nickMaybe, bool autoJoin) { - if (autoJoin) { +void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<std::string>& nickMaybe, bool addAutoJoin) { + if (addAutoJoin) { MUCBookmark bookmark(mucJID, mucJID.getNode()); bookmark.setAutojoin(true); if (nickMaybe) { @@ -296,12 +496,17 @@ void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional } else { std::string nick = nickMaybe ? nickMaybe.get() : jid_.getNode(); MUC::ref muc = mucManager->createMUC(mucJID); - MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_); + MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_); mucControllers_[mucJID] = controller; controller->setAvailableServerFeatures(serverDiscoInfo_); controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller)); + controller->onUserJoined.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), "", true)); + controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), _1, true)); + controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller)); + handleChatActivity(mucJID.toBare(), "", true); } - mucControllers_[mucJID]->activateChatWindow(); + + mucControllers_[mucJID]->showChatWindow(); } void ChatsManager::handleSearchMUCRequest() { @@ -311,7 +516,7 @@ void ChatsManager::handleSearchMUCRequest() { void ChatsManager::handleIncomingMessage(boost::shared_ptr<Message> message) { JID jid = message->getFrom(); boost::shared_ptr<MessageEvent> event(new MessageEvent(message)); - if (!event->isReadable() && !message->getPayload<ChatState>() && message->getSubject().empty()) { + if (!event->isReadable() && !message->getPayload<ChatState>() && !message->hasSubject()) { return; } @@ -338,5 +543,24 @@ void ChatsManager::handleMUCSelectedAfterSearch(const JID& muc) { } } +void ChatsManager::handleMUCBookmarkActivated(const MUCBookmark& mucBookmark) { + uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(mucBookmark.getRoom(), mucBookmark.getNick())); +} + +void ChatsManager::handleNewFileTransferController(FileTransferController* ftc) { + ChatController* chatController = getChatControllerOrCreate(ftc->getOtherParty()); + chatController->handleNewFileTransferController(ftc); + chatController->activateChatWindow(); +} + +void ChatsManager::handleRecentActivated(const ChatListWindow::Chat& chat) { + if (chat.isMUC) { + uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(chat.jid, chat.nick)); + } + else { + uiEventStream_->send(boost::make_shared<RequestChatUIEvent>(chat.jid)); + } +} + } |