summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCătălin Badea <catalin.badea392@gmail.com>2012-08-21 19:06:05 (GMT)
committerKevin Smith <git@kismith.co.uk>2012-08-30 20:54:18 (GMT)
commit7d0cd94de71d9e55e573e28206470439ecde3db5 (patch)
treec361caa96dac71d53a74ba76aa3dc1d349a0c59e /Swift/Controllers
parent6856199274e9c5e581220fccf520b8f011519d17 (diff)
downloadswift-contrib-7d0cd94de71d9e55e573e28206470439ecde3db5.zip
swift-contrib-7d0cd94de71d9e55e573e28206470439ecde3db5.tar.bz2
History dialog
Add history dialog as an experimental feature. License: This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details.
Diffstat (limited to 'Swift/Controllers')
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp18
-rw-r--r--Swift/Controllers/Chat/ChatController.h4
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp11
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.h7
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp10
-rw-r--r--Swift/Controllers/Chat/ChatsManager.h4
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp70
-rw-r--r--Swift/Controllers/Chat/MUCController.h6
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp2
-rw-r--r--Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp2
-rw-r--r--Swift/Controllers/HistoryController.cpp56
-rw-r--r--Swift/Controllers/HistoryController.h40
-rw-r--r--Swift/Controllers/HistoryViewController.cpp352
-rw-r--r--Swift/Controllers/HistoryViewController.h67
-rw-r--r--Swift/Controllers/MainController.cpp17
-rw-r--r--Swift/Controllers/MainController.h4
-rw-r--r--Swift/Controllers/SConscript2
-rw-r--r--Swift/Controllers/Storages/FileStorages.cpp15
-rw-r--r--Swift/Controllers/Storages/FileStorages.h3
-rw-r--r--Swift/Controllers/UIEvents/RequestHistoryUIEvent.h14
-rw-r--r--Swift/Controllers/UIInterfaces/HistoryWindow.h34
-rw-r--r--Swift/Controllers/UIInterfaces/HistoryWindowFactory.h18
-rw-r--r--Swift/Controllers/UIInterfaces/UIFactory.h2
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,