diff options
Diffstat (limited to 'Swift')
| -rw-r--r-- | Swift/Controllers/Chat/ChatController.cpp | 34 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatController.h | 1 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.cpp | 16 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.h | 7 | ||||
| -rw-r--r-- | Swift/Controllers/UIInterfaces/ChatWindow.h | 5 | ||||
| -rw-r--r-- | Swift/Controllers/UnitTest/MockChatWindow.h | 3 | ||||
| -rw-r--r-- | Swift/QtUI/ChatSnippet.h | 7 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatView.cpp | 19 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatView.h | 2 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 26 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.h | 3 | ||||
| -rw-r--r-- | Swift/QtUI/QtTextEdit.cpp | 2 | 
12 files changed, 108 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);  	} | 
 Swift
 Swift