summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2010-02-16 09:05:37 (GMT)
committerKevin Smith <git@kismith.co.uk>2010-02-17 08:05:08 (GMT)
commit5a334fd9b676564a8915baad312d92bd86358eec (patch)
treeaaecbbccd9cddcb843c126b8c022f1d1e667efde
parent231c2cb6d00061e70860626467107f4c63f359a0 (diff)
downloadswift-5a334fd9b676564a8915baad312d92bd86358eec.zip
swift-5a334fd9b676564a8915baad312d92bd86358eec.tar.bz2
Preliminary Chat State Notifications support.
Only covers Active and Composing (Which is very possibly all we care about).
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp24
-rw-r--r--Swift/Controllers/Chat/ChatController.h8
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp7
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.h2
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp6
-rw-r--r--Swift/Controllers/UIInterfaces/ChatWindow.h4
-rw-r--r--Swift/Controllers/UnitTest/MockChatWindow.h1
-rw-r--r--Swift/QtUI/QtChatTabs.cpp8
-rw-r--r--Swift/QtUI/QtChatWindow.cpp33
-rw-r--r--Swift/QtUI/QtChatWindow.h6
-rw-r--r--Swift/QtUI/QtTabbable.h3
-rw-r--r--Swiften/Chat/ChatStateMessageSender.cpp17
-rw-r--r--Swiften/Chat/ChatStateMessageSender.h15
-rw-r--r--Swiften/Chat/ChatStateNotifier.cpp4
-rw-r--r--Swiften/Chat/ChatStateNotifier.h5
-rw-r--r--Swiften/Chat/ChatStateTracker.cpp24
-rw-r--r--Swiften/Chat/ChatStateTracker.h16
-rw-r--r--Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp10
-rw-r--r--Swiften/Elements/ChatState.h20
-rw-r--r--Swiften/Parser/PayloadParsers/ChatStateParser.cpp35
-rw-r--r--Swiften/Parser/PayloadParsers/ChatStateParser.h18
-rw-r--r--Swiften/Parser/PayloadParsers/ChatStateParserFactory.h25
-rw-r--r--Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp2
-rw-r--r--Swiften/Parser/SConscript1
-rw-r--r--Swiften/SConscript1
-rw-r--r--Swiften/Serializer/PayloadSerializers/ChatStateSerializer.cpp22
-rw-r--r--Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h13
-rw-r--r--Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp2
28 files changed, 311 insertions, 21 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index e4031f2..8d63d99 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -3,6 +3,9 @@
#include <boost/bind.hpp>
#include "Swiften/Avatars/AvatarManager.h"
+#include "Swiften/Chat/ChatStateNotifier.h"
+#include "Swiften/Chat/ChatStateMessageSender.h"
+#include "Swiften/Chat/ChatStateTracker.h"
#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
#include "Swift/Controllers/NickResolver.h"
@@ -14,9 +17,20 @@ namespace Swift {
*/
ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager)
: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager) {
+ chatStateNotifier_ = new ChatStateNotifier();
+ chatStateMessageSender_ = new ChatStateMessageSender(chatStateNotifier_, stanzaChannel, contact);
+ chatStateTracker_ = new ChatStateTracker();
nickResolver_ = nickResolver;
presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1, _2));
+ chatStateTracker_->onChatStateChange.connect(boost::bind(&ChatWindow::setContactChatState, chatWindow_, _1));
chatWindow_->setName(nickResolver_->jidToNick(toJID_));
+ chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
+ chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
+}
+
+void ChatController::setToJID(const JID& jid) {
+ chatStateMessageSender_->setContact(jid);
+ ChatControllerBase::setToJID(jid);
}
bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) {
@@ -30,10 +44,19 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<Message> message
toJID_ = from;
}
}
+ chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>());
+ chatStateTracker_->handleMessageReceived(message);
+}
+
+void ChatController::preSendMessageRequest(boost::shared_ptr<Message> message) {
+ if (chatStateNotifier_->contactShouldReceiveStates()) {
+ message->addPayload(boost::shared_ptr<Payload>(new ChatState(ChatState::Active)));
+ }
}
void ChatController::postSendMessage(const String& body) {
addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional<SecurityLabel>(), String(avatarManager_->getAvatarPath(selfJID_).string()));
+ chatStateNotifier_->userSentMessage();
}
String ChatController::senderDisplayNameFromMessage(const JID& from) {
@@ -62,6 +85,7 @@ void ChatController::handlePresenceChange(boost::shared_ptr<Presence> newPresenc
if (!(toJID_.isBare() && newPresence->getFrom().equals(toJID_, JID::WithoutResource)) && newPresence->getFrom() != toJID_) {
return;
}
+ chatStateTracker_->handlePresenceChange(newPresence, previousPresence);
String newStatusChangeString = getStatusChangeString(newPresence);
if (!previousPresence || newStatusChangeString != getStatusChangeString(previousPresence)) {
chatWindow_->addSystemMessage(newStatusChangeString);
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index 58ea797..f2963fd 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -5,10 +5,14 @@
namespace Swift {
class AvatarManager;
+ class ChatStateNotifier;
+ class ChatStateMessageSender;
+ class ChatStateTracker;
class NickResolver;
class ChatController : public ChatControllerBase {
public:
ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager*);
+ virtual void setToJID(const JID& jid);
private:
void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> previousPresence);
@@ -16,11 +20,15 @@ namespace Swift {
bool isIncomingMessageFromMe(boost::shared_ptr<Message> message);
void postSendMessage(const String &body);
void preHandleIncomingMessage(boost::shared_ptr<Message> message);
+ void preSendMessageRequest(boost::shared_ptr<Message>);
String senderDisplayNameFromMessage(const JID& from);
private:
NickResolver* nickResolver_;
JID contact_;
+ ChatStateNotifier* chatStateNotifier_;
+ ChatStateMessageSender* chatStateMessageSender_;
+ ChatStateTracker* chatStateTracker_;
};
}
#endif
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index 5f78795..6fe50d3 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -98,7 +98,9 @@ void ChatControllerBase::addMessage(const String& message, const String& senderN
}
void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) {
- unreadMessages_.push_back(messageEvent);
+ if (messageEvent->isReadable()) {
+ unreadMessages_.push_back(messageEvent);
+ }
chatWindow_->setUnreadMessageCount(unreadMessages_.size());
boost::shared_ptr<Message> message = messageEvent->getStanza();
@@ -109,6 +111,9 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m
chatWindow_->addErrorMessage(errorMessage);
}
else {
+ if (!messageEvent->isReadable()) {
+ return;
+ }
showChatWindow();
boost::shared_ptr<SecurityLabel> label = message->getPayload<SecurityLabel>();
boost::optional<SecurityLabel> maybeLabel = label ? boost::optional<SecurityLabel>(*label) : boost::optional<SecurityLabel>();
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index abf0116..e0d6ac5 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -32,7 +32,7 @@ namespace Swift {
void handleIncomingMessage(boost::shared_ptr<MessageEvent> message);
void addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath);
void setEnabled(bool enabled);
- void setToJID(const JID& jid) {toJID_ = jid;};
+ virtual void setToJID(const JID& jid) {toJID_ = jid;};
protected:
ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager);
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 0efd3e1..9a72f1f 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -133,9 +133,9 @@ void ChatsManager::handleJoinMUCRequest(const JID &muc, const String &nick) {
void ChatsManager::handleIncomingMessage(boost::shared_ptr<Message> message) {
JID jid = message->getFrom();
boost::shared_ptr<MessageEvent> event(new MessageEvent(message));
- if (!event->isReadable()) {
- return;
- }
+ //if (!event->isReadable()) {
+ // return;
+ //}
// Try to deliver it to a MUC
if (message->getType() == Message::Groupchat || message->getType() == Message::Error) {
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index 59c3152..5b38ba2 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -8,6 +8,7 @@
#include "Swiften/Base/String.h"
#include "Swiften/Elements/SecurityLabel.h"
+#include "Swiften/Elements/ChatState.h"
namespace Swift {
class AvatarManager;
@@ -23,6 +24,7 @@ namespace Swift {
virtual void addSystemMessage(const String& message) = 0;
virtual void addErrorMessage(const String& message) = 0;
+ virtual void setContactChatState(ChatState::ChatStateType state) = 0;
virtual void setName(const String& name) = 0;
virtual void show() = 0;
virtual void activate() = 0;
@@ -38,6 +40,8 @@ namespace Swift {
boost::signal<void ()> onClosed;
boost::signal<void ()> onAllMessagesRead;
boost::signal<void (const String&)> onSendMessageRequest;
+ boost::signal<void ()> onUserTyping;
+ boost::signal<void ()> onUserCancelsTyping;
};
}
#endif
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index dc744cd..b487f50 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -13,6 +13,7 @@ namespace Swift {
virtual void addSystemMessage(const String& /*message*/) {};
virtual void addErrorMessage(const String& /*message*/) {};
+ virtual void setContactChatState(ChatState::ChatStateType /*state*/) {};
virtual void setName(const String& name) {name_ = name;};
virtual void show() {};
virtual void activate() {};
diff --git a/Swift/QtUI/QtChatTabs.cpp b/Swift/QtUI/QtChatTabs.cpp
index 6e5c55d..53fa5ce 100644
--- a/Swift/QtUI/QtChatTabs.cpp
+++ b/Swift/QtUI/QtChatTabs.cpp
@@ -104,7 +104,13 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) {
return;
}
tabs_->setTabText(index, widget->windowTitle());
- tabs_->tabBar()->setTabTextColor(index, tabbable->isWidgetAlerting() ? QColor(255,0,0) : QColor(-1,-1,-1)); //invalid resets to default
+ QColor tabTextColor;
+ switch (tabbable->getWidgetAlertState()) {
+ case QtTabbable::WaitingActivity : tabTextColor = QColor(255, 0, 0); break;
+ case QtTabbable::ImpendingActivity : tabTextColor = QColor(0, 255, 0); break;
+ default : tabTextColor = QColor(-1,-1,-1);//invalid resets to default
+ }
+ tabs_->tabBar()->setTabTextColor(index, tabTextColor);
if (widget == tabs_->currentWidget()) {
setWindowTitle(widget->windowTitle());
}
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index e982b21..6765e8a 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -56,7 +56,11 @@ QtChatWindow::QtChatWindow(const QString &contact, QtTreeWidgetFactory *treeWidg
input_->setAcceptRichText(false);
layout->addWidget(input_);
+ inputClearing_ = false;
+ contactIsTyping_ = false;
+
connect(input_, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
+ connect(input_, SIGNAL(textChanged()), this, SLOT(handleInputChanged()));
setFocusProxy(input_);
connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(qAppFocusChanged(QWidget*, QWidget*)));
@@ -134,8 +138,20 @@ void QtChatWindow::setUnreadMessageCount(int count) {
updateTitleWithUnreadCount();
}
-bool QtChatWindow::isWidgetAlerting() {
- return unreadCount_ > 0;
+void QtChatWindow::setContactChatState(ChatState::ChatStateType state) {
+ contactIsTyping_ = (state == ChatState::Composing);
+ printf("Hay, composing %d, %d!\n", state, contactIsTyping_);
+ emit titleUpdated();
+}
+
+QtTabbable::AlertType QtChatWindow::getWidgetAlertState() {
+ if (contactIsTyping_) {
+ return ImpendingActivity;
+ }
+ if (unreadCount_ > 0) {
+ return WaitingActivity;
+ }
+ return NoActivity;
}
void QtChatWindow::setName(const String& name) {
@@ -205,7 +221,20 @@ void QtChatWindow::addSystemMessage(const String& message) {
void QtChatWindow::returnPressed() {
onSendMessageRequest(Q2PSTRING(input_->toPlainText()));
messageLog_->scrollToBottom();
+ inputClearing_ = true;
input_->clear();
+ inputClearing_ = false;
+}
+
+void QtChatWindow::handleInputChanged() {
+ if (inputClearing_) {
+ return;
+ }
+ if (input_->toPlainText().isEmpty()) {
+ onUserCancelsTyping();
+ } else {
+ onUserTyping();
+ }
}
void QtChatWindow::show() {
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index e147fb8..bde91e1 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -35,7 +35,8 @@ namespace Swift {
SecurityLabel getSelectedSecurityLabel();
void setName(const String& name);
void setInputEnabled(bool enabled);
- virtual bool isWidgetAlerting();
+ QtTabbable::AlertType getWidgetAlertState();
+ void setContactChatState(ChatState::ChatStateType state);
protected slots:
void qAppFocusChanged(QWidget* old, QWidget* now);
@@ -45,11 +46,13 @@ namespace Swift {
void showEvent(QShowEvent* event);
private slots:
void returnPressed();
+ void handleInputChanged();
private:
void updateTitleWithUnreadCount();
int unreadCount_;
+ bool contactIsTyping_;
QString contact_;
QtChatView *messageLog_;
QtTextEdit* input_;
@@ -59,6 +62,7 @@ namespace Swift {
bool previousMessageWasSelf_;
bool previousMessageWasSystem_;
QString previousSenderName_;
+ bool inputClearing_;
};
}
diff --git a/Swift/QtUI/QtTabbable.h b/Swift/QtUI/QtTabbable.h
index be528ce..cce45db 100644
--- a/Swift/QtUI/QtTabbable.h
+++ b/Swift/QtUI/QtTabbable.h
@@ -7,9 +7,10 @@ namespace Swift {
class QtTabbable : public QWidget {
Q_OBJECT
public:
+ enum AlertType {NoActivity, WaitingActivity, ImpendingActivity};
~QtTabbable();
bool isWidgetSelected();
- virtual bool isWidgetAlerting() {return false;};
+ virtual AlertType getWidgetAlertState() {return NoActivity;};
protected:
QtTabbable() : QWidget() {};
diff --git a/Swiften/Chat/ChatStateMessageSender.cpp b/Swiften/Chat/ChatStateMessageSender.cpp
index d053cb5..ad1495f 100644
--- a/Swiften/Chat/ChatStateMessageSender.cpp
+++ b/Swiften/Chat/ChatStateMessageSender.cpp
@@ -1,5 +1,22 @@
#include "Swiften/Chat/ChatStateMessageSender.h"
+#include <boost/bind.hpp>
+
+#include "Swiften/Client/StanzaChannel.h"
+
namespace Swift {
+ChatStateMessageSender::ChatStateMessageSender(ChatStateNotifier* notifier, StanzaChannel* stanzaChannel, const JID& contact) : contact_(contact) {
+ notifier_ = notifier;
+ stanzaChannel_ = stanzaChannel;
+ notifier_->onChatStateChanged.connect(boost::bind(&ChatStateMessageSender::handleChatStateChanged, this, _1));
+}
+
+void ChatStateMessageSender::handleChatStateChanged(ChatState::ChatStateType state) {
+ boost::shared_ptr<Message> message(new Message());
+ message->setTo(contact_);
+ message->addPayload(boost::shared_ptr<Payload>(new ChatState(state)));
+ stanzaChannel_->sendMessage(message);
+}
+
}
diff --git a/Swiften/Chat/ChatStateMessageSender.h b/Swiften/Chat/ChatStateMessageSender.h
index d27973d..aff0791 100644
--- a/Swiften/Chat/ChatStateMessageSender.h
+++ b/Swiften/Chat/ChatStateMessageSender.h
@@ -1,7 +1,20 @@
#pragma once
+#include "Swiften/Chat/ChatStateNotifier.h"
+#include "Swiften/Elements/ChatState.h"
+#include "Swiften/JID/JID.h"
+
namespace Swift {
+ class StanzaChannel;
class ChatStateMessageSender {
-
+ public:
+ ChatStateMessageSender(ChatStateNotifier* notifier, StanzaChannel* stanzaChannel, const JID& contact);
+ void setContact(const JID& contact) {contact_ = contact;};
+
+ private:
+ void handleChatStateChanged(ChatState::ChatStateType state);
+ ChatStateNotifier* notifier_;
+ StanzaChannel* stanzaChannel_;
+ JID contact_;
};
}
diff --git a/Swiften/Chat/ChatStateNotifier.cpp b/Swiften/Chat/ChatStateNotifier.cpp
index 432f708..7c6560f 100644
--- a/Swiften/Chat/ChatStateNotifier.cpp
+++ b/Swiften/Chat/ChatStateNotifier.cpp
@@ -16,7 +16,7 @@ void ChatStateNotifier::setContactHas85Caps(bool hasCaps) {
void ChatStateNotifier::setUserIsTyping() {
if (contactShouldReceiveStates() && !userIsTyping_) {
userIsTyping_ = true;
- onChatStateChanged(Composing);
+ onChatStateChanged(ChatState::Composing);
}
}
@@ -27,7 +27,7 @@ void ChatStateNotifier::userSentMessage() {
void ChatStateNotifier::userCancelledNewMessage() {
if (userIsTyping_) {
userIsTyping_ = false;
- onChatStateChanged(Active);
+ onChatStateChanged(ChatState::Active);
}
}
diff --git a/Swiften/Chat/ChatStateNotifier.h b/Swiften/Chat/ChatStateNotifier.h
index 90228b7..0ef4255 100644
--- a/Swiften/Chat/ChatStateNotifier.h
+++ b/Swiften/Chat/ChatStateNotifier.h
@@ -3,10 +3,11 @@
#include <boost/signals.hpp>
#include <boost/shared_ptr.hpp>
+#include "Swiften/Elements/ChatState.h"
+
namespace Swift {
class ChatStateNotifier {
public:
- enum ChatState {Active, Composing, Paused, Inactive, Gone};
ChatStateNotifier();
void setContactHas85Caps(bool hasCaps);
void setUserIsTyping();
@@ -15,7 +16,7 @@ namespace Swift {
void receivedMessageFromContact(bool hasActiveElement);
bool contactShouldReceiveStates();
- boost::signal<void (ChatState)> onChatStateChanged;
+ boost::signal<void (ChatState::ChatStateType)> onChatStateChanged;
private:
bool contactHas85Caps_;
bool isInConversation_;
diff --git a/Swiften/Chat/ChatStateTracker.cpp b/Swiften/Chat/ChatStateTracker.cpp
index 553d2f4..94e01eb 100644
--- a/Swiften/Chat/ChatStateTracker.cpp
+++ b/Swiften/Chat/ChatStateTracker.cpp
@@ -1,5 +1,29 @@
#include "Swiften/Chat/ChatStateTracker.h"
namespace Swift {
+ChatStateTracker::ChatStateTracker() {
+ currentState_ = ChatState::Gone;
+}
+
+void ChatStateTracker::handleMessageReceived(boost::shared_ptr<Message> message) {
+ boost::shared_ptr<ChatState> statePayload = message->getPayload<ChatState>();
+ if (statePayload) {
+ changeState(statePayload->getChatState());;
+ }
+}
+
+void ChatStateTracker::handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence>) {
+ if (newPresence->getType() == Presence::Unavailable) {
+ onChatStateChange(ChatState::Gone);
+ }
+}
+
+void ChatStateTracker::changeState(ChatState::ChatStateType state) {
+ printf("Comparing state %d to old state %d\n", state, currentState_);
+ if (state != currentState_) {
+ currentState_ = state;
+ onChatStateChange(state);
+ }
+}
}
diff --git a/Swiften/Chat/ChatStateTracker.h b/Swiften/Chat/ChatStateTracker.h
index 005c479..e612852 100644
--- a/Swiften/Chat/ChatStateTracker.h
+++ b/Swiften/Chat/ChatStateTracker.h
@@ -1,7 +1,21 @@
#pragma once
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/ELements/ChatState.h"
+
namespace Swift {
class ChatStateTracker {
-
+ public:
+ ChatStateTracker();
+ void handleMessageReceived(boost::shared_ptr<Message> message);
+ void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> oldPresence);
+ boost::signal<void (ChatState::ChatStateType)> onChatStateChange;
+ private:
+ void changeState(ChatState::ChatStateType state);
+ ChatState::ChatStateType currentState_;
};
}
diff --git a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
index bacfc3a..44ec9d8 100644
--- a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
+++ b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
@@ -18,15 +18,15 @@ public:
int composingCallCount;
int activeCallCount;
- ChatStateNotifier::ChatState currentState;
+ ChatState::ChatStateType currentState;
private:
- void handleChatStateChanged(ChatStateNotifier::ChatState newState) {
+ void handleChatStateChanged(ChatState::ChatStateType newState) {
switch (newState) {
- case ChatStateNotifier::Composing:
+ case ChatState::Composing:
composingCallCount++;
break;
- case ChatStateNotifier::Active:
+ case ChatState::Active:
activeCallCount++;
break;
default:
@@ -86,7 +86,7 @@ public:
notifier_->userCancelledNewMessage();
CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
CPPUNIT_ASSERT_EQUAL(1, monitor_->activeCallCount);
- CPPUNIT_ASSERT_EQUAL(ChatStateNotifier::Active, monitor_->currentState);
+ CPPUNIT_ASSERT_EQUAL(ChatState::Active, monitor_->currentState);
}
diff --git a/Swiften/Elements/ChatState.h b/Swiften/Elements/ChatState.h
new file mode 100644
index 0000000..3378cc3
--- /dev/null
+++ b/Swiften/Elements/ChatState.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Elements/Payload.h"
+
+namespace Swift {
+ class ChatState : public Payload {
+ public:
+ enum ChatStateType {Active, Composing, Paused, Inactive, Gone};
+ ChatState(ChatStateType state = Active) {
+ state_ = state;
+ }
+
+ ChatStateType getChatState() { return state_; }
+ void setChatState(ChatStateType state) {state_ = state;}
+
+ private:
+ ChatStateType state_;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/ChatStateParser.cpp b/Swiften/Parser/PayloadParsers/ChatStateParser.cpp
new file mode 100644
index 0000000..52d860a
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ChatStateParser.cpp
@@ -0,0 +1,35 @@
+#include "Swiften/Parser/PayloadParsers/ChatStateParser.h"
+
+namespace Swift {
+
+ChatStateParser::ChatStateParser() : level_(0) {
+}
+
+void ChatStateParser::handleStartElement(const String& element, const String&, const AttributeMap&) {
+ if (level_ == 0) {
+ ChatState::ChatStateType state = ChatState::Active;
+ if (element == "active") {
+ state = ChatState::Active;
+ } else if (element == "composing") {
+ state = ChatState::Composing;
+ } else if (element == "inactive") {
+ state = ChatState::Inactive;
+ } else if (element == "paused") {
+ state = ChatState::Paused;
+ } else if (element == "gone") {
+ state = ChatState::Gone;
+ }
+ getPayloadInternal()->setChatState(state);
+ }
+ ++level_;
+}
+
+void ChatStateParser::handleEndElement(const String&, const String&) {
+ --level_;
+}
+
+void ChatStateParser::handleCharacterData(const String&) {
+
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/ChatStateParser.h b/Swiften/Parser/PayloadParsers/ChatStateParser.h
new file mode 100644
index 0000000..cd212c0
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ChatStateParser.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "Swiften/Elements/ChatState.h"
+#include "Swiften/Parser/GenericPayloadParser.h"
+
+namespace Swift {
+ class ChatStateParser : public GenericPayloadParser<ChatState> {
+ public:
+ ChatStateParser();
+
+ virtual void handleStartElement(const String& element, const String&, const AttributeMap& attributes);
+ virtual void handleEndElement(const String& element, const String&);
+ virtual void handleCharacterData(const String& data);
+
+ private:
+ int level_;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/ChatStateParserFactory.h b/Swiften/Parser/PayloadParsers/ChatStateParserFactory.h
new file mode 100644
index 0000000..1582d09
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/ChatStateParserFactory.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Swiften/Parser/PayloadParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/ChatStateParser.h"
+
+namespace Swift {
+ class PayloadParserFactoryCollection;
+
+ class ChatStateParserFactory : public PayloadParserFactory {
+ public:
+ ChatStateParserFactory() {
+ }
+
+ virtual bool canParse(const String& element, const String& ns, const AttributeMap&) const {
+ return ns == "http://jabber.org/protocol/chatstates" &&
+ (element == "active" || element == "composing"
+ || element == "paused" || element == "inactive" || element == "gone");
+ }
+
+ virtual PayloadParser* createPayloadParser() {
+ return new ChatStateParser();
+ }
+
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index eb4cda0..0857f64 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -4,6 +4,7 @@
#include "Swiften/Parser/PayloadParserFactory.h"
#include "Swiften/Parser/PayloadParsers/ErrorParserFactory.h"
#include "Swiften/Parser/PayloadParsers/BodyParserFactory.h"
+#include "Swiften/Parser/PayloadParsers/ChatStateParserFactory.h"
#include "Swiften/Parser/PayloadParsers/PriorityParserFactory.h"
#include "Swiften/Parser/PayloadParsers/ResourceBindParserFactory.h"
#include "Swiften/Parser/PayloadParsers/StartSessionParserFactory.h"
@@ -41,6 +42,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
factories_.push_back(shared_ptr<PayloadParserFactory>(new VCardUpdateParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new VCardParserFactory()));
factories_.push_back(shared_ptr<PayloadParserFactory>(new PrivateStorageParserFactory(this)));
+ factories_.push_back(shared_ptr<PayloadParserFactory>(new ChatStateParserFactory()));
foreach(shared_ptr<PayloadParserFactory> factory, factories_) {
addFactory(factory.get());
}
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index 1e1dcd8..7d93d8b 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -18,6 +18,7 @@ sources = [
"PayloadParserFactory.cpp",
"PayloadParserFactoryCollection.cpp",
"PayloadParsers/BodyParser.cpp",
+ "PayloadParsers/ChatStateParser.cpp",
"PayloadParsers/DiscoInfoParser.cpp",
"PayloadParsers/ErrorParser.cpp",
"PayloadParsers/FullPayloadParserFactoryCollection.cpp",
diff --git a/Swiften/SConscript b/Swiften/SConscript
index e31818c..3f82bfe 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -55,6 +55,7 @@ sources = [
"Serializer/PayloadSerializer.cpp",
"Serializer/PayloadSerializerCollection.cpp",
"Serializer/PayloadSerializers/CapsInfoSerializer.cpp",
+ "Serializer/PayloadSerializers/ChatStateSerializer.cpp",
"Serializer/PayloadSerializers/DiscoInfoSerializer.cpp",
"Serializer/PayloadSerializers/ErrorSerializer.cpp",
"Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp",
diff --git a/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.cpp b/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.cpp
new file mode 100644
index 0000000..0507092
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.cpp
@@ -0,0 +1,22 @@
+#include "Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h"
+
+namespace Swift {
+
+ChatStateSerializer::ChatStateSerializer() : GenericPayloadSerializer<ChatState>() {
+}
+
+String ChatStateSerializer::serializePayload(boost::shared_ptr<ChatState> chatState) const {
+ String result("<");
+ switch (chatState->getChatState()) {
+ case ChatState::Active: result += "active"; break;
+ case ChatState::Composing: result += "composing"; break;
+ case ChatState::Paused: result += "paused"; break;
+ case ChatState::Inactive: result += "inactive"; break;
+ case ChatState::Gone: result += "gone"; break;
+ default: result += "gone"; break;
+ }
+ result += " xmlns=\"http://jabber.org/protocol/chatstates\"/>";
+ return result;
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h b/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h
new file mode 100644
index 0000000..e99b8b6
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/ChatState.h"
+
+namespace Swift {
+ class ChatStateSerializer : public GenericPayloadSerializer<ChatState> {
+ public:
+ ChatStateSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<ChatState> error) const;
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index fc20018..accf6c6 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -2,6 +2,7 @@
#include "Swiften/Base/foreach.h"
#include "Swiften/Serializer/PayloadSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/BodySerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/ChatStateSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/PrioritySerializer.h"
#include "Swiften/Serializer/PayloadSerializers/ErrorSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/RosterSerializer.h"
@@ -25,6 +26,7 @@ namespace Swift {
FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
serializers_.push_back(new BodySerializer());
+ serializers_.push_back(new ChatStateSerializer());
serializers_.push_back(new PrioritySerializer());
serializers_.push_back(new ErrorSerializer());
serializers_.push_back(new RosterSerializer());