diff options
23 files changed, 283 insertions, 17 deletions
| diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index 911f3e2..9767f8d 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -102,8 +102,23 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me  			setToJID(from);  		}  	} -	chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>()); +	boost::shared_ptr<Replace> replace = message->getPayload<Replace>(); +	if (replace) { +		// Determine the timestamp +		boost::posix_time::ptime timeStamp = boost::posix_time::microsec_clock::universal_time(); +		boost::optional<boost::posix_time::ptime> messageTimeStamp = getMessageTimestamp(message); +		if (messageTimeStamp) { +			timeStamp = *messageTimeStamp; +		} +		std::string body = message->getBody(); +		chatWindow_->replaceMessage(body, lastMessageUIID_, timeStamp); +		replacedMessage_ = true; +	} +	else { +		replacedMessage_ = false; +	}  	chatStateTracker_->handleMessageReceived(message); +	chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>());  }  void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) { @@ -116,10 +131,19 @@ void ChatController::preSendMessageRequest(boost::shared_ptr<Message> message) {  }  void ChatController::postSendMessage(const std::string& body, boost::shared_ptr<Stanza> sentStanza) { -	std::string id = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), std::string(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); -	if (stanzaChannel_->getStreamManagementEnabled() && !id.empty()) { -		chatWindow_->setAckState(id, ChatWindow::Pending); -		unackedStanzas_[sentStanza] = id; +	if (stanzaChannel_->getStreamManagementEnabled() && !myLastMessageUIID_.empty()) { +		chatWindow_->setAckState(myLastMessageUIID_, ChatWindow::Pending); +		unackedStanzas_[sentStanza] = myLastMessageUIID_; +	} +	boost::shared_ptr<Replace> replace = sentStanza->getPayload<Replace>(); +	if (replace) { +		chatWindow_->replaceMessage(body, myLastMessageUIID_, boost::posix_time::microsec_clock::universal_time()); +	} else { +		myLastMessageUIID_ = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), std::string(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); +		if (stanzaChannel_->getStreamManagementEnabled()) { +			chatWindow_->setAckState(myLastMessageUIID_, ChatWindow::Pending); +			unackedStanzas_[sentStanza] = myLastMessageUIID_; +		}  	}  	lastWasPresence_ = false;  	chatStateNotifier_->userSentMessage(); diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index dd4bf90..4fafb44 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -40,6 +40,7 @@ namespace Swift {  			NickResolver* nickResolver_;  			ChatStateNotifier* chatStateNotifier_;  			ChatStateTracker* chatStateTracker_; +			std::string myLastMessageUIID_;  			bool isInMUC_;  			bool lastWasPresence_;  			std::string lastStatusChangeString_; diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index 566d075..14e17cd 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -30,9 +30,10 @@ namespace Swift {  ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory) {  	chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream);  	chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); -	chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1)); +	chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2));  	setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable());  	createDayChangeTimer(); +	replacedMessage_ = false;  }  ChatControllerBase::~ChatControllerBase() { @@ -88,7 +89,7 @@ void ChatControllerBase::handleAllMessagesRead() {  	chatWindow_->setUnreadMessageCount(0);  } -void ChatControllerBase::handleSendMessageRequest(const std::string &body) { +void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool isCorrectionMessage) {  	if (!stanzaChannel_->isAvailable() || body.empty()) {  		return;  	} @@ -104,6 +105,10 @@ void ChatControllerBase::handleSendMessageRequest(const std::string &body) {  		boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();  		message->addPayload(boost::shared_ptr<Delay>(new Delay(now, selfJID_)));  	} +	if (isCorrectionMessage) { +		message->addPayload(boost::shared_ptr<Replace> (new Replace(lastSentMessageStanzaID_))); +	} +	message->setID(lastSentMessageStanzaID_ = idGenerator_.generateID());  	stanzaChannel_->sendMessage(message);  	postSendMessage(message->getBody(), boost::dynamic_pointer_cast<Stanza>(message));  	onActivity(message->getBody()); @@ -164,7 +169,7 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m  		JID from = message->getFrom();  		std::vector<boost::shared_ptr<Delay> > delayPayloads = message->getPayloads<Delay>();  		for (size_t i = 0; useDelayForLatency_ && i < delayPayloads.size(); i++) { -			if (!delayPayloads[i]->getFrom()) {  +			if (!delayPayloads[i]->getFrom()) {  				continue;  			}  			boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); @@ -180,9 +185,10 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m  		if (messageTimeStamp) {  			timeStamp = *messageTimeStamp;  		} - -		addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp);  		onActivity(body); +		if (!replacedMessage_) { +			lastMessageUIID_ = addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp); +		}  	}  	chatWindow_->show();  	chatWindow_->setUnreadMessageCount(unreadMessages_.size()); diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index 4898320..e0f1b94 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -26,6 +26,7 @@  #include "Swiften/Elements/ErrorPayload.h"  #include "Swiften/Presence/PresenceOracle.h"  #include "Swiften/Queries/IQRouter.h" +#include "Swiften/Base/IDGenerator.h"  namespace Swift {  	class IQRouter; @@ -66,8 +67,10 @@ namespace Swift {  			virtual void dayTicked() {};  		private: +			IDGenerator idGenerator_; +			std::string lastSentMessageStanzaID_;  			void createDayChangeTimer(); -			void handleSendMessageRequest(const std::string &body); +			void handleSendMessageRequest(const std::string &body, bool isCorrectionMessage);  			void handleAllMessagesRead();  			void handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog>, ErrorPayload::ref error);  			std::string getErrorMessage(boost::shared_ptr<ErrorPayload>); @@ -82,6 +85,8 @@ namespace Swift {  			ChatWindow* chatWindow_;  			JID toJID_;  			bool labelsEnabled_; +			bool replacedMessage_; +			std::string lastMessageUIID_;  			PresenceOracle* presenceOracle_;  			AvatarManager* avatarManager_;  			bool useDelayForLatency_; diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index c7bcf1e..aa4416b 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -40,6 +40,7 @@ namespace Swift {  			virtual void addSystemMessage(const std::string& message) = 0;  			virtual void addPresenceMessage(const std::string& message) = 0;  			virtual void addErrorMessage(const std::string& message) = 0; +			virtual void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0;  			virtual void setContactChatState(ChatState::ChatStateType state) = 0;  			virtual void setName(const std::string& name) = 0; @@ -61,9 +62,11 @@ namespace Swift {  			boost::signal<void ()> onClosed;  			boost::signal<void ()> onAllMessagesRead; -			boost::signal<void (const std::string&)> onSendMessageRequest; +			boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest; +			boost::signal<void ()> onSendCorrectionMessageRequest;  			boost::signal<void ()> onUserTyping;  			boost::signal<void ()> onUserCancelsTyping; +			boost::signal<void (bool correction)> onSendMessageCorrection;  	};  }  #endif diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 53a90a7..82c5b52 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -34,12 +34,13 @@ namespace Swift {  			virtual void setRosterModel(Roster* /*roster*/) {};  			virtual void setTabComplete(TabComplete*) {};  			virtual void replaceLastMessage(const std::string&) {}; +			virtual void replaceMessage(const std::string&, const std::string&, const boost::posix_time::ptime&) {};  			void setAckState(const std::string& /*id*/, AckState /*state*/) {};  			virtual void flash() {};  			boost::signal<void ()> onClosed;  			boost::signal<void ()> onAllMessagesRead; -			boost::signal<void (const std::string&)> onSendMessageRequest; +			boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest;  			std::string name_;  			std::string lastMessageBody_; diff --git a/Swift/QtUI/ChatSnippet.h b/Swift/QtUI/ChatSnippet.h index 3aa5fcc..7cbb3b3 100644 --- a/Swift/QtUI/ChatSnippet.h +++ b/Swift/QtUI/ChatSnippet.h @@ -17,7 +17,7 @@ namespace Swift {  		public:  			ChatSnippet(bool appendToPrevious);  			virtual ~ChatSnippet(); -			 +  			virtual const QString& getContent() const = 0;  			virtual QString getContinuationElementID() const { return ""; } @@ -26,7 +26,7 @@ namespace Swift {  			bool getAppendToPrevious() const {  				return appendToPrevious_;  			} -			 +  			static QString escape(const QString& original) {  				QString result(original);  				result.replace("%message%", "%message%"); @@ -37,11 +37,12 @@ namespace Swift {  				return result;  			} +			static QString timeToEscapedString(const QDateTime& time); +  		protected:  			void setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet> continuationFallback) {  				continuationFallback_ = continuationFallback;  			} -			static QString timeToEscapedString(const QDateTime& time);  		private:  			bool appendToPrevious_;  			boost::shared_ptr<ChatSnippet> continuationFallback_; diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp index 4846af6..eab6d91 100644 --- a/Swift/QtUI/QtChatView.cpp +++ b/Swift/QtUI/QtChatView.cpp @@ -138,6 +138,25 @@ void QtChatView::replaceLastMessage(const QString& newMessage, const QString& no  	replace.setInnerXml(ChatSnippet::escape(note));  } +QString QtChatView::getLastSentMessage() { +	return lastElement_.toPlainText(); +} + +void QtChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime) { +	rememberScrolledToBottom(); +	QWebElement message = document_.findFirst("#" + id); +	if (!message.isNull()) { +		QWebElement replaceContent = message.findFirst("span.swift_message"); +		assert(!replaceContent.isNull()); +		QString old = replaceContent.toOuterXml(); +		replaceContent.setInnerXml(ChatSnippet::escape(newMessage)); +		QWebElement replaceTime = message.findFirst("span.swift_time"); +		assert(!replaceTime.isNull()); +		old = replaceTime.toOuterXml(); +		replaceTime.setInnerXml(ChatSnippet::escape(tr("%1 edited").arg(ChatSnippet::timeToEscapedString(editTime)))); +	} +} +  void QtChatView::copySelectionToClipboard() {  	if (!webPage_->selectedText().isEmpty()) {  		webPage_->triggerAction(QWebPage::Copy); diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h index ce12ca8..32741f4 100644 --- a/Swift/QtUI/QtChatView.h +++ b/Swift/QtUI/QtChatView.h @@ -30,8 +30,10 @@ namespace Swift {  			void addLastSeenLine();  			void replaceLastMessage(const QString& newMessage);  			void replaceLastMessage(const QString& newMessage, const QString& note); +			void replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time);  			void rememberScrolledToBottom();  			void setAckXML(const QString& id, const QString& xml); +			QString getLastSentMessage();  		signals:  			void gotFocus(); diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 312ec65..ab230c4 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -35,6 +35,7 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	inputEnabled_ = true;  	completer_ = NULL;  	theme_ = theme; +	isCorrection_ = false;  	updateTitleWithUnreadCount();  	QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this); @@ -99,6 +100,7 @@ void QtChatWindow::setTabComplete(TabComplete* completer) {  void QtChatWindow::handleKeyPressEvent(QKeyEvent* event) {  	int key = event->key();  	Qt::KeyboardModifiers modifiers = event->modifiers(); +	QTextCursor cursor;  	if (key == Qt::Key_W && modifiers == Qt::ControlModifier) {  		close();  	} else if ( @@ -118,6 +120,17 @@ void QtChatWindow::handleKeyPressEvent(QKeyEvent* event) {  		emit requestActiveTab();  	} else if (key == Qt::Key_Tab) {  		tabComplete(); +	} else if ((key == Qt::Key_Up) && input_->toPlainText().isEmpty() && !(lastSentMessage_.isEmpty())) { +		cursor = input_->textCursor(); +		cursor.select(QTextCursor::Document); +		cursor.beginEditBlock(); +		cursor.insertText(QString(lastSentMessage_)); +		cursor.endEditBlock(); +		isCorrection_ = true; +	} else if (key == Qt::Key_Down && isCorrection_ && (cursor = input_->textCursor()).atBlockEnd()) { +		cursor.select(QTextCursor::Document); +		cursor.removeSelectedText(); +		isCorrection_ = false;  	} else {  		messageLog_->handleKeyPressEvent(event);  	} @@ -351,6 +364,15 @@ void QtChatWindow::addSystemMessage(const std::string& message) {  	previousMessageWasPresence_ = false;  } +void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { +	if (!id.empty()) { +		QString messageHTML(Qt::escape(P2QSTRING(message))); +		messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); +		messageHTML.replace("\n","<br/>"); +		messageLog_->replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time)); +	} +} +  void QtChatWindow::addPresenceMessage(const std::string& message) {  	if (isWidgetSelected()) {  		onAllMessagesRead(); @@ -372,7 +394,9 @@ void QtChatWindow::returnPressed() {  		return;  	}  	messageLog_->scrollToBottom(); -	onSendMessageRequest(Q2PSTRING(input_->toPlainText())); +	lastSentMessage_ = QString(input_->toPlainText()); +	onSendMessageRequest(Q2PSTRING(input_->toPlainText()), isCorrection_); +	isCorrection_ = false;  	inputClearing_ = true;  	input_->clear();  	inputClearing_ = false; diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index 9e3aeb3..fd6b315 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -36,6 +36,7 @@ namespace Swift {  			void addSystemMessage(const std::string& message);  			void addPresenceMessage(const std::string& message);  			void addErrorMessage(const std::string& errorMessage); +			void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time);  			void show();  			void activate();  			void setUnreadMessageCount(int count); @@ -82,6 +83,7 @@ namespace Swift {  			bool contactIsTyping_;  			LastLineTracker lastLineTracker_;  			QString contact_; +			QString lastSentMessage_;  			QtChatView* messageLog_;  			QtChatTheme* theme_;  			QtTextEdit* input_; @@ -89,6 +91,7 @@ namespace Swift {  			QtTreeWidget* treeWidget_;  			TabComplete* completer_;  			std::vector<SecurityLabelsCatalog::Item> availableLabels_; +			bool isCorrection_;  			bool previousMessageWasSelf_;  			bool previousMessageWasSystem_;  			bool previousMessageWasPresence_; diff --git a/Swift/QtUI/QtTextEdit.cpp b/Swift/QtUI/QtTextEdit.cpp index 79b3b8d..f7ac1ef 100644 --- a/Swift/QtUI/QtTextEdit.cpp +++ b/Swift/QtUI/QtTextEdit.cpp @@ -31,6 +31,8 @@ void QtTextEdit::keyPressEvent(QKeyEvent* event) {  			   || (key == Qt::Key_Tab && modifiers == Qt::ControlModifier)  			   || (key == Qt::Key_A && modifiers == Qt::AltModifier)  			   || (key == Qt::Key_Tab) +			   || (key == Qt::Key_Up) +			   || (key == Qt::Key_Down)  	) {  		emit unhandledKeyPressEvent(event);  	} diff --git a/Swiften/Elements/Message.h b/Swiften/Elements/Message.h index a553eb3..3623e73 100644 --- a/Swiften/Elements/Message.h +++ b/Swiften/Elements/Message.h @@ -14,6 +14,7 @@  #include "Swiften/Elements/Subject.h"  #include "Swiften/Elements/ErrorPayload.h"  #include "Swiften/Elements/Stanza.h" +#include "Swiften/Elements/Replace.h"  namespace Swift {  	class Message : public Stanza { diff --git a/Swiften/Elements/Replace.h b/Swiften/Elements/Replace.h new file mode 100644 index 0000000..dc8ff59 --- /dev/null +++ b/Swiften/Elements/Replace.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> + +#include "Swiften/Elements/Payload.h" + +namespace Swift { +	class Replace : public Payload { +		public: +			typedef boost::shared_ptr<Replace> ref; +			Replace(std::string id = "") : replaceID_(id) {}; +			std::string getId() { +				return replaceID_; +			} +			void setId(std::string id) { +				replaceID_ = id; +			} +		private: +			std::string replaceID_; +	}; +} diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp index 5052e67..55deafc 100644 --- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp +++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp @@ -40,6 +40,7 @@  #include "Swiften/Parser/PayloadParsers/DelayParserFactory.h"  #include "Swiften/Parser/PayloadParsers/MUCUserPayloadParserFactory.h"  #include "Swiften/Parser/PayloadParsers/NicknameParserFactory.h" +#include "Swiften/Parser/PayloadParsers/ReplaceParser.h"  using namespace boost; @@ -49,6 +50,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<IBBParser>("", "http://jabber.org/protocol/ibb")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<StatusShowParser>("show")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<StatusParser>("status"))); +	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<ReplaceParser>("replace", "http://swift.im/protocol/replace")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<BodyParser>("body")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<SubjectParser>("subject")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<PriorityParser>("priority"))); diff --git a/Swiften/Parser/PayloadParsers/ReplaceParser.cpp b/Swiften/Parser/PayloadParsers/ReplaceParser.cpp new file mode 100644 index 0000000..8e5659f --- /dev/null +++ b/Swiften/Parser/PayloadParsers/ReplaceParser.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "Swiften/Parser/PayloadParsers/ReplaceParser.h" + +namespace Swift { + +	ReplaceParser::ReplaceParser() : level_(0) { +	} + +	void ReplaceParser::handleStartElement(const std::string&, const std::string&, const AttributeMap& attributes) { +		if (level_ == 0) { +			std::string id = attributes.getAttribute("id"); +			getPayloadInternal()->setId(id); +		} +		level_++; +	} + +	void ReplaceParser::handleEndElement(const std::string&, const std::string&) { +		--level_; +	} + +	void ReplaceParser::handleCharacterData(const std::string&) { +	} + +} diff --git a/Swiften/Parser/PayloadParsers/ReplaceParser.h b/Swiften/Parser/PayloadParsers/ReplaceParser.h new file mode 100644 index 0000000..0789927 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/ReplaceParser.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Elements/Replace.h" +#include "Swiften/Parser/GenericPayloadParser.h" + +namespace Swift { +	class ReplaceParser : public GenericPayloadParser<Replace> { +		public: +			ReplaceParser(); +			virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes); +			virtual void handleEndElement(const std::string& element, const std::string&); +			virtual void handleCharacterData(const std::string& data); + +		private: +			int level_; +	}; +} diff --git a/Swiften/Parser/PayloadParsers/UnitTest/ReplaceTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/ReplaceTest.cpp new file mode 100644 index 0000000..7c34eb1 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/UnitTest/ReplaceTest.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Parser/PayloadParsers/ReplaceParser.h" +#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h" + +using namespace Swift; + +class ReplaceParserTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(ReplaceParserTest); +		CPPUNIT_TEST(testParseTrivial); +		CPPUNIT_TEST(testParseChild); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void testParseTrivial() { +			PayloadsParserTester parser; +			CPPUNIT_ASSERT(parser.parse("<replace id='bad1' xmlns='http://swift.im/protocol/replace'/>")); +			Replace::ref payload = boost::dynamic_pointer_cast <Replace>(parser.getPayload()); +			CPPUNIT_ASSERT_EQUAL(std::string("bad1"), payload->getId()); +		} +		void testParseChild() { +			PayloadsParserTester parser; +			CPPUNIT_ASSERT(parser.parse("<replace id='bad1' xmlns='http://swift.im/protocol/replace' ><child xmlns='blah' id=\"hi\"/></replace>")); +			Replace::ref payload = boost::dynamic_pointer_cast <Replace>(parser.getPayload()); +			CPPUNIT_ASSERT_EQUAL(std::string("bad1"), payload->getId()); +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ReplaceParserTest); diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript index e7e8991..d9915ed 100644 --- a/Swiften/Parser/SConscript +++ b/Swiften/Parser/SConscript @@ -53,6 +53,7 @@ sources = [  		"PayloadParsers/DelayParser.cpp",  		"PayloadParsers/MUCUserPayloadParser.cpp",  		"PayloadParsers/NicknameParser.cpp", +		"PayloadParsers/ReplaceParser.cpp",  		"PlatformXMLParserFactory.cpp",  		"PresenceParser.cpp",  		"SerializingParser.cpp", diff --git a/Swiften/SConscript b/Swiften/SConscript index b2dcdff..e474ae3 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -288,6 +288,7 @@ if env["SCONS_STAGE"] == "build" :  			File("Parser/PayloadParsers/UnitTest/StorageParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/PrivateStorageParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/VCardUpdateParserTest.cpp"), +			File("Parser/PayloadParsers/UnitTest/ReplaceTest.cpp"),  			File("Parser/UnitTest/AttributeMapTest.cpp"),  			File("Parser/UnitTest/IQParserTest.cpp"),  			File("Parser/UnitTest/MessageParserTest.cpp"), @@ -331,6 +332,7 @@ if env["SCONS_STAGE"] == "build" :  			File("Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp"), +			File("Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp"),  			File("Serializer/UnitTest/StreamFeaturesSerializerTest.cpp"),  			File("Serializer/UnitTest/AuthSuccessSerializerTest.cpp"),  			File("Serializer/UnitTest/AuthChallengeSerializerTest.cpp"), diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp index 0f05580..a1f412b 100644 --- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp +++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp @@ -41,6 +41,7 @@  #include "Swiften/Serializer/PayloadSerializers/InBandRegistrationPayloadSerializer.h"  #include "Swiften/Serializer/PayloadSerializers/NicknameSerializer.h"  #include "Swiften/Serializer/PayloadSerializers/SearchPayloadSerializer.h" +#include "Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h"  namespace Swift { @@ -79,6 +80,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {  	serializers_.push_back(new InBandRegistrationPayloadSerializer());  	serializers_.push_back(new NicknameSerializer());  	serializers_.push_back(new SearchPayloadSerializer()); +	serializers_.push_back(new ReplaceSerializer());  	foreach(PayloadSerializer* serializer, serializers_) {  		addSerializer(serializer);  	} diff --git a/Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h b/Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h new file mode 100644 index 0000000..303b2b8 --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Serializer/GenericPayloadSerializer.h" +#include "Swiften/Serializer/XML/XMLTextNode.h" +#include "Swiften/Elements/Replace.h" + +namespace Swift { +	class ReplaceSerializer : public GenericPayloadSerializer<Replace> { +		public: +			ReplaceSerializer() : GenericPayloadSerializer<Replace>() {} + +			virtual std::string serializePayload(boost::shared_ptr<Replace> replace) const { +				return "<replace id = '" + replace->getId() + "' xmlns='http://swift.im/protocol/replace'/>"; +			} +	}; +} diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp new file mode 100644 index 0000000..8fe96e5 --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h" + +using namespace Swift; + +class ReplaceSerializerTest: public CppUnit::TestFixture { +	CPPUNIT_TEST_SUITE(ReplaceSerializerTest); +	CPPUNIT_TEST(testSerialize); +	CPPUNIT_TEST_SUITE_END(); + +	public: +		ReplaceSerializerTest() {} + +		void testSerialize() { +			ReplaceSerializer testling; +			boost::shared_ptr<Replace> replace(new Replace()); +			replace->setId("bad1"); +			CPPUNIT_ASSERT_EQUAL(std::string("<replace id = 'bad1' xmlns='http://swift.im/protocol/replace'/>"), testling.serialize(replace)); +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ReplaceSerializerTest); | 
 Swift
 Swift