summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2010-09-03 15:07:47 (GMT)
committerKevin Smith <git@kismith.co.uk>2010-09-03 19:06:29 (GMT)
commita0185934b0c929622c5526b84235b86cd44aad1d (patch)
tree2b377a5e8754c35e1a40fe7405dd75804e66fd73
parentfde15d66a75334b23ca8bbd56b44e33893c813c4 (diff)
downloadswift-a0185934b0c929622c5526b84235b86cd44aad1d.zip
swift-a0185934b0c929622c5526b84235b86cd44aad1d.tar.bz2
XEP-0198 Ack support in the UI
Resolves: #7
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp30
-rw-r--r--Swift/Controllers/Chat/ChatController.h5
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp8
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.h8
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp2
-rw-r--r--Swift/Controllers/Chat/ChatsManager.h2
-rw-r--r--Swift/Controllers/UIInterfaces/ChatWindow.h12
-rw-r--r--Swift/Controllers/UnitTest/MockChatWindow.h5
-rw-r--r--Swift/QtUI/MessageSnippet.cpp5
-rw-r--r--Swift/QtUI/MessageSnippet.h2
-rw-r--r--Swift/QtUI/QtChatView.cpp9
-rw-r--r--Swift/QtUI/QtChatView.h1
-rw-r--r--Swift/QtUI/QtChatWindow.cpp24
-rw-r--r--Swift/QtUI/QtChatWindow.h10
-rw-r--r--Swift/QtUI/Swift.qrc1
-rw-r--r--Swift/resources/icons/throbber.gifbin0 -> 673 bytes
-rw-r--r--Swiften/Client/Client.h1
-rw-r--r--Swiften/Client/DummyStanzaChannel.h4
-rw-r--r--Swiften/Client/StanzaChannel.h2
19 files changed, 102 insertions, 29 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index 52288c2..9154b9a 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -10,10 +10,11 @@
10 10
11#include "Swiften/Avatars/AvatarManager.h" 11#include "Swiften/Avatars/AvatarManager.h"
12#include "Swiften/Chat/ChatStateNotifier.h" 12#include "Swiften/Chat/ChatStateNotifier.h"
13#include "Swiften/Chat/ChatStateMessageSender.h" 13#include "Swiften/Chat/ChatStateMessageSender.h"
14#include "Swiften/Chat/ChatStateTracker.h" 14#include "Swiften/Chat/ChatStateTracker.h"
15#include "Swiften/Client/StanzaChannel.h"
15#include "Swift/Controllers/UIInterfaces/ChatWindow.h" 16#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
16#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" 17#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
17#include "Swift/Controllers/NickResolver.h" 18#include "Swift/Controllers/NickResolver.h"
18#include "Swift/Controllers/EventController.h" 19#include "Swift/Controllers/EventController.h"
19 20
@@ -30,10 +31,11 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
30 chatStateMessageSender_ = new ChatStateMessageSender(chatStateNotifier_, stanzaChannel, contact); 31 chatStateMessageSender_ = new ChatStateMessageSender(chatStateNotifier_, stanzaChannel, contact);
31 chatStateTracker_ = new ChatStateTracker(); 32 chatStateTracker_ = new ChatStateTracker();
32 nickResolver_ = nickResolver; 33 nickResolver_ = nickResolver;
33 presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1, _2)); 34 presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1, _2));
34 chatStateTracker_->onChatStateChange.connect(boost::bind(&ChatWindow::setContactChatState, chatWindow_, _1)); 35 chatStateTracker_->onChatStateChange.connect(boost::bind(&ChatWindow::setContactChatState, chatWindow_, _1));
36 stanzaChannel_->onStanzaAcked.connect(boost::bind(&ChatController::handleStanzaAcked, this, _1));
35 String nick = nickResolver_->jidToNick(toJID_); 37 String nick = nickResolver_->jidToNick(toJID_);
36 chatWindow_->setName(nick); 38 chatWindow_->setName(nick);
37 String startMessage("Starting chat with " + nick); 39 String startMessage("Starting chat with " + nick);
38 if (isInMUC) { 40 if (isInMUC) {
39 startMessage += " in chatroom " + contact.toBare().toString() + "."; 41 startMessage += " in chatroom " + contact.toBare().toString() + ".";
@@ -41,10 +43,11 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
41 startMessage += " (" + contact.toBare().toString() + ")."; 43 startMessage += " (" + contact.toBare().toString() + ").";
42 } 44 }
43 chatWindow_->addSystemMessage(startMessage); 45 chatWindow_->addSystemMessage(startMessage);
44 chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_)); 46 chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
45 chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_)); 47 chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
48
46} 49}
47 50
48ChatController::~ChatController() { 51ChatController::~ChatController() {
49 delete chatStateNotifier_; 52 delete chatStateNotifier_;
50 delete chatStateMessageSender_; 53 delete chatStateMessageSender_;
@@ -78,16 +81,39 @@ void ChatController::preSendMessageRequest(boost::shared_ptr<Message> message) {
78 if (chatStateNotifier_->contactShouldReceiveStates()) { 81 if (chatStateNotifier_->contactShouldReceiveStates()) {
79 message->addPayload(boost::shared_ptr<Payload>(new ChatState(ChatState::Active))); 82 message->addPayload(boost::shared_ptr<Payload>(new ChatState(ChatState::Active)));
80 } 83 }
81} 84}
82 85
83void ChatController::postSendMessage(const String& body) { 86void ChatController::postSendMessage(const String& body, boost::shared_ptr<Stanza> sentStanza) {
84 addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional<SecurityLabel>(), String(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); 87 String id = addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional<SecurityLabel>(), String(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time());
88 if (stanzaChannel_->getStreamManagementEnabled()) {
89 chatWindow_->setAckState(id, ChatWindow::Pending);
90 unackedStanzas_[sentStanza] = id;
91 }
85 lastWasPresence_ = false; 92 lastWasPresence_ = false;
86 chatStateNotifier_->userSentMessage(); 93 chatStateNotifier_->userSentMessage();
87} 94}
88 95
96void ChatController::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) {
97 String id = unackedStanzas_[stanza];
98 if (id != "") {
99 chatWindow_->setAckState(id, ChatWindow::Received);
100 }
101 unackedStanzas_.erase(unackedStanzas_.find(stanza));
102}
103
104void ChatController::setEnabled(bool enabled) {
105 if (!enabled) {
106 std::map<boost::shared_ptr<Stanza>, String>::iterator it = unackedStanzas_.begin();
107 for ( ; it != unackedStanzas_.end(); it++) {
108 chatWindow_->setAckState(it->second, ChatWindow::Failed);
109 }
110 unackedStanzas_.clear();
111 }
112 ChatControllerBase::setEnabled(enabled);
113}
114
89String ChatController::senderDisplayNameFromMessage(const JID& from) { 115String ChatController::senderDisplayNameFromMessage(const JID& from) {
90 return nickResolver_->jidToNick(from); 116 return nickResolver_->jidToNick(from);
91} 117}
92 118
93String ChatController::getStatusChangeString(boost::shared_ptr<Presence> presence) { 119String ChatController::getStatusChangeString(boost::shared_ptr<Presence> presence) {
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index d833094..971fca9 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -18,28 +18,31 @@ namespace Swift {
18 class ChatController : public ChatControllerBase { 18 class ChatController : public ChatControllerBase {
19 public: 19 public:
20 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); 20 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);
21 virtual ~ChatController(); 21 virtual ~ChatController();
22 virtual void setToJID(const JID& jid); 22 virtual void setToJID(const JID& jid);
23 virtual void setEnabled(bool enabled);
23 24
24 private: 25 private:
25 void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> previousPresence); 26 void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> previousPresence);
26 String getStatusChangeString(boost::shared_ptr<Presence> presence); 27 String getStatusChangeString(boost::shared_ptr<Presence> presence);
27 bool isIncomingMessageFromMe(boost::shared_ptr<Message> message); 28 bool isIncomingMessageFromMe(boost::shared_ptr<Message> message);
28 void postSendMessage(const String &body); 29 void postSendMessage(const String &body, boost::shared_ptr<Stanza> sentStanza);
29 void preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent); 30 void preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent);
30 void preSendMessageRequest(boost::shared_ptr<Message>); 31 void preSendMessageRequest(boost::shared_ptr<Message>);
31 String senderDisplayNameFromMessage(const JID& from); 32 String senderDisplayNameFromMessage(const JID& from);
32 virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const; 33 virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const;
34 void handleStanzaAcked(boost::shared_ptr<Stanza> stanza);
33 35
34 private: 36 private:
35 NickResolver* nickResolver_; 37 NickResolver* nickResolver_;
36 JID contact_; 38 JID contact_;
37 ChatStateNotifier* chatStateNotifier_; 39 ChatStateNotifier* chatStateNotifier_;
38 ChatStateMessageSender* chatStateMessageSender_; 40 ChatStateMessageSender* chatStateMessageSender_;
39 ChatStateTracker* chatStateTracker_; 41 ChatStateTracker* chatStateTracker_;
40 bool isInMUC_; 42 bool isInMUC_;
41 bool lastWasPresence_; 43 bool lastWasPresence_;
44 std::map<boost::shared_ptr<Stanza>, String> unackedStanzas_;
42 }; 45 };
43} 46}
44#endif 47#endif
45 48
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index e38d12d..b7dd1c0 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -77,11 +77,11 @@ void ChatControllerBase::handleSendMessageRequest(const String &body) {
77 if (useDelayForLatency_) { 77 if (useDelayForLatency_) {
78 boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); 78 boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
79 message->addPayload(boost::shared_ptr<Delay>(new Delay(now, selfJID_))); 79 message->addPayload(boost::shared_ptr<Delay>(new Delay(now, selfJID_)));
80 } 80 }
81 stanzaChannel_->sendMessage(message); 81 stanzaChannel_->sendMessage(message);
82 postSendMessage(message->getBody()); 82 postSendMessage(message->getBody(), boost::dynamic_pointer_cast<Stanza>(message));
83} 83}
84 84
85void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog> catalog, const boost::optional<ErrorPayload>& error) { 85void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog> catalog, const boost::optional<ErrorPayload>& error) {
86 if (!error) { 86 if (!error) {
87 if (catalog->getLabels().size() == 0) { 87 if (catalog->getLabels().size() == 0) {
@@ -104,15 +104,15 @@ void ChatControllerBase::showChatWindow() {
104 104
105void ChatControllerBase::activateChatWindow() { 105void ChatControllerBase::activateChatWindow() {
106 chatWindow_->activate(); 106 chatWindow_->activate();
107} 107}
108 108
109void ChatControllerBase::addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) { 109String ChatControllerBase::addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) {
110 if (message.beginsWith("/me ")) { 110 if (message.beginsWith("/me ")) {
111 chatWindow_->addAction(message.getSplittedAtFirst(' ').second, senderName, senderIsSelf, label, avatarPath, time); 111 return chatWindow_->addAction(message.getSplittedAtFirst(' ').second, senderName, senderIsSelf, label, avatarPath, time);
112 } else { 112 } else {
113 chatWindow_->addMessage(message, senderName, senderIsSelf, label, avatarPath, time); 113 return chatWindow_->addMessage(message, senderName, senderIsSelf, label, avatarPath, time);
114 } 114 }
115} 115}
116 116
117bool ChatControllerBase::isFromContact(const JID& from) { 117bool ChatControllerBase::isFromContact(const JID& from) {
118 return from.toBare() == toJID_.toBare(); 118 return from.toBare() == toJID_.toBare();
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index 61d0ab7..2466690 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -13,10 +13,11 @@
13#include "Swiften/Base/boost_bsignals.h" 13#include "Swiften/Base/boost_bsignals.h"
14#include <boost/filesystem.hpp> 14#include <boost/filesystem.hpp>
15#include <boost/optional.hpp> 15#include <boost/optional.hpp>
16#include <boost/date_time/posix_time/posix_time.hpp> 16#include <boost/date_time/posix_time/posix_time.hpp>
17 17
18#include "Swiften/Elements/Stanza.h"
18#include "Swiften/Base/String.h" 19#include "Swiften/Base/String.h"
19#include "Swiften/Elements/DiscoInfo.h" 20#include "Swiften/Elements/DiscoInfo.h"
20#include "Swiften/Events/MessageEvent.h" 21#include "Swiften/Events/MessageEvent.h"
21#include "Swiften/JID/JID.h" 22#include "Swiften/JID/JID.h"
22#include "Swiften/Elements/SecurityLabelsCatalog.h" 23#include "Swiften/Elements/SecurityLabelsCatalog.h"
@@ -38,17 +39,20 @@ namespace Swift {
38 virtual ~ChatControllerBase(); 39 virtual ~ChatControllerBase();
39 void showChatWindow(); 40 void showChatWindow();
40 void activateChatWindow(); 41 void activateChatWindow();
41 void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info); 42 void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info);
42 void handleIncomingMessage(boost::shared_ptr<MessageEvent> message); 43 void handleIncomingMessage(boost::shared_ptr<MessageEvent> message);
43 void addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time); 44 String addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time);
44 virtual void setEnabled(bool enabled); 45 virtual void setEnabled(bool enabled);
45 virtual void setToJID(const JID& jid) {toJID_ = jid;}; 46 virtual void setToJID(const JID& jid) {toJID_ = jid;};
46 protected: 47 protected:
47 ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController); 48 ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController);
48 49
49 virtual void postSendMessage(const String&) {}; 50 /**
51 * Pass the Message appended, and the stanza used to send it.
52 */
53 virtual void postSendMessage(const String&, boost::shared_ptr<Stanza>) {};
50 virtual String senderDisplayNameFromMessage(const JID& from) = 0; 54 virtual String senderDisplayNameFromMessage(const JID& from) = 0;
51 virtual bool isIncomingMessageFromMe(boost::shared_ptr<Message>) = 0; 55 virtual bool isIncomingMessageFromMe(boost::shared_ptr<Message>) = 0;
52 virtual void preHandleIncomingMessage(boost::shared_ptr<MessageEvent>) {}; 56 virtual void preHandleIncomingMessage(boost::shared_ptr<MessageEvent>) {};
53 virtual void preSendMessageRequest(boost::shared_ptr<Message>) {}; 57 virtual void preSendMessageRequest(boost::shared_ptr<Message>) {};
54 virtual bool isFromContact(const JID& from); 58 virtual bool isFromContact(const JID& from);
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index dfe75d6..acc5e1d 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -6,12 +6,10 @@
6 6
7#include "Swift/Controllers/Chat/ChatsManager.h" 7#include "Swift/Controllers/Chat/ChatsManager.h"
8 8
9#include <boost/bind.hpp> 9#include <boost/bind.hpp>
10 10
11#include "Swiften/Client/Client.h"
12
13#include "Swift/Controllers/Chat/ChatController.h" 11#include "Swift/Controllers/Chat/ChatController.h"
14#include "Swift/Controllers/EventController.h" 12#include "Swift/Controllers/EventController.h"
15#include "Swift/Controllers/Chat/MUCController.h" 13#include "Swift/Controllers/Chat/MUCController.h"
16#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" 14#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
17#include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" 15#include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index d7a6275..3efd507 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -59,11 +59,11 @@ namespace Swift {
59 std::map<JID, MUCController*> mucControllers_; 59 std::map<JID, MUCController*> mucControllers_;
60 std::map<JID, ChatController*> chatControllers_; 60 std::map<JID, ChatController*> chatControllers_;
61 EventController* eventController_; 61 EventController* eventController_;
62 JID jid_; 62 JID jid_;
63 StanzaChannel* stanzaChannel_; 63 StanzaChannel* stanzaChannel_;
64 IQRouter* iqRouter_;; 64 IQRouter* iqRouter_;
65 ChatWindowFactory* chatWindowFactory_; 65 ChatWindowFactory* chatWindowFactory_;
66 NickResolver* nickResolver_; 66 NickResolver* nickResolver_;
67 PresenceOracle* presenceOracle_; 67 PresenceOracle* presenceOracle_;
68 AvatarManager* avatarManager_; 68 AvatarManager* avatarManager_;
69 PresenceSender* presenceSender_; 69 PresenceSender* presenceSender_;
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index 4d00dca..1ee632c 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -23,15 +23,22 @@ namespace Swift {
23 class Roster; 23 class Roster;
24 class TabComplete; 24 class TabComplete;
25 25
26 class ChatWindow { 26 class ChatWindow {
27 public: 27 public:
28 enum AckState {Pending, Received, Failed};
28 ChatWindow() {} 29 ChatWindow() {}
29 virtual ~ChatWindow() {}; 30 virtual ~ChatWindow() {};
30 31
31 virtual void addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) = 0; 32 /** Add message to window.
32 virtual void addAction(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) = 0; 33 * @return id of added message (for acks).
34 */
35 virtual String addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) = 0;
36 /** Adds action to window.
37 * @return id of added message (for acks);
38 */
39 virtual String addAction(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) = 0;
33 virtual void addSystemMessage(const String& message) = 0; 40 virtual void addSystemMessage(const String& message) = 0;
34 virtual void addPresenceMessage(const String& message) = 0; 41 virtual void addPresenceMessage(const String& message) = 0;
35 virtual void addErrorMessage(const String& message) = 0; 42 virtual void addErrorMessage(const String& message) = 0;
36 43
37 virtual void setContactChatState(ChatState::ChatStateType state) = 0; 44 virtual void setContactChatState(ChatState::ChatStateType state) = 0;
@@ -47,10 +54,11 @@ namespace Swift {
47 virtual SecurityLabel getSelectedSecurityLabel() = 0; 54 virtual SecurityLabel getSelectedSecurityLabel() = 0;
48 virtual void setInputEnabled(bool enabled) = 0; 55 virtual void setInputEnabled(bool enabled) = 0;
49 virtual void setRosterModel(Roster* model) = 0; 56 virtual void setRosterModel(Roster* model) = 0;
50 virtual void setTabComplete(TabComplete* completer) = 0; 57 virtual void setTabComplete(TabComplete* completer) = 0;
51 virtual void replaceLastMessage(const String& message) = 0; 58 virtual void replaceLastMessage(const String& message) = 0;
59 virtual void setAckState(const String& id, AckState state) = 0;
52 60
53 boost::signal<void ()> onClosed; 61 boost::signal<void ()> onClosed;
54 boost::signal<void ()> onAllMessagesRead; 62 boost::signal<void ()> onAllMessagesRead;
55 boost::signal<void (const String&)> onSendMessageRequest; 63 boost::signal<void (const String&)> onSendMessageRequest;
56 boost::signal<void ()> onUserTyping; 64 boost::signal<void ()> onUserTyping;
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index 822a128..4e7a117 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -12,12 +12,12 @@ namespace Swift {
12 class MockChatWindow : public ChatWindow { 12 class MockChatWindow : public ChatWindow {
13 public: 13 public:
14 MockChatWindow() {}; 14 MockChatWindow() {};
15 virtual ~MockChatWindow(); 15 virtual ~MockChatWindow();
16 16
17 virtual void addMessage(const String& message, const String& /*senderName*/, bool /*senderIsSelf*/, const boost::optional<SecurityLabel>& /*label*/, const String& /*avatarPath*/, const boost::posix_time::ptime&) {lastMessageBody_ = message;}; 17 virtual String addMessage(const String& message, const String& /*senderName*/, bool /*senderIsSelf*/, const boost::optional<SecurityLabel>& /*label*/, const String& /*avatarPath*/, const boost::posix_time::ptime&) {lastMessageBody_ = message; return "";};
18 virtual void addAction(const String& message, const String& /*senderName*/, bool /*senderIsSelf*/, const boost::optional<SecurityLabel>& /*label*/, const String& /*avatarPath*/, const boost::posix_time::ptime&) {lastMessageBody_ = message;}; 18 virtual String addAction(const String& message, const String& /*senderName*/, bool /*senderIsSelf*/, const boost::optional<SecurityLabel>& /*label*/, const String& /*avatarPath*/, const boost::posix_time::ptime&) {lastMessageBody_ = message; return "";};
19 virtual void addSystemMessage(const String& /*message*/) {}; 19 virtual void addSystemMessage(const String& /*message*/) {};
20 virtual void addErrorMessage(const String& /*message*/) {}; 20 virtual void addErrorMessage(const String& /*message*/) {};
21 virtual void addPresenceMessage(const String& /*message*/) {}; 21 virtual void addPresenceMessage(const String& /*message*/) {};
22 22
23 virtual void setContactChatState(ChatState::ChatStateType /*state*/) {}; 23 virtual void setContactChatState(ChatState::ChatStateType /*state*/) {};
@@ -32,10 +32,11 @@ namespace Swift {
32 virtual SecurityLabel getSelectedSecurityLabel() {return SecurityLabel();}; 32 virtual SecurityLabel getSelectedSecurityLabel() {return SecurityLabel();};
33 virtual void setInputEnabled(bool /*enabled*/) {}; 33 virtual void setInputEnabled(bool /*enabled*/) {};
34 virtual void setRosterModel(Roster* /*roster*/) {}; 34 virtual void setRosterModel(Roster* /*roster*/) {};
35 virtual void setTabComplete(TabComplete*) {}; 35 virtual void setTabComplete(TabComplete*) {};
36 virtual void replaceLastMessage(const Swift::String&) {}; 36 virtual void replaceLastMessage(const Swift::String&) {};
37 void setAckState(const String& /*id*/, AckState /*state*/) {};
37 38
38 boost::signal<void ()> onClosed; 39 boost::signal<void ()> onClosed;
39 boost::signal<void ()> onAllMessagesRead; 40 boost::signal<void ()> onAllMessagesRead;
40 boost::signal<void (const String&)> onSendMessageRequest; 41 boost::signal<void (const String&)> onSendMessageRequest;
41 42
diff --git a/Swift/QtUI/MessageSnippet.cpp b/Swift/QtUI/MessageSnippet.cpp
index 0159386..1c8ddca 100644
--- a/Swift/QtUI/MessageSnippet.cpp
+++ b/Swift/QtUI/MessageSnippet.cpp
@@ -9,11 +9,11 @@
9#include <QtDebug> 9#include <QtDebug>
10#include <QDateTime> 10#include <QDateTime>
11 11
12namespace Swift { 12namespace Swift {
13 13
14MessageSnippet::MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme) : ChatSnippet(appendToPrevious) { 14MessageSnippet::MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id) : ChatSnippet(appendToPrevious) {
15 if (isIncoming) { 15 if (isIncoming) {
16 if (appendToPrevious) { 16 if (appendToPrevious) {
17 content_ = theme->getIncomingNextContent(); 17 content_ = theme->getIncomingNextContent();
18 } 18 }
19 else { 19 else {
@@ -27,14 +27,15 @@ MessageSnippet::MessageSnippet(const QString& message, const QString& sender, co
27 else { 27 else {
28 content_ = theme->getOutgoingContent(); 28 content_ = theme->getOutgoingContent();
29 } 29 }
30 } 30 }
31 31
32 content_.replace("%message%", "<span class='swift_message'>" + escape(message) + "</span>"); 32 content_.replace("%message%", "<span class='swift_ack'></span><span class='swift_message'>" + escape(message) + "</span>");
33 content_.replace("%sender%", escape(sender)); 33 content_.replace("%sender%", escape(sender));
34 content_.replace("%time%", "<span class='swift_time'>" + escape(time.toString("h:mm")) + "</span>"); 34 content_.replace("%time%", "<span class='swift_time'>" + escape(time.toString("h:mm")) + "</span>");
35 content_.replace("%userIconPath%", escape(iconURI)); 35 content_.replace("%userIconPath%", escape(iconURI));
36 content_ = "<div id='" + id + "'>" + content_ + "</div>";
36} 37}
37 38
38MessageSnippet::~MessageSnippet() { 39MessageSnippet::~MessageSnippet() {
39 40
40} 41}
diff --git a/Swift/QtUI/MessageSnippet.h b/Swift/QtUI/MessageSnippet.h
index 4918c19..c7425e9 100644
--- a/Swift/QtUI/MessageSnippet.h
+++ b/Swift/QtUI/MessageSnippet.h
@@ -13,11 +13,11 @@
13class QDateTime; 13class QDateTime;
14 14
15namespace Swift { 15namespace Swift {
16 class MessageSnippet : public ChatSnippet { 16 class MessageSnippet : public ChatSnippet {
17 public: 17 public:
18 MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme); 18 MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id);
19 virtual ~MessageSnippet(); 19 virtual ~MessageSnippet();
20 const QString& getContent() const { 20 const QString& getContent() const {
21 return content_; 21 return content_;
22 } 22 }
23 23
diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp
index d48365b..145371f 100644
--- a/Swift/QtUI/QtChatView.cpp
+++ b/Swift/QtUI/QtChatView.cpp
@@ -127,10 +127,19 @@ void QtChatView::copySelectionToClipboard() {
127 if (!webPage_->selectedText().isEmpty()) { 127 if (!webPage_->selectedText().isEmpty()) {
128 webPage_->triggerAction(QWebPage::Copy); 128 webPage_->triggerAction(QWebPage::Copy);
129 } 129 }
130} 130}
131 131
132void QtChatView::setAckXML(const QString& id, const QString& xml) {
133 QWebElement message = document_.findFirst("#" + id);
134 /* Deliberately not asserting here, so that when we start expiring old messages it won't hit us */
135 if (message.isNull()) return;
136 QWebElement ackElement = message.findFirst("span.swift_ack");
137 assert(!ackElement.isNull());
138 ackElement.setInnerXml(xml);
139}
140
132bool QtChatView::isScrolledToBottom() const { 141bool QtChatView::isScrolledToBottom() const {
133 return webPage_->mainFrame()->scrollBarValue(Qt::Vertical) == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical); 142 return webPage_->mainFrame()->scrollBarValue(Qt::Vertical) == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical);
134} 143}
135 144
136void QtChatView::scrollToBottom() { 145void QtChatView::scrollToBottom() {
diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h
index ce1f8bc..e60c92f 100644
--- a/Swift/QtUI/QtChatView.h
+++ b/Swift/QtUI/QtChatView.h
@@ -29,10 +29,11 @@ namespace Swift {
29 29
30 void addMessage(boost::shared_ptr<ChatSnippet> snippet); 30 void addMessage(boost::shared_ptr<ChatSnippet> snippet);
31 void replaceLastMessage(const QString& newMessage); 31 void replaceLastMessage(const QString& newMessage);
32 void replaceLastMessage(const QString& newMessage, const QString& note); 32 void replaceLastMessage(const QString& newMessage, const QString& note);
33 bool isScrolledToBottom() const; 33 bool isScrolledToBottom() const;
34 void setAckXML(const QString& id, const QString& xml);
34 35
35 signals: 36 signals:
36 void gotFocus(); 37 void gotFocus();
37 38
38 public slots: 39 public slots:
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index 70bde4b..2955ee4 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -243,15 +243,15 @@ void QtChatWindow::updateTitleWithUnreadCount() {
243 setWindowTitle(contact_); 243 setWindowTitle(contact_);
244 } 244 }
245 emit titleUpdated(); 245 emit titleUpdated();
246} 246}
247 247
248void QtChatWindow::addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) { 248String QtChatWindow::addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) {
249 addMessage(message, senderName, senderIsSelf, label, avatarPath, "", time); 249 return addMessage(message, senderName, senderIsSelf, label, avatarPath, "", time);
250} 250}
251 251
252void QtChatWindow::addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const QString& style, const boost::posix_time::ptime& time) { 252String QtChatWindow::addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const QString& style, const boost::posix_time::ptime& time) {
253 if (isWidgetSelected()) { 253 if (isWidgetSelected()) {
254 onAllMessagesRead(); 254 onAllMessagesRead();
255 } 255 }
256 256
257 QString htmlString; 257 QString htmlString;
@@ -266,24 +266,36 @@ void QtChatWindow::addMessage(const String &message, const String &senderName, b
266 QString styleSpanEnd = style == "" ? "" : "</span>"; 266 QString styleSpanEnd = style == "" ? "" : "</span>";
267 htmlString += styleSpanStart + messageHTML + styleSpanEnd; 267 htmlString += styleSpanStart + messageHTML + styleSpanEnd;
268 268
269 bool appendToPrevious = !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName))); 269 bool appendToPrevious = !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
270 QString qAvatarPath = avatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(P2QSTRING(avatarPath)).toEncoded(); 270 QString qAvatarPath = avatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(P2QSTRING(avatarPath)).toEncoded();
271 messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_))); 271 String id = id_.generateID();
272 messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
272 273
273 previousMessageWasSelf_ = senderIsSelf; 274 previousMessageWasSelf_ = senderIsSelf;
274 previousSenderName_ = P2QSTRING(senderName); 275 previousSenderName_ = P2QSTRING(senderName);
275 previousMessageWasSystem_ = false; 276 previousMessageWasSystem_ = false;
276 previousMessageWasPresence_ = false; 277 previousMessageWasPresence_ = false;
278 return id;
279}
280
281void QtChatWindow::setAckState(String const& id, ChatWindow::AckState state) {
282 QString xml;
283 switch (state) {
284 case ChatWindow::Pending: xml = "<img src='qrc:/icons/throbber.gif' alt='This message has not been received by your server yet.'/>"; break;
285 case ChatWindow::Received: xml = ""; break;
286 case ChatWindow::Failed: xml = "<img src='qrc:/icons/error.png' alt='This message may not have been transmitted.'/>"; break;
287 }
288 messageLog_->setAckXML(P2QSTRING(id), xml);
277} 289}
278 290
279int QtChatWindow::getCount() { 291int QtChatWindow::getCount() {
280 return unreadCount_; 292 return unreadCount_;
281} 293}
282 294
283void QtChatWindow::addAction(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) { 295String QtChatWindow::addAction(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) {
284 addMessage(" *" + message + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time); 296 return addMessage(" *" + message + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time);
285} 297}
286 298
287void QtChatWindow::addErrorMessage(const String& errorMessage) { 299void QtChatWindow::addErrorMessage(const String& errorMessage) {
288 if (isWidgetSelected()) { 300 if (isWidgetSelected()) {
289 onAllMessagesRead(); 301 onAllMessagesRead();
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index a51b866..333aa44 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -9,10 +9,12 @@
9 9
10#include "Swift/Controllers/UIInterfaces/ChatWindow.h" 10#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
11 11
12#include "QtTabbable.h" 12#include "QtTabbable.h"
13 13
14#include "Swiften/Base/IDGenerator.h"
15
14class QTextEdit; 16class QTextEdit;
15class QLineEdit; 17class QLineEdit;
16class QComboBox; 18class QComboBox;
17 19
18namespace Swift { 20namespace Swift {
@@ -26,12 +28,12 @@ namespace Swift {
26 class QtChatWindow : public QtTabbable, public ChatWindow { 28 class QtChatWindow : public QtTabbable, public ChatWindow {
27 Q_OBJECT 29 Q_OBJECT
28 public: 30 public:
29 QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream); 31 QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream);
30 ~QtChatWindow(); 32 ~QtChatWindow();
31 void addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time); 33 String addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time);
32 void addAction(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time); 34 String addAction(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time);
33 void addSystemMessage(const String& message); 35 void addSystemMessage(const String& message);
34 void addPresenceMessage(const String& message); 36 void addPresenceMessage(const String& message);
35 void addErrorMessage(const String& errorMessage); 37 void addErrorMessage(const String& errorMessage);
36 void show(); 38 void show();
37 void activate(); 39 void activate();
@@ -48,10 +50,11 @@ namespace Swift {
48 void setContactChatState(ChatState::ChatStateType state); 50 void setContactChatState(ChatState::ChatStateType state);
49 void setRosterModel(Roster* roster); 51 void setRosterModel(Roster* roster);
50 void setTabComplete(TabComplete* completer); 52 void setTabComplete(TabComplete* completer);
51 int getCount(); 53 int getCount();
52 void replaceLastMessage(const String& message); 54 void replaceLastMessage(const String& message);
55 void setAckState(const String& id, AckState state);
53 56
54 signals: 57 signals:
55 void geometryChanged(); 58 void geometryChanged();
56 59
57 protected slots: 60 protected slots:
@@ -69,11 +72,11 @@ namespace Swift {
69 void handleKeyPressEvent(QKeyEvent* event); 72 void handleKeyPressEvent(QKeyEvent* event);
70 73
71 private: 74 private:
72 void updateTitleWithUnreadCount(); 75 void updateTitleWithUnreadCount();
73 void tabComplete(); 76 void tabComplete();
74 void addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const QString& style, const boost::posix_time::ptime& time); 77 String addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const QString& style, const boost::posix_time::ptime& time);
75 78
76 int unreadCount_; 79 int unreadCount_;
77 bool contactIsTyping_; 80 bool contactIsTyping_;
78 QString contact_; 81 QString contact_;
79 QtChatView* messageLog_; 82 QtChatView* messageLog_;
@@ -88,9 +91,10 @@ namespace Swift {
88 bool previousMessageWasPresence_; 91 bool previousMessageWasPresence_;
89 QString previousSenderName_; 92 QString previousSenderName_;
90 bool inputClearing_; 93 bool inputClearing_;
91 UIEventStream* eventStream_; 94 UIEventStream* eventStream_;
92 bool inputEnabled_; 95 bool inputEnabled_;
96 IDGenerator id_;
93 }; 97 };
94} 98}
95 99
96#endif 100#endif
diff --git a/Swift/QtUI/Swift.qrc b/Swift/QtUI/Swift.qrc
index 4da687c..6c8e027 100644
--- a/Swift/QtUI/Swift.qrc
+++ b/Swift/QtUI/Swift.qrc
@@ -8,10 +8,11 @@
8 <file alias="icons/away.png">../resources/icons/away.png</file> 8 <file alias="icons/away.png">../resources/icons/away.png</file>
9 <file alias="icons/dnd.png">../resources/icons/dnd.png</file> 9 <file alias="icons/dnd.png">../resources/icons/dnd.png</file>
10 <file alias="icons/offline.png">../resources/icons/offline.png</file> 10 <file alias="icons/offline.png">../resources/icons/offline.png</file>
11 <file alias="icons/certificate.png">../resources/icons/certificate.png</file> 11 <file alias="icons/certificate.png">../resources/icons/certificate.png</file>
12 <file alias="icons/error.png">../resources/icons/error.png</file> 12 <file alias="icons/error.png">../resources/icons/error.png</file>
13 <file alias="icons/throbber.gif">../resources/icons/throbber.gif</file>
13 <file alias="icons/avatar.png">../resources/icons/avatar.png</file> 14 <file alias="icons/avatar.png">../resources/icons/avatar.png</file>
14 <file alias="icons/tray-standard.png">../resources/icons/tray-standard.png</file> 15 <file alias="icons/tray-standard.png">../resources/icons/tray-standard.png</file>
15 <file alias="icons/new-chat.png">../resources/icons/new-chat.png</file> 16 <file alias="icons/new-chat.png">../resources/icons/new-chat.png</file>
16 <file alias="COPYING">../../COPYING</file> 17 <file alias="COPYING">../../COPYING</file>
17 </qresource> 18 </qresource>
diff --git a/Swift/resources/icons/throbber.gif b/Swift/resources/icons/throbber.gif
new file mode 100644
index 0000000..d0bce15
--- /dev/null
+++ b/Swift/resources/icons/throbber.gif
Binary files differ
diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h
index 10e7c38..57228ef 100644
--- a/Swiften/Client/Client.h
+++ b/Swiften/Client/Client.h
@@ -53,11 +53,10 @@ namespace Swift {
53 public: 53 public:
54 boost::signal<void (const ClientError&)> onError; 54 boost::signal<void (const ClientError&)> onError;
55 boost::signal<void ()> onConnected; 55 boost::signal<void ()> onConnected;
56 boost::signal<void (const String&)> onDataRead; 56 boost::signal<void (const String&)> onDataRead;
57 boost::signal<void (const String&)> onDataWritten; 57 boost::signal<void (const String&)> onDataWritten;
58 boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaAcked;
59 58
60 private: 59 private:
61 void handleConnectorFinished(boost::shared_ptr<Connection>, Connector::ref); 60 void handleConnectorFinished(boost::shared_ptr<Connection>, Connector::ref);
62 void send(boost::shared_ptr<Stanza>); 61 void send(boost::shared_ptr<Stanza>);
63 virtual String getNewIQID(); 62 virtual String getNewIQID();
diff --git a/Swiften/Client/DummyStanzaChannel.h b/Swiften/Client/DummyStanzaChannel.h
index b35ed92..d43d68a 100644
--- a/Swiften/Client/DummyStanzaChannel.h
+++ b/Swiften/Client/DummyStanzaChannel.h
@@ -37,10 +37,14 @@ namespace Swift {
37 37
38 virtual bool isAvailable() { 38 virtual bool isAvailable() {
39 return true; 39 return true;
40 } 40 }
41 41
42 virtual bool getStreamManagementEnabled() const {
43 return false;
44 }
45
42 template<typename T> bool isRequestAtIndex(int index, const JID& jid, IQ::Type type) { 46 template<typename T> bool isRequestAtIndex(int index, const JID& jid, IQ::Type type) {
43 boost::shared_ptr<IQ> iqStanza = boost::dynamic_pointer_cast<IQ>(sentStanzas[index]); 47 boost::shared_ptr<IQ> iqStanza = boost::dynamic_pointer_cast<IQ>(sentStanzas[index]);
44 return iqStanza && iqStanza->getType() == type && iqStanza->getTo() == jid && iqStanza->getPayload<T>(); 48 return iqStanza && iqStanza->getType() == type && iqStanza->getTo() == jid && iqStanza->getPayload<T>();
45 } 49 }
46 50
diff --git a/Swiften/Client/StanzaChannel.h b/Swiften/Client/StanzaChannel.h
index bfde05c..09a6db3 100644
--- a/Swiften/Client/StanzaChannel.h
+++ b/Swiften/Client/StanzaChannel.h
@@ -17,10 +17,12 @@ namespace Swift {
17 class StanzaChannel : public IQChannel { 17 class StanzaChannel : public IQChannel {
18 public: 18 public:
19 virtual void sendMessage(boost::shared_ptr<Message>) = 0; 19 virtual void sendMessage(boost::shared_ptr<Message>) = 0;
20 virtual void sendPresence(boost::shared_ptr<Presence>) = 0; 20 virtual void sendPresence(boost::shared_ptr<Presence>) = 0;
21 virtual bool isAvailable() = 0; 21 virtual bool isAvailable() = 0;
22 virtual bool getStreamManagementEnabled() const = 0;
22 23
23 boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived; 24 boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived;
24 boost::signal<void (boost::shared_ptr<Presence>) > onPresenceReceived; 25 boost::signal<void (boost::shared_ptr<Presence>) > onPresenceReceived;
26 boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaAcked;
25 }; 27 };
26} 28}