diff options
Diffstat (limited to 'Swift/Controllers')
23 files changed, 737 insertions, 21 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index 2fa4559..f40f704 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -37,8 +37,8 @@ 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, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings) - : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider), eventStream_(eventStream), userWantsReceipts_(userWantsReceipts), settings_(settings) { +ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry) + : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry), eventStream_(eventStream), userWantsReceipts_(userWantsReceipts), settings_(settings) { isInMUC_ = isInMUC; lastWasPresence_ = false; chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider); @@ -353,4 +353,18 @@ boost::optional<boost::posix_time::ptime> ChatController::getMessageTimestamp(bo return message->getTimestamp(); } +void ChatController::logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool /* isIncoming */) { + HistoryMessage::Type type; + if (mucRegistry_->isMUC(fromJID.toBare()) || mucRegistry_->isMUC(toJID.toBare())) { + type = HistoryMessage::PrivateMessage; + } + else { + type = HistoryMessage::Chat; + } + + if (historyController_) { + historyController_->addMessage(message, fromJID, toJID, type, timeStamp); + } +} + } diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index 7043231..a873ae9 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -21,10 +21,11 @@ namespace Swift { class EntityCapsProvider; class FileTransferController; class SettingsProvider; + class HistoryController; class ChatController : public ChatControllerBase { public: - ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings); + ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry); virtual ~ChatController(); virtual void setToJID(const JID& jid); virtual void setOnline(bool online); @@ -34,6 +35,7 @@ namespace Swift { protected: void cancelReplaces(); JID getBaseJID(); + void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming); private: void handlePresenceChange(boost::shared_ptr<Presence> newPresence); diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index 2bfff4f..b5fe0c0 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -33,7 +33,7 @@ namespace Swift { -ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory), entityCapsProvider_(entityCapsProvider) { +ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory), entityCapsProvider_(entityCapsProvider), historyController_(historyController), mucRegistry_(mucRegistry) { chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream); chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2)); @@ -133,8 +133,9 @@ void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool } } preSendMessageRequest(message); + + boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); if (useDelayForLatency_) { - boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); message->addPayload(boost::make_shared<Delay>(now, selfJID_)); } if (isCorrectionMessage) { @@ -144,6 +145,10 @@ void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool stanzaChannel_->sendMessage(message); postSendMessage(message->getBody(), boost::dynamic_pointer_cast<Stanza>(message)); onActivity(message->getBody()); + +#ifdef SWIFT_EXPERIMENTAL_HISTORY + logMessage(body, selfJID_, toJID_, now, false); +#endif } void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog> catalog, ErrorPayload::ref error) { @@ -251,6 +256,8 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m else { lastMessagesUIID_[from] = addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp); } + + logMessage(body, from, selfJID_, timeStamp, true); } chatWindow_->show(); chatWindow_->setUnreadMessageCount(unreadMessages_.size()); diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index 8aed069..b26af02 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -27,6 +27,8 @@ #include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Queries/IQRouter.h" #include "Swiften/Base/IDGenerator.h" +#include <Swift/Controllers/HistoryController.h> +#include <Swiften/MUC/MUCRegistry.h> namespace Swift { class IQRouter; @@ -58,7 +60,7 @@ namespace Swift { void handleCapsChanged(const JID& jid); protected: - ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider); + ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry); /** * Pass the Message appended, and the stanza used to send it. @@ -78,6 +80,7 @@ namespace Swift { virtual void cancelReplaces() = 0; /** JID any iq for account should go to - bare except for PMs */ virtual JID getBaseJID(); + virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) = 0; private: IDGenerator idGenerator_; @@ -111,5 +114,7 @@ namespace Swift { TimerFactory* timerFactory_; EntityCapsProvider* entityCapsProvider_; SecurityLabelsCatalog::Item lastLabel_; + HistoryController* historyController_; + MUCRegistry* mucRegistry_; }; } diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index 4fc3752..6b51df6 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -71,7 +71,8 @@ ChatsManager::ChatsManager( FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, - SettingsProvider* settings) : + SettingsProvider* settings, + HistoryController* historyController) : jid_(jid), joinMUCWindowFactory_(joinMUCWindowFactory), useDelayForLatency_(useDelayForLatency), @@ -81,7 +82,8 @@ ChatsManager::ChatsManager( ftOverview_(ftOverview), roster_(roster), eagleMode_(eagleMode), - settings_(settings) { + settings_(settings), + historyController_(historyController) { timerFactory_ = timerFactory; eventController_ = eventController; stanzaChannel_ = stanzaChannel; @@ -512,7 +514,7 @@ ChatController* ChatsManager::getChatControllerOrFindAnother(const JID &contact) ChatController* ChatsManager::createNewChatController(const JID& contact) { assert(chatControllers_.find(contact) == chatControllers_.end()); - ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_, userWantsReceipts_, settings_); + ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_, userWantsReceipts_, settings_, historyController_, mucRegistry_); chatControllers_[contact] = controller; controller->setAvailableServerFeatures(serverDiscoInfo_); controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1, false)); @@ -585,7 +587,7 @@ void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional if (createAsReservedIfNew) { muc->setCreateAsReservedIfNew(); } - MUCController* controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_, roster_); + MUCController* controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_, roster_, historyController_, mucRegistry_); mucControllers_[mucJID] = controller; controller->setAvailableServerFeatures(serverDiscoInfo_); controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller)); diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h index a8c69c4..94efde1 100644 --- a/Swift/Controllers/Chat/ChatsManager.h +++ b/Swift/Controllers/Chat/ChatsManager.h @@ -47,10 +47,11 @@ namespace Swift { class FileTransferController; class XMPPRoster; class SettingsProvider; + class HistoryController; class ChatsManager { public: - ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings); + ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings, HistoryController* historyController_); virtual ~ChatsManager(); void setAvatarManager(AvatarManager* avatarManager); void setOnline(bool enabled); @@ -129,5 +130,6 @@ namespace Swift { bool eagleMode_; bool userWantsReceipts_; SettingsProvider* settings_; + HistoryController* historyController_; }; } diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index 0469cc6..fcd1f04 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -59,8 +59,10 @@ MUCController::MUCController ( TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, - XMPPRoster* roster) : - ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider), muc_(muc), nick_(nick), desiredNick_(nick), password_(password) { + XMPPRoster* roster, + HistoryController* historyController, + MUCRegistry* mucRegistry) : + ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry), muc_(muc), nick_(nick), desiredNick_(nick), password_(password) { parting_ = true; joined_ = false; lastWasPresence_ = false; @@ -193,11 +195,14 @@ void MUCController::rejoin() { muc_->setPassword(*password_); } //FIXME: check for received activity - if (lastActivity_ == boost::posix_time::not_a_date_time) { - muc_->joinAs(nick_); - } else { - muc_->joinWithContextSince(nick_, lastActivity_); +#ifdef SWIFT_EXPERIMENTAL_HISTORY + if (lastActivity_ == boost::posix_time::not_a_date_time && historyController_) { + lastActivity_ = historyController_->getLastTimeStampFromMUC(selfJID_, toJID_); } + muc_->joinWithContextSince(nick_, lastActivity_); +#else + muc_->joinAs(nick_); +#endif } } @@ -273,6 +278,11 @@ void MUCController::handleJoinComplete(const std::string& nick) { std::string joinMessage = str(format(QT_TRANSLATE_NOOP("", "You have entered room %1% as %2%.")) % toJID_.toString() % nick); nick_ = nick; chatWindow_->addSystemMessage(joinMessage); + +#ifdef SWIFT_EXPERIMENTAL_HISTORY + addRecentLogs(); +#endif + clearPresenceQueue(); shouldJoinOnReconnect_ = true; setEnabled(true); @@ -430,6 +440,7 @@ void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> mes } if (!doneGettingHistory_) { + checkDuplicates(message); messageEvent->conclude(); } } @@ -779,4 +790,51 @@ void MUCController::handleAffiliationListReceived(MUCOccupant::Affiliation affil chatWindow_->setAffiliations(affiliation, jids); } +void MUCController::logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) { + // log only incoming messages + if (isIncoming && historyController_) { + historyController_->addMessage(message, fromJID, toJID, HistoryMessage::Groupchat, timeStamp); + } +} + +void MUCController::addRecentLogs() { + if (!historyController_) { + return; + } + + joinContext_ = historyController_->getMUCContext(selfJID_, toJID_, lastActivity_); + + foreach (const HistoryMessage& message, joinContext_) { + bool senderIsSelf = nick_ == message.getFromJID().getResource(); + + // the chatWindow uses utc timestamps + addMessage(message.getMessage(), senderDisplayNameFromMessage(message.getFromJID()), senderIsSelf, boost::shared_ptr<SecurityLabel>(new SecurityLabel()), std::string(avatarManager_->getAvatarPath(message.getFromJID()).string()), message.getTime() - boost::posix_time::hours(message.getOffset())); + } +} + +void MUCController::checkDuplicates(boost::shared_ptr<Message> newMessage) { + std::string body = newMessage->getBody(); + JID jid = newMessage->getFrom(); + boost::optional<boost::posix_time::ptime> time = newMessage->getTimestamp(); + + reverse_foreach (const HistoryMessage& message, joinContext_) { + boost::posix_time::ptime messageTime = message.getTime() - boost::posix_time::hours(message.getOffset()); + if (time && time < messageTime) { + break; + } + if (time && time != messageTime) { + continue; + } + if (message.getFromJID() != jid) { + continue; + } + if (message.getMessage() != body) { + continue; + } + + // Mark the message as unreadable + newMessage->setBody(""); + } +} + } diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index 6bf056b..63893d0 100644 --- a/Swift/Controllers/Chat/MUCController.h +++ b/Swift/Controllers/Chat/MUCController.h @@ -44,7 +44,7 @@ namespace Swift { class MUCController : public ChatControllerBase { public: - MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* roster); + MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* roster, HistoryController* historyController, MUCRegistry* mucRegistry); ~MUCController(); boost::signal<void ()> onUserLeft; boost::signal<void ()> onUserJoined; @@ -64,6 +64,7 @@ namespace Swift { void preHandleIncomingMessage(boost::shared_ptr<MessageEvent>); void postHandleIncomingMessage(boost::shared_ptr<MessageEvent>); void cancelReplaces(); + void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming); private: void setAvailableRoomActions(const MUCOccupant::Affiliation& affiliation, const MUCOccupant::Role& role); @@ -105,6 +106,8 @@ namespace Swift { void handleChangeAffiliationsRequest(const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& changes); void handleInviteToMUCWindowDismissed(); void handleInviteToMUCWindowCompleted(); + void addRecentLogs(); + void checkDuplicates(boost::shared_ptr<Message> newMessage); private: MUC::ref muc_; @@ -126,6 +129,7 @@ namespace Swift { boost::optional<std::string> password_; InviteToChatWindow* inviteWindow_; XMPPRoster* xmppRoster_; + std::vector<HistoryMessage> joinContext_; }; } diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 11d0ce2..294dcb8 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -102,7 +102,7 @@ public: ftOverview_ = new FileTransferOverview(ftManager_); mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_); - manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_); + manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, NULL); avatarManager_ = new NullAvatarManager(); manager_->setAvatarManager(avatarManager_); diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp index 2cc62bc..4f37229 100644 --- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp @@ -64,7 +64,7 @@ public: entityCapsProvider_ = new DummyEntityCapsProvider(); muc_ = boost::make_shared<MUC>(stanzaChannel_, iqRouter_, directedPresenceSender_, mucJID_, mucRegistry_); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc_->getJID(), uiEventStream_).Return(window_); - controller_ = new MUCController (self_, muc_, boost::optional<std::string>(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_, NULL); + controller_ = new MUCController (self_, muc_, boost::optional<std::string>(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_, NULL, NULL, mucRegistry_); }; void tearDown() { diff --git a/Swift/Controllers/HistoryController.cpp b/Swift/Controllers/HistoryController.cpp new file mode 100644 index 0000000..d730236 --- /dev/null +++ b/Swift/Controllers/HistoryController.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012 Catalin Badea + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <Swift/Controllers/HistoryController.h> +#include <Swiften/History/HistoryStorage.h> +#include <Swiften/History/HistoryMessage.h> +#include <boost/date_time/c_local_time_adjustor.hpp> + +namespace Swift { + +HistoryController::HistoryController(HistoryStorage* localHistoryStorage) : localHistory_(localHistoryStorage) { +} + +HistoryController::~HistoryController() { +} + +void HistoryController::addMessage(const std::string& message, const JID& fromJID, const JID& toJID, HistoryMessage::Type type, const boost::posix_time::ptime& timeStamp) { + // note: using localtime timestamps + boost::posix_time::ptime localTime = boost::date_time::c_local_adjustor<boost::posix_time::ptime>::utc_to_local(timeStamp); + int offset = (localTime - timeStamp).hours(); + + HistoryMessage historyMessage(message, fromJID, toJID, type, localTime, offset); + + localHistory_->addMessage(historyMessage); + onNewMessage(historyMessage); +} + +std::vector<HistoryMessage> HistoryController::getMessagesFromDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const { + return localHistory_->getMessagesFromDate(selfJID, contactJID, type, date); +} + +std::vector<HistoryMessage> HistoryController::getMUCContext(const JID& selfJID, const JID& mucJID, const boost::posix_time::ptime& timeStamp) const { + boost::posix_time::ptime localTime = boost::date_time::c_local_adjustor<boost::posix_time::ptime>::utc_to_local(timeStamp); + return getMessagesFromDate(selfJID, mucJID, HistoryMessage::Groupchat, localTime.date()); +} + +std::vector<HistoryMessage> HistoryController::getMessagesFromPreviousDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const { + return localHistory_->getMessagesFromPreviousDate(selfJID, contactJID, type, date); +} + +std::vector<HistoryMessage> HistoryController::getMessagesFromNextDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const { + return localHistory_->getMessagesFromNextDate(selfJID, contactJID, type, date); +} + +ContactsMap HistoryController::getContacts(const JID& selfJID, HistoryMessage::Type type, const std::string& keyword) const { + return localHistory_->getContacts(selfJID, type, keyword); +} + +boost::posix_time::ptime HistoryController::getLastTimeStampFromMUC(const JID& selfJID, const JID& mucJID) { + return localHistory_->getLastTimeStampFromMUC(selfJID, mucJID); +} + +} diff --git a/Swift/Controllers/HistoryController.h b/Swift/Controllers/HistoryController.h new file mode 100644 index 0000000..8c86409 --- /dev/null +++ b/Swift/Controllers/HistoryController.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012 Catalin Badea + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/JID/JID.h> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <vector> +#include <set> +#include <Swiften/Base/boost_bsignals.h> +#include <Swiften/History/HistoryMessage.h> +#include <Swiften/History/HistoryStorage.h> + +namespace Swift { + class JID; + + class HistoryController { + public: + HistoryController(HistoryStorage* localHistoryStorage); + ~HistoryController(); + + void addMessage(const std::string& message, const JID& fromJID, const JID& toJID, HistoryMessage::Type type, const boost::posix_time::ptime& timeStamp); + std::vector<HistoryMessage> getMessagesFromDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const; + std::vector<HistoryMessage> getMessagesFromPreviousDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const; + std::vector<HistoryMessage> getMessagesFromNextDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const; + ContactsMap getContacts(const JID& selfJID, HistoryMessage::Type type, const std::string& keyword = std::string()) const; + std::vector<HistoryMessage> getMUCContext(const JID& selfJID, const JID& mucJID, const boost::posix_time::ptime& timeStamp) const; + + boost::posix_time::ptime getLastTimeStampFromMUC(const JID& selfJID, const JID& mucJID); + + boost::signal<void (const HistoryMessage&)> onNewMessage; + + private: + HistoryStorage* localHistory_; + bool remoteArchiveSupported_; + }; +} diff --git a/Swift/Controllers/HistoryViewController.cpp b/Swift/Controllers/HistoryViewController.cpp new file mode 100644 index 0000000..9343017 --- /dev/null +++ b/Swift/Controllers/HistoryViewController.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2012 Catalin Badea + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <Swift/Controllers/HistoryViewController.h> + +#include <Swift/Controllers/UIInterfaces/HistoryWindowFactory.h> +#include <Swift/Controllers/UIEvents/RequestHistoryUIEvent.h> +#include <Swift/Controllers/HistoryController.h> +#include <Swiften/History/HistoryMessage.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/Client/NickResolver.h> +#include <Swiften/Avatars/AvatarManager.h> +#include <Swift/Controllers/Roster/SetPresence.h> +#include <Swift/Controllers/Roster/SetAvatar.h> + +namespace Swift { + static const std::string category[] = { "Contacts", "MUC", "Contacts" }; + +HistoryViewController::HistoryViewController( + const JID& selfJID, + UIEventStream* uiEventStream, + HistoryController* historyController, + NickResolver* nickResolver, + AvatarManager* avatarManager, + PresenceOracle* presenceOracle, + HistoryWindowFactory* historyWindowFactory) : + selfJID_(selfJID), + uiEventStream_(uiEventStream), + historyController_(historyController), + nickResolver_(nickResolver), + avatarManager_(avatarManager), + presenceOracle_(presenceOracle), + historyWindowFactory_(historyWindowFactory), + historyWindow_(NULL), + selectedItem_(NULL), + currentResultDate_(boost::gregorian::not_a_date_time) { + uiEventStream_->onUIEvent.connect(boost::bind(&HistoryViewController::handleUIEvent, this, _1)); + + roster_ = new Roster(false, true); +} + +HistoryViewController::~HistoryViewController() { + uiEventStream_->onUIEvent.disconnect(boost::bind(&HistoryViewController::handleUIEvent, this, _1)); + if (historyWindow_) { + historyWindow_->onSelectedContactChanged.disconnect(boost::bind(&HistoryViewController::handleSelectedContactChanged, this, _1)); + historyWindow_->onReturnPressed.disconnect(boost::bind(&HistoryViewController::handleReturnPressed, this, _1)); + historyWindow_->onScrollReachedTop.disconnect(boost::bind(&HistoryViewController::handleScrollReachedTop, this, _1)); + historyWindow_->onScrollReachedBottom.disconnect(boost::bind(&HistoryViewController::handleScrollReachedBottom, this, _1)); + historyWindow_->onPreviousButtonClicked.disconnect(boost::bind(&HistoryViewController::handlePreviousButtonClicked, this)); + historyWindow_->onNextButtonClicked.disconnect(boost::bind(&HistoryViewController::handleNextButtonClicked, this)); + historyWindow_->onCalendarClicked.disconnect(boost::bind(&HistoryViewController::handleCalendarClicked, this, _1)); + historyController_->onNewMessage.disconnect(boost::bind(&HistoryViewController::handleNewMessage, this, _1)); + + presenceOracle_->onPresenceChange.disconnect(boost::bind(&HistoryViewController::handlePresenceChanged, this, _1)); + avatarManager_->onAvatarChanged.disconnect(boost::bind(&HistoryViewController::handleAvatarChanged, this, _1)); + + delete historyWindow_; + } + delete roster_; +} + +void HistoryViewController::handleUIEvent(boost::shared_ptr<UIEvent> rawEvent) { + boost::shared_ptr<RequestHistoryUIEvent> event = boost::dynamic_pointer_cast<RequestHistoryUIEvent>(rawEvent); + if (event != NULL) { + if (historyWindow_ == NULL) { + historyWindow_ = historyWindowFactory_->createHistoryWindow(uiEventStream_); + historyWindow_->onSelectedContactChanged.connect(boost::bind(&HistoryViewController::handleSelectedContactChanged, this, _1)); + historyWindow_->onReturnPressed.connect(boost::bind(&HistoryViewController::handleReturnPressed, this, _1)); + historyWindow_->onScrollReachedTop.connect(boost::bind(&HistoryViewController::handleScrollReachedTop, this, _1)); + historyWindow_->onScrollReachedBottom.connect(boost::bind(&HistoryViewController::handleScrollReachedBottom, this, _1)); + historyWindow_->onPreviousButtonClicked.connect(boost::bind(&HistoryViewController::handlePreviousButtonClicked, this)); + historyWindow_->onNextButtonClicked.connect(boost::bind(&HistoryViewController::handleNextButtonClicked, this)); + historyWindow_->onCalendarClicked.connect(boost::bind(&HistoryViewController::handleCalendarClicked, this, _1)); + historyController_->onNewMessage.connect(boost::bind(&HistoryViewController::handleNewMessage, this, _1)); + + presenceOracle_->onPresenceChange.connect(boost::bind(&HistoryViewController::handlePresenceChanged, this, _1)); + avatarManager_->onAvatarChanged.connect(boost::bind(&HistoryViewController::handleAvatarChanged, this, _1)); + + historyWindow_->setRosterModel(roster_); + } + + // populate roster by doing an empty search + handleReturnPressed(std::string()); + + historyWindow_->activate(); + } +} + +void HistoryViewController::handleSelectedContactChanged(RosterItem* newContact) { + // FIXME: signal is triggerd twice. + ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(newContact); + + if (contact && selectedItem_ != contact) { + selectedItem_ = contact; + historyWindow_->resetConversationView(); + } + else { + return; + } + + JID contactJID = contact->getJID(); + + std::vector<HistoryMessage> messages; + for (int it = HistoryMessage::Chat; it <= HistoryMessage::PrivateMessage; it++) { + HistoryMessage::Type type = static_cast<HistoryMessage::Type>(it); + + if (contacts_[type].count(contactJID)) { + currentResultDate_ = *contacts_[type][contactJID].rbegin(); + selectedItemType_ = type; + messages = historyController_->getMessagesFromDate(selfJID_, contactJID, type, currentResultDate_); + } + } + + historyWindow_->setDate(currentResultDate_); + + foreach (const HistoryMessage& message, messages) { + addNewMessage(message, false); + } +} + +void HistoryViewController::handleNewMessage(const HistoryMessage& message) { + JID contactJID = message.getFromJID().toBare() == selfJID_ ? message.getToJID() : message.getFromJID(); + + JID displayJID; + if (message.getType() == HistoryMessage::PrivateMessage) { + displayJID = contactJID; + } + else { + displayJID = contactJID.toBare(); + } + + // check current conversation + if (selectedItem_ && selectedItem_->getJID() == displayJID) { + if (historyWindow_->getLastVisibleDate() == message.getTime().date()) { + addNewMessage(message, false); + } + } + + // check if the new message matches the query + if (message.getMessage().find(historyWindow_->getSearchBoxText()) == std::string::npos) { + return; + } + + // update contacts + if (!contacts_[message.getType()].count(displayJID)) { + roster_->addContact(displayJID, displayJID, nickResolver_->jidToNick(displayJID), category[message.getType()], avatarManager_->getAvatarPath(displayJID).string()); + } + + contacts_[message.getType()][displayJID].insert(message.getTime().date()); +} + +void HistoryViewController::addNewMessage(const HistoryMessage& message, bool addAtTheTop) { + bool senderIsSelf = message.getFromJID().toBare() == selfJID_; + std::string avatarPath = avatarManager_->getAvatarPath(message.getFromJID()).string(); + + std::string nick = message.getType() != HistoryMessage::Groupchat ? nickResolver_->jidToNick(message.getFromJID()) : message.getFromJID().getResource(); + historyWindow_->addMessage(message.getMessage(), nick, senderIsSelf, avatarPath, message.getTime(), addAtTheTop); +} + +void HistoryViewController::handleReturnPressed(const std::string& keyword) { + reset(); + + for (int it = HistoryMessage::Chat; it <= HistoryMessage::PrivateMessage; it++) { + HistoryMessage::Type type = static_cast<HistoryMessage::Type>(it); + + contacts_[type] = historyController_->getContacts(selfJID_, type, keyword); + + for (ContactsMap::const_iterator contact = contacts_[type].begin(); contact != contacts_[type].end(); contact++) { + const JID& jid = contact->first; + std::string nick; + if (type == HistoryMessage::PrivateMessage) { + nick = jid.toString(); + } + else { + nick = nickResolver_->jidToNick(jid); + } + roster_->addContact(jid, jid, nick, category[type], avatarManager_->getAvatarPath(jid).string()); + + Presence::ref presence = getPresence(jid, type == HistoryMessage::Groupchat); + + if (presence.get()) { + roster_->applyOnItem(SetPresence(presence, JID::WithoutResource), jid); + } + } + } +} + +void HistoryViewController::handleScrollReachedTop(const boost::gregorian::date& date) { + if (!selectedItem_) { + return; + } + + std::vector<HistoryMessage> messages = historyController_->getMessagesFromPreviousDate(selfJID_, selectedItem_->getJID(), selectedItemType_, date); + + foreach (const HistoryMessage& message, messages) { + addNewMessage(message, true); + } + historyWindow_->resetConversationViewTopInsertPoint(); +} + +void HistoryViewController::handleScrollReachedBottom(const boost::gregorian::date& date) { + if (!selectedItem_) { + return; + } + + std::vector<HistoryMessage> messages = historyController_->getMessagesFromNextDate(selfJID_, selectedItem_->getJID(), selectedItemType_, date); + + foreach (const HistoryMessage& message, messages) { + addNewMessage(message, false); + } +} + +void HistoryViewController::handleNextButtonClicked() { + if (!selectedItem_) { + return; + } + + std::set<boost::gregorian::date>::iterator date = contacts_[selectedItemType_][selectedItem_->getJID()].find(currentResultDate_); + + if (*date == *contacts_[selectedItemType_][selectedItem_->getJID()].rbegin()) { + return; + } + + historyWindow_->resetConversationView(); + currentResultDate_ = *(++date); + std::vector<HistoryMessage> messages = historyController_->getMessagesFromDate(selfJID_, selectedItem_->getJID(), selectedItemType_, currentResultDate_); + historyWindow_->setDate(currentResultDate_); + + foreach (const HistoryMessage& message, messages) { + addNewMessage(message, false); + } +} + +void HistoryViewController::handlePreviousButtonClicked() { + if (!selectedItem_) { + return; + } + + std::set<boost::gregorian::date>::iterator date = contacts_[selectedItemType_][selectedItem_->getJID()].find(currentResultDate_); + + if (date == contacts_[selectedItemType_][selectedItem_->getJID()].begin()) { + return; + } + + historyWindow_->resetConversationView(); + currentResultDate_ = *(--date); + std::vector<HistoryMessage> messages = historyController_->getMessagesFromDate(selfJID_, selectedItem_->getJID(), selectedItemType_, currentResultDate_); + historyWindow_->setDate(currentResultDate_); + + foreach (const HistoryMessage& message, messages) { + addNewMessage(message, false); + } +} + +void HistoryViewController::reset() { + roster_->removeAll(); + contacts_.clear(); + selectedItem_ = NULL; + historyWindow_->resetConversationView(); +} + +void HistoryViewController::handleCalendarClicked(const boost::gregorian::date& date) { + if (!selectedItem_) { + return; + } + + boost::gregorian::date newDate; + if (contacts_[selectedItemType_][selectedItem_->getJID()].count(date)) { + newDate = date; + } + else if (date < currentResultDate_) { + foreach(const boost::gregorian::date& current, contacts_[selectedItemType_][selectedItem_->getJID()]) { + if (current > date) { + newDate = current; + break; + } + } + } + else { + reverse_foreach(const boost::gregorian::date& current, contacts_[selectedItemType_][selectedItem_->getJID()]) { + if (current < date) { + newDate = current; + break; + } + } + } + + historyWindow_->setDate(newDate); + if (newDate == currentResultDate_) { + return; + } + currentResultDate_ = newDate; + historyWindow_->resetConversationView(); + + std::vector<HistoryMessage> messages = historyController_->getMessagesFromDate(selfJID_, selectedItem_->getJID(), selectedItemType_, currentResultDate_); + historyWindow_->setDate(currentResultDate_); + + foreach (const HistoryMessage& message, messages) { + addNewMessage(message, false); + } +} + +void HistoryViewController::handlePresenceChanged(Presence::ref presence) { + JID jid = presence->getFrom(); + + if (contacts_[HistoryMessage::Chat].count(jid.toBare())) { + roster_->applyOnItems(SetPresence(presence, JID::WithoutResource)); + return; + } + + if (contacts_[HistoryMessage::Groupchat].count(jid.toBare())) { + Presence::ref availablePresence = boost::make_shared<Presence>(Presence()); + availablePresence->setFrom(jid.toBare()); + roster_->applyOnItems(SetPresence(availablePresence, JID::WithResource)); + } + + if (contacts_[HistoryMessage::PrivateMessage].count(jid)) { + roster_->applyOnItems(SetPresence(presence, JID::WithResource)); + } +} + +void HistoryViewController::handleAvatarChanged(const JID& jid) { + std::string path = avatarManager_->getAvatarPath(jid).string(); + roster_->applyOnItems(SetAvatar(jid, path)); +} + +Presence::ref HistoryViewController::getPresence(const JID& jid, bool isMUC) { + if (jid.isBare() && !isMUC) { + return presenceOracle_->getHighestPriorityPresence(jid); + } + + std::vector<Presence::ref> mucPresence = presenceOracle_->getAllPresence(jid.toBare()); + + if (isMUC && !mucPresence.empty()) { + Presence::ref presence = boost::make_shared<Presence>(Presence()); + presence->setFrom(jid); + return presence; + } + + foreach (Presence::ref presence, mucPresence) { + if (presence.get() && presence->getFrom() == jid) { + return presence; + } + } + + return Presence::create(); +} + +} diff --git a/Swift/Controllers/HistoryViewController.h b/Swift/Controllers/HistoryViewController.h new file mode 100644 index 0000000..f44c968 --- /dev/null +++ b/Swift/Controllers/HistoryViewController.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2012 Catalin Badea + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/boost_bsignals.h> +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> + +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swiften/JID/JID.h> +#include <Swiften/Presence/PresenceOracle.h> +#include <Swiften/History/HistoryStorage.h> +#include <set> + +namespace Swift { + class HistoryWindowFactory; + class HistoryWindow; + class Roster; + class RosterItem; + class ContactRosterItem; + class HistoryController; + class NickResolver; + class AvatarManager; + + class HistoryViewController { + public: + HistoryViewController(const JID& selfJID, UIEventStream* uiEventStream, HistoryController* historyController, NickResolver* nickResolver, AvatarManager* avatarManager, PresenceOracle* presenceOracle, HistoryWindowFactory* historyWindowFactory); + ~HistoryViewController(); + + private: + void handleUIEvent(boost::shared_ptr<UIEvent> event); + void handleSelectedContactChanged(RosterItem* item); + void handleNewMessage(const HistoryMessage& message); + void handleReturnPressed(const std::string& keyword); + void handleScrollReachedTop(const boost::gregorian::date& date); + void handleScrollReachedBottom(const boost::gregorian::date& date); + void handlePreviousButtonClicked(); + void handleNextButtonClicked(); + void handleCalendarClicked(const boost::gregorian::date& date); + void handlePresenceChanged(Presence::ref presence); + void handleAvatarChanged(const JID& jid); + + void addNewMessage(const HistoryMessage& message, bool addAtTheTop); + void reset(); + Presence::ref getPresence(const JID& jid, bool isMUC); + + private: + JID selfJID_; + UIEventStream* uiEventStream_; + HistoryController* historyController_; + NickResolver* nickResolver_; + AvatarManager* avatarManager_; + PresenceOracle* presenceOracle_; + HistoryWindowFactory* historyWindowFactory_; + HistoryWindow* historyWindow_; + Roster* roster_; + + std::map<HistoryMessage::Type, ContactsMap> contacts_; + ContactRosterItem* selectedItem_; + HistoryMessage::Type selectedItemType_; + boost::gregorian::date currentResultDate_; + }; +} diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index f124298..2c02ba8 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -37,6 +37,8 @@ #include "Swift/Controllers/SystemTray.h" #include "Swift/Controllers/SystemTrayController.h" #include "Swift/Controllers/XMLConsoleController.h" +#include <Swift/Controllers/HistoryController.h> +#include <Swift/Controllers/HistoryViewController.h> #include "Swift/Controllers/FileTransferListController.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/Controllers/PresenceNotifier.h" @@ -112,6 +114,7 @@ MainController::MainController( eventNotifier_ = NULL; rosterController_ = NULL; chatsManager_ = NULL; + historyViewController_ = NULL; eventWindowController_ = NULL; profileController_ = NULL; contactEditController_ = NULL; @@ -218,6 +221,12 @@ void MainController::resetClient() { eventWindowController_ = NULL; delete chatsManager_; chatsManager_ = NULL; +#ifdef SWIFT_EXPERIMENTAL_HISTORY + delete historyController_; + historyController_ = NULL; + delete historyViewController_; + historyViewController_ = NULL; +#endif delete ftOverview_; ftOverview_ = NULL; delete rosterController_; @@ -295,7 +304,13 @@ void MainController::handleConnected() { * be before they receive stanzas that need it (e.g. bookmarks).*/ client_->getVCardManager()->requestOwnVCard(); - chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_); +#ifdef SWIFT_EXPERIMENTAL_HISTORY + historyController_ = new HistoryController(storages_->getHistoryStorage()); + historyViewController_ = new HistoryViewController(jid_, uiEventStream_, historyController_, client_->getNickResolver(), client_->getAvatarManager(), client_->getPresenceOracle(), uiFactory_); + chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_, historyController_); +#else + chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_, NULL); +#endif client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1)); chatsManager_->setAvatarManager(client_->getAvatarManager()); diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index eeba9f3..8f04f6c 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -52,6 +52,8 @@ namespace Swift { class SoundEventController; class SoundPlayer; class XMLConsoleController; + class HistoryViewController; + class HistoryController; class FileTransferListController; class UIEventStream; class EventWindowFactory; @@ -143,6 +145,8 @@ namespace Swift { LoginWindow* loginWindow_; UIEventStream* uiEventStream_; XMLConsoleController* xmlConsoleController_; + HistoryViewController* historyViewController_; + HistoryController* historyController_; FileTransferListController* fileTransferListController_; ChatsManager* chatsManager_; ProfileController* profileController_; diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index eca0d38..b6f81b3 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -45,6 +45,8 @@ if env["SCONS_STAGE"] == "build" : "SoundEventController.cpp", "SystemTrayController.cpp", "XMLConsoleController.cpp", + "HistoryViewController.cpp", + "HistoryController.cpp", "FileTransferListController.cpp", "StatusTracker.cpp", "PresenceNotifier.cpp", diff --git a/Swift/Controllers/Storages/FileStorages.cpp b/Swift/Controllers/Storages/FileStorages.cpp index 6447099..cff87d3 100644 --- a/Swift/Controllers/Storages/FileStorages.cpp +++ b/Swift/Controllers/Storages/FileStorages.cpp @@ -9,6 +9,7 @@ #include "Swift/Controllers/Storages/AvatarFileStorage.h" #include "Swift/Controllers/Storages/CapsFileStorage.h" #include "Swift/Controllers/Storages/RosterFileStorage.h" +#include <Swiften/History/SQLiteHistoryStorage.h> namespace Swift { @@ -18,6 +19,9 @@ FileStorages::FileStorages(const boost::filesystem::path& baseDir, const JID& ji capsStorage = new CapsFileStorage(baseDir / "caps"); avatarStorage = new AvatarFileStorage(baseDir / "avatars", baseDir / profile / "avatars"); rosterStorage = new RosterFileStorage(baseDir / profile / "roster.xml"); +#ifdef SWIFT_EXPERIMENTAL_HISTORY + historyStorage = new SQLiteHistoryStorage((baseDir / "history.db").string()); +#endif } FileStorages::~FileStorages() { @@ -25,6 +29,9 @@ FileStorages::~FileStorages() { delete avatarStorage; delete capsStorage; delete vcardStorage; +#ifdef SWIFT_EXPERIMENTAL_HISTORY + delete historyStorage; +#endif } VCardStorage* FileStorages::getVCardStorage() const { @@ -43,4 +50,12 @@ RosterStorage* FileStorages::getRosterStorage() const { return rosterStorage; } +HistoryStorage* FileStorages::getHistoryStorage() const { +#ifdef SWIFT_EXPERIMENTAL_HISTORY + return historyStorage; +#else + return NULL; +#endif +} + } diff --git a/Swift/Controllers/Storages/FileStorages.h b/Swift/Controllers/Storages/FileStorages.h index 28df314..5e89db8 100644 --- a/Swift/Controllers/Storages/FileStorages.h +++ b/Swift/Controllers/Storages/FileStorages.h @@ -15,6 +15,7 @@ namespace Swift { class AvatarFileStorage; class CapsFileStorage; class RosterFileStorage; + class HistoryStorage; class JID; /** @@ -43,11 +44,13 @@ namespace Swift { virtual AvatarStorage* getAvatarStorage() const; virtual CapsStorage* getCapsStorage() const; virtual RosterStorage* getRosterStorage() const; + virtual HistoryStorage* getHistoryStorage() const; private: VCardFileStorage* vcardStorage; AvatarFileStorage* avatarStorage; CapsFileStorage* capsStorage; RosterFileStorage* rosterStorage; + HistoryStorage* historyStorage; }; } diff --git a/Swift/Controllers/UIEvents/RequestHistoryUIEvent.h b/Swift/Controllers/UIEvents/RequestHistoryUIEvent.h new file mode 100644 index 0000000..025e91f --- /dev/null +++ b/Swift/Controllers/UIEvents/RequestHistoryUIEvent.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2012 Catalin Badea + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { + class RequestHistoryUIEvent : public UIEvent { + }; +} diff --git a/Swift/Controllers/UIInterfaces/HistoryWindow.h b/Swift/Controllers/UIInterfaces/HistoryWindow.h new file mode 100644 index 0000000..ffb0ad5 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/HistoryWindow.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 Catalin Badea + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/Roster/Roster.h> + +namespace Swift { + class HistoryWindow { + public: + virtual ~HistoryWindow() {}; + + virtual void activate() = 0; + virtual void setRosterModel(Roster*) = 0; + virtual void addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, const std::string& avatarPath, const boost::posix_time::ptime& time, bool addAtTheTop) = 0; + virtual void resetConversationView() = 0; + virtual void resetConversationViewTopInsertPoint() = 0; // this is a temporary fix used in adding messages at the top + virtual void setDate(const boost::gregorian::date& date) = 0; + + virtual std::string getSearchBoxText() = 0; + virtual boost::gregorian::date getLastVisibleDate() = 0; + + boost::signal<void (RosterItem*)> onSelectedContactChanged; + boost::signal<void (const std::string&)> onReturnPressed; + boost::signal<void (const boost::gregorian::date&)> onScrollReachedTop; + boost::signal<void (const boost::gregorian::date&)> onScrollReachedBottom; + boost::signal<void ()> onPreviousButtonClicked; + boost::signal<void ()> onNextButtonClicked; + boost::signal<void (const boost::gregorian::date&)> onCalendarClicked; + }; +} diff --git a/Swift/Controllers/UIInterfaces/HistoryWindowFactory.h b/Swift/Controllers/UIInterfaces/HistoryWindowFactory.h new file mode 100644 index 0000000..e91bc37 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/HistoryWindowFactory.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2012 Catalin Badea + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/HistoryWindow.h> + +namespace Swift { + class UIEventStream; + class HistoryWindowFactory { + public: + virtual ~HistoryWindowFactory() {}; + virtual HistoryWindow* createHistoryWindow(UIEventStream* eventStream) = 0; + }; +} diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h index cf89dab..d6bea77 100644 --- a/Swift/Controllers/UIInterfaces/UIFactory.h +++ b/Swift/Controllers/UIInterfaces/UIFactory.h @@ -8,6 +8,7 @@ #include <Swift/Controllers/UIInterfaces/ChatListWindowFactory.h> #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/HistoryWindowFactory.h> #include <Swift/Controllers/UIInterfaces/EventWindowFactory.h> #include <Swift/Controllers/UIInterfaces/LoginWindowFactory.h> #include <Swift/Controllers/UIInterfaces/MainWindowFactory.h> @@ -24,6 +25,7 @@ namespace Swift { class UIFactory : public ChatListWindowFactory, public ChatWindowFactory, + public HistoryWindowFactory, public EventWindowFactory, public LoginWindowFactory, public MainWindowFactory, |