diff options
| author | Kevin Smith <git@kismith.co.uk> | 2010-09-03 15:07:47 (GMT) | 
|---|---|---|
| committer | Kevin Smith <git@kismith.co.uk> | 2010-09-03 19:06:29 (GMT) | 
| commit | a0185934b0c929622c5526b84235b86cd44aad1d (patch) | |
| tree | 2b377a5e8754c35e1a40fe7405dd75804e66fd73 | |
| parent | fde15d66a75334b23ca8bbd56b44e33893c813c4 (diff) | |
| download | swift-a0185934b0c929622c5526b84235b86cd44aad1d.zip swift-a0185934b0c929622c5526b84235b86cd44aad1d.tar.bz2 | |
XEP-0198 Ack support in the UI
Resolves: #7
| -rw-r--r-- | Swift/Controllers/Chat/ChatController.cpp | 30 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatController.h | 5 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.cpp | 8 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.h | 8 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatsManager.cpp | 2 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatsManager.h | 2 | ||||
| -rw-r--r-- | Swift/Controllers/UIInterfaces/ChatWindow.h | 12 | ||||
| -rw-r--r-- | Swift/Controllers/UnitTest/MockChatWindow.h | 5 | ||||
| -rw-r--r-- | Swift/QtUI/MessageSnippet.cpp | 5 | ||||
| -rw-r--r-- | Swift/QtUI/MessageSnippet.h | 2 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatView.cpp | 9 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatView.h | 1 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 24 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.h | 10 | ||||
| -rw-r--r-- | Swift/QtUI/Swift.qrc | 1 | ||||
| -rw-r--r-- | Swift/resources/icons/throbber.gif | bin | 0 -> 673 bytes | |||
| -rw-r--r-- | Swiften/Client/Client.h | 1 | ||||
| -rw-r--r-- | Swiften/Client/DummyStanzaChannel.h | 4 | ||||
| -rw-r--r-- | Swiften/Client/StanzaChannel.h | 2 | 
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 @@ -12,6 +12,7 @@  #include "Swiften/Chat/ChatStateNotifier.h"  #include "Swiften/Chat/ChatStateMessageSender.h"  #include "Swiften/Chat/ChatStateTracker.h" +#include "Swiften/Client/StanzaChannel.h"  #include "Swift/Controllers/UIInterfaces/ChatWindow.h"  #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"  #include "Swift/Controllers/NickResolver.h" @@ -32,6 +33,7 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ  	nickResolver_ = nickResolver;  	presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1, _2));  	chatStateTracker_->onChatStateChange.connect(boost::bind(&ChatWindow::setContactChatState, chatWindow_, _1)); +	stanzaChannel_->onStanzaAcked.connect(boost::bind(&ChatController::handleStanzaAcked, this, _1));  	String nick = nickResolver_->jidToNick(toJID_);  	chatWindow_->setName(nick);  	String startMessage("Starting chat with " + nick); @@ -43,6 +45,7 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ  	chatWindow_->addSystemMessage(startMessage);  	chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));  	chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_)); +  }  ChatController::~ChatController() { @@ -80,12 +83,35 @@ void ChatController::preSendMessageRequest(boost::shared_ptr<Message> message) {  	}  } -void ChatController::postSendMessage(const String& body) { -	addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional<SecurityLabel>(), String(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); +void ChatController::postSendMessage(const String& body, boost::shared_ptr<Stanza> sentStanza) { +	String id = addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional<SecurityLabel>(), String(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); +	if (stanzaChannel_->getStreamManagementEnabled()) { +		chatWindow_->setAckState(id, ChatWindow::Pending); +		unackedStanzas_[sentStanza] = id; +	}  	lastWasPresence_ = false;  	chatStateNotifier_->userSentMessage();  } +void ChatController::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) { +	String id = unackedStanzas_[stanza]; +	if (id != "") { +		chatWindow_->setAckState(id, ChatWindow::Received); +	} +	unackedStanzas_.erase(unackedStanzas_.find(stanza)); +} + +void ChatController::setEnabled(bool enabled) { +	if (!enabled) { +		std::map<boost::shared_ptr<Stanza>, String>::iterator it = unackedStanzas_.begin(); +		for ( ; it != unackedStanzas_.end(); it++) { +			chatWindow_->setAckState(it->second, ChatWindow::Failed); +		} +		unackedStanzas_.clear(); +	} +	ChatControllerBase::setEnabled(enabled); +} +  String ChatController::senderDisplayNameFromMessage(const JID& from) {  	return nickResolver_->jidToNick(from);  } 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 @@ -20,16 +20,18 @@ namespace Swift {  			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);  			virtual ~ChatController();  			virtual void setToJID(const JID& jid); +			virtual void setEnabled(bool enabled);  		private:  			void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> previousPresence);  			String getStatusChangeString(boost::shared_ptr<Presence> presence);  			bool isIncomingMessageFromMe(boost::shared_ptr<Message> message); -			void postSendMessage(const String &body); +			void postSendMessage(const String &body, boost::shared_ptr<Stanza> sentStanza);  			void preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent);  			void preSendMessageRequest(boost::shared_ptr<Message>);  			String senderDisplayNameFromMessage(const JID& from);  			virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const; +			void handleStanzaAcked(boost::shared_ptr<Stanza> stanza);  		private:  			NickResolver* nickResolver_; @@ -39,6 +41,7 @@ namespace Swift {  			ChatStateTracker* chatStateTracker_;  			bool isInMUC_;  			bool lastWasPresence_; +			std::map<boost::shared_ptr<Stanza>, String> unackedStanzas_;  	};  }  #endif 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 @@ -79,7 +79,7 @@ void ChatControllerBase::handleSendMessageRequest(const String &body) {  		message->addPayload(boost::shared_ptr<Delay>(new Delay(now, selfJID_)));  	}  	stanzaChannel_->sendMessage(message); -	postSendMessage(message->getBody()); +	postSendMessage(message->getBody(), boost::dynamic_pointer_cast<Stanza>(message));  }  void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog> catalog, const boost::optional<ErrorPayload>& error) { @@ -106,11 +106,11 @@ void ChatControllerBase::activateChatWindow() {  	chatWindow_->activate();  } -void ChatControllerBase::addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) { +String ChatControllerBase::addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) {  	if (message.beginsWith("/me ")) { -		chatWindow_->addAction(message.getSplittedAtFirst(' ').second, senderName, senderIsSelf, label, avatarPath, time); +		return chatWindow_->addAction(message.getSplittedAtFirst(' ').second, senderName, senderIsSelf, label, avatarPath, time);  	} else { -		chatWindow_->addMessage(message, senderName, senderIsSelf, label, avatarPath, time); +		return chatWindow_->addMessage(message, senderName, senderIsSelf, label, avatarPath, time);  	}  } 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 @@ -15,6 +15,7 @@  #include <boost/optional.hpp>  #include <boost/date_time/posix_time/posix_time.hpp> +#include "Swiften/Elements/Stanza.h"  #include "Swiften/Base/String.h"  #include "Swiften/Elements/DiscoInfo.h"  #include "Swiften/Events/MessageEvent.h" @@ -40,13 +41,16 @@ namespace Swift {  			void activateChatWindow();  			void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info);  			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, const boost::posix_time::ptime& time); +			String addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time);  			virtual void setEnabled(bool enabled);  			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, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController); -			virtual void postSendMessage(const String&) {}; +			/** +			 * Pass the Message appended, and the stanza used to send it. +			 */ +			virtual void postSendMessage(const String&, boost::shared_ptr<Stanza>) {};  			virtual String senderDisplayNameFromMessage(const JID& from) = 0;  			virtual bool isIncomingMessageFromMe(boost::shared_ptr<Message>) = 0;  			virtual void preHandleIncomingMessage(boost::shared_ptr<MessageEvent>) {}; 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 @@ -8,8 +8,6 @@  #include <boost/bind.hpp> -#include "Swiften/Client/Client.h" -  #include "Swift/Controllers/Chat/ChatController.h"  #include "Swift/Controllers/EventController.h"  #include "Swift/Controllers/Chat/MUCController.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 @@ -61,7 +61,7 @@ namespace Swift {  			EventController* eventController_;  			JID jid_;  			StanzaChannel* stanzaChannel_; -			IQRouter* iqRouter_;; +			IQRouter* iqRouter_;  			ChatWindowFactory* chatWindowFactory_;  			NickResolver* nickResolver_;  			PresenceOracle* presenceOracle_; 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 @@ -25,11 +25,18 @@ namespace Swift {  	class ChatWindow {  		public: +			enum AckState {Pending, Received, Failed};  			ChatWindow() {}  			virtual ~ChatWindow() {}; -			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; -			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; +			/** Add message to window. +			 * @return id of added message (for acks). +			 */ +			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; +			/** Adds action to window. +			 * @return id of added message (for acks); +			 */ +			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;  			virtual void addSystemMessage(const String& message) = 0;  			virtual void addPresenceMessage(const String& message) = 0;  			virtual void addErrorMessage(const String& message) = 0; @@ -49,6 +56,7 @@ namespace Swift {  			virtual void setRosterModel(Roster* model) = 0;  			virtual void setTabComplete(TabComplete* completer) = 0;  			virtual void replaceLastMessage(const String& message) = 0; +			virtual void setAckState(const String& id, AckState state) = 0;  			boost::signal<void ()> onClosed;  			boost::signal<void ()> onAllMessagesRead; 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 @@ -14,8 +14,8 @@ namespace Swift {  			MockChatWindow() {};  			virtual ~MockChatWindow(); -			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;}; -			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;}; +			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 "";}; +			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 "";};  			virtual void addSystemMessage(const String& /*message*/) {};  			virtual void addErrorMessage(const String& /*message*/) {};  			virtual void addPresenceMessage(const String& /*message*/) {}; @@ -34,6 +34,7 @@ namespace Swift {  			virtual void setRosterModel(Roster* /*roster*/) {};  			virtual void setTabComplete(TabComplete*) {};  			virtual void replaceLastMessage(const Swift::String&) {}; +			void setAckState(const String& /*id*/, AckState /*state*/) {};  			boost::signal<void ()> onClosed;  			boost::signal<void ()> onAllMessagesRead; 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 @@ -11,7 +11,7 @@  namespace Swift { -MessageSnippet::MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme) : ChatSnippet(appendToPrevious) { +MessageSnippet::MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id) : ChatSnippet(appendToPrevious) {  	if (isIncoming) {  		if (appendToPrevious) {  			content_ = theme->getIncomingNextContent(); @@ -29,10 +29,11 @@ MessageSnippet::MessageSnippet(const QString& message, const QString& sender, co  		}  	} -	content_.replace("%message%", "<span class='swift_message'>" + escape(message) + "</span>"); +	content_.replace("%message%", "<span class='swift_ack'></span><span class='swift_message'>" + escape(message) + "</span>");  	content_.replace("%sender%", escape(sender));  	content_.replace("%time%", "<span class='swift_time'>" + escape(time.toString("h:mm")) + "</span>");  	content_.replace("%userIconPath%", escape(iconURI)); +	content_ = "<div id='" + id + "'>" + content_ + "</div>";  }  MessageSnippet::~MessageSnippet() { 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 @@ -15,7 +15,7 @@ class QDateTime;  namespace Swift {  	class MessageSnippet : public ChatSnippet {  		public: -			MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme); +			MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id);  			virtual ~MessageSnippet();  			const QString& getContent() const {  				return content_; 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 @@ -129,6 +129,15 @@ void QtChatView::copySelectionToClipboard() {  	}  } +void QtChatView::setAckXML(const QString& id, const QString& xml) { +	QWebElement message = document_.findFirst("#" + id); +	/* Deliberately not asserting here, so that when we start expiring old messages it won't hit us */ +	if (message.isNull()) return; +	QWebElement ackElement = message.findFirst("span.swift_ack"); +	assert(!ackElement.isNull()); +	ackElement.setInnerXml(xml); +} +  bool QtChatView::isScrolledToBottom() const {  	return webPage_->mainFrame()->scrollBarValue(Qt::Vertical) == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical);  } 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 @@ -31,6 +31,7 @@ namespace Swift {  			void replaceLastMessage(const QString& newMessage);  			void replaceLastMessage(const QString& newMessage, const QString& note);  			bool isScrolledToBottom() const; +			void setAckXML(const QString& id, const QString& xml);  		signals:  			void gotFocus(); 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 @@ -245,11 +245,11 @@ void QtChatWindow::updateTitleWithUnreadCount() {  	emit titleUpdated();  } -void QtChatWindow::addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) { -	addMessage(message, senderName, senderIsSelf, label, avatarPath, "", time); +String QtChatWindow::addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) { +	return addMessage(message, senderName, senderIsSelf, label, avatarPath, "", time);  } -void 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) { +String 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) {  	if (isWidgetSelected()) {  		onAllMessagesRead();  	} @@ -268,20 +268,32 @@ void QtChatWindow::addMessage(const String &message, const String &senderName, b  	bool appendToPrevious = !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));  	QString qAvatarPath =  avatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(P2QSTRING(avatarPath)).toEncoded(); -	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_))); +	String id = id_.generateID(); +	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));  	previousMessageWasSelf_ = senderIsSelf;  	previousSenderName_ = P2QSTRING(senderName);  	previousMessageWasSystem_ = false;  	previousMessageWasPresence_ = false; +	return id; +} + +void QtChatWindow::setAckState(String const& id, ChatWindow::AckState state) { +	QString xml; +	switch (state) { +		case ChatWindow::Pending: xml = "<img src='qrc:/icons/throbber.gif' alt='This message has not been received by your server yet.'/>"; break; +		case ChatWindow::Received: xml = ""; break; +		case ChatWindow::Failed: xml = "<img src='qrc:/icons/error.png' alt='This message may not have been transmitted.'/>"; break; +	} +	messageLog_->setAckXML(P2QSTRING(id), xml);  }  int QtChatWindow::getCount() {  	return unreadCount_;  } -void QtChatWindow::addAction(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) { -	addMessage(" *" + message + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time); +String QtChatWindow::addAction(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time) { +	return addMessage(" *" + message + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time);  }  void QtChatWindow::addErrorMessage(const String& errorMessage) { 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 @@ -11,6 +11,8 @@  #include "QtTabbable.h" +#include "Swiften/Base/IDGenerator.h" +  class QTextEdit;  class QLineEdit;  class QComboBox; @@ -28,8 +30,8 @@ namespace Swift {  		public:  			QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream);  			~QtChatWindow(); -			void addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time); -			void addAction(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time); +			String addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time); +			String addAction(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time);  			void addSystemMessage(const String& message);  			void addPresenceMessage(const String& message);  			void addErrorMessage(const String& errorMessage); @@ -50,6 +52,7 @@ namespace Swift {  			void setTabComplete(TabComplete* completer);  			int getCount();  			void replaceLastMessage(const String& message); +			void setAckState(const String& id, AckState state);  		signals:  			void geometryChanged(); @@ -71,7 +74,7 @@ namespace Swift {  		private:  			void updateTitleWithUnreadCount();  			void tabComplete(); -			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); +			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);  			int unreadCount_;  			bool contactIsTyping_; @@ -90,6 +93,7 @@ namespace Swift {  			bool inputClearing_;  			UIEventStream* eventStream_;  			bool inputEnabled_; +			IDGenerator id_;  	};  } 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 @@ -10,6 +10,7 @@  		<file alias="icons/offline.png">../resources/icons/offline.png</file>  		<file alias="icons/certificate.png">../resources/icons/certificate.png</file>  		<file alias="icons/error.png">../resources/icons/error.png</file> +		<file alias="icons/throbber.gif">../resources/icons/throbber.gif</file>  		<file alias="icons/avatar.png">../resources/icons/avatar.png</file>  		<file alias="icons/tray-standard.png">../resources/icons/tray-standard.png</file>  		<file alias="icons/new-chat.png">../resources/icons/new-chat.png</file> diff --git a/Swift/resources/icons/throbber.gif b/Swift/resources/icons/throbber.gifBinary files differ new file mode 100644 index 0000000..d0bce15 --- /dev/null +++ b/Swift/resources/icons/throbber.gif 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 @@ -55,7 +55,6 @@ namespace Swift {  			boost::signal<void ()> onConnected;  			boost::signal<void (const String&)> onDataRead;  			boost::signal<void (const String&)> onDataWritten; -			boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaAcked;  		private:  			void handleConnectorFinished(boost::shared_ptr<Connection>, Connector::ref); 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 @@ -39,6 +39,10 @@ namespace Swift {  				return true;  			} +			virtual bool getStreamManagementEnabled() const { +				return false; +			} +  			template<typename T> bool isRequestAtIndex(int index, const JID& jid, IQ::Type type) {  				boost::shared_ptr<IQ> iqStanza = boost::dynamic_pointer_cast<IQ>(sentStanzas[index]);  				return iqStanza && iqStanza->getType() == type && iqStanza->getTo() == jid && iqStanza->getPayload<T>(); 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 @@ -19,8 +19,10 @@ namespace Swift {  			virtual void sendMessage(boost::shared_ptr<Message>) = 0;  			virtual void sendPresence(boost::shared_ptr<Presence>) = 0;  			virtual bool isAvailable() = 0; +			virtual bool getStreamManagementEnabled() const = 0;  			boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived;  			boost::signal<void (boost::shared_ptr<Presence>) > onPresenceReceived; +			boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaAcked;  	};  } | 
 Swift
 Swift