diff options
author | Kevin Smith <git@kismith.co.uk> | 2010-02-16 09:05:37 (GMT) |
---|---|---|
committer | Kevin Smith <git@kismith.co.uk> | 2010-02-17 08:05:08 (GMT) |
commit | 5a334fd9b676564a8915baad312d92bd86358eec (patch) | |
tree | aaecbbccd9cddcb843c126b8c022f1d1e667efde /Swift | |
parent | 231c2cb6d00061e70860626467107f4c63f359a0 (diff) | |
download | swift-contrib-5a334fd9b676564a8915baad312d92bd86358eec.zip swift-contrib-5a334fd9b676564a8915baad312d92bd86358eec.tar.bz2 |
Preliminary Chat State Notifications support.
Only covers Active and Composing (Which is very possibly all we care about).
Diffstat (limited to 'Swift')
-rw-r--r-- | Swift/Controllers/Chat/ChatController.cpp | 24 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatController.h | 8 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.cpp | 7 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.h | 2 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatsManager.cpp | 6 | ||||
-rw-r--r-- | Swift/Controllers/UIInterfaces/ChatWindow.h | 4 | ||||
-rw-r--r-- | Swift/Controllers/UnitTest/MockChatWindow.h | 1 | ||||
-rw-r--r-- | Swift/QtUI/QtChatTabs.cpp | 8 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 33 | ||||
-rw-r--r-- | Swift/QtUI/QtChatWindow.h | 6 | ||||
-rw-r--r-- | Swift/QtUI/QtTabbable.h | 3 |
11 files changed, 92 insertions, 10 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() {}; |