summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swift/Controllers/Chat/ChatController.cpp')
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp224
1 files changed, 199 insertions, 25 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index 5a18a98..0b34681 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -1,9 +1,9 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swift/Controllers/Chat/ChatController.h"
+#include <Swift/Controllers/Chat/ChatController.h>
#include <boost/bind.hpp>
@@ -11,25 +11,37 @@
#include <stdio.h>
-#include <Swift/Controllers/Intl.h>
-#include <Swiften/Base/format.h>
-#include <Swiften/Base/Algorithm.h>
#include <Swiften/Avatars/AvatarManager.h>
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/DateTime.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/format.h>
+#include <Swiften/Base/Log.h>
#include <Swiften/Chat/ChatStateNotifier.h>
#include <Swiften/Chat/ChatStateTracker.h>
+#include <Swiften/Client/ClientBlockListManager.h>
+#include <Swiften/Client/NickResolver.h>
#include <Swiften/Client/StanzaChannel.h>
+#include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swiften/Elements/DeliveryReceipt.h>
+#include <Swiften/Elements/DeliveryReceiptRequest.h>
+#include <Swiften/Elements/Idle.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
+
+#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
-#include <Swiften/Client/NickResolver.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/FileTransfer/FileTransferController.h>
#include <Swift/Controllers/StatusUtil.h>
-#include <Swiften/Disco/EntityCapsProvider.h>
-#include <Swiften/Base/foreach.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
-#include <Swiften/Elements/DeliveryReceipt.h>
-#include <Swiften/Elements/DeliveryReceiptRequest.h>
+#include <Swift/Controllers/UIEvents/AcceptWhiteboardSessionUIEvent.h>
+#include <Swift/Controllers/UIEvents/CancelWhiteboardSessionUIEvent.h>
+#include <Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h>
+#include <Swift/Controllers/UIEvents/InviteToMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestInviteToMUCUIEvent.h>
#include <Swift/Controllers/SettingConstants.h>
-
-#include <Swiften/Base/Log.h>
+#include <Swift/Controllers/Highlighter.h>
+#include <Swift/Controllers/Chat/ChatMessageParser.h>
namespace Swift {
@@ -38,6 +50,6 @@ namespace Swift {
* The controller does not gain ownership of the stanzaChannel, nor the factory.
*/
-ChatController::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, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings)
- : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider), eventStream_(eventStream), userWantsReceipts_(userWantsReceipts), settings_(settings) {
+ChatController::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, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, boost::shared_ptr<ChatMessageParser> chatMessageParser, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider)
+ : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider), eventStream_(eventStream), userWantsReceipts_(userWantsReceipts), settings_(settings), clientBlockListManager_(clientBlockListManager) {
isInMUC_ = isInMUC;
lastWasPresence_ = false;
@@ -60,4 +72,8 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
theirPresence = contact.isBare() ? presenceOracle->getHighestPriorityPresence(contact.toBare()) : presenceOracle->getLastPresence(contact);
}
+ Idle::ref idle;
+ if (theirPresence && (idle = theirPresence->getPayload<Idle>())) {
+ startMessage += str(format(QT_TRANSLATE_NOOP("", ", who has been idle since %1%")) % dateTimeToLocalString(idle->getSince()));
+ }
startMessage += ": " + statusShowTypeToFriendlyName(theirPresence ? theirPresence->getShow() : StatusShow::None);
if (theirPresence && !theirPresence->getStatus().empty()) {
@@ -67,5 +83,5 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
chatStateNotifier_->setContactIsOnline(theirPresence && theirPresence->getType() == Presence::Available);
startMessage += ".";
- chatWindow_->addSystemMessage(startMessage);
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(startMessage), ChatWindow::DefaultDirection);
chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
@@ -74,7 +90,15 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
chatWindow_->onFileTransferCancel.connect(boost::bind(&ChatController::handleFileTransferCancel, this, _1));
chatWindow_->onSendFileRequest.connect(boost::bind(&ChatController::handleSendFileRequest, this, _1));
+ chatWindow_->onWhiteboardSessionAccept.connect(boost::bind(&ChatController::handleWhiteboardSessionAccept, this));
+ chatWindow_->onWhiteboardSessionCancel.connect(boost::bind(&ChatController::handleWhiteboardSessionCancel, this));
+ chatWindow_->onWhiteboardWindowShow.connect(boost::bind(&ChatController::handleWhiteboardWindowShow, this));
+ chatWindow_->onBlockUserRequest.connect(boost::bind(&ChatController::handleBlockUserRequest, this));
+ chatWindow_->onUnblockUserRequest.connect(boost::bind(&ChatController::handleUnblockUserRequest, this));
+ chatWindow_->onInviteToChat.connect(boost::bind(&ChatController::handleInviteToChat, this, _1));
+ chatWindow_->onClosed.connect(boost::bind(&ChatController::handleWindowClosed, this));
handleBareJIDCapsChanged(toJID_);
settings_->onSettingChanged.connect(boost::bind(&ChatController::handleSettingChanged, this, _1));
+ eventStream_->onUIEvent.connect(boost::bind(&ChatController::handleUIEvent, this, _1));
}
@@ -86,4 +110,5 @@ void ChatController::handleContactNickChanged(const JID& jid, const std::string&
ChatController::~ChatController() {
+ eventStream_->onUIEvent.disconnect(boost::bind(&ChatController::handleUIEvent, this, _1));
settings_->onSettingChanged.disconnect(boost::bind(&ChatController::handleSettingChanged, this, _1));
nickResolver_->onNickChanged.disconnect(boost::bind(&ChatController::handleContactNickChanged, this, _1, _2));
@@ -92,4 +117,8 @@ ChatController::~ChatController() {
}
+JID ChatController::getBaseJID() {
+ return isInMUC_ ? toJID_ : ChatControllerBase::getBaseJID();
+}
+
void ChatController::cancelReplaces() {
lastWasPresence_ = false;
@@ -109,4 +138,9 @@ void ChatController::handleBareJIDCapsChanged(const JID& /*jid*/) {
contactSupportsReceipts_ = ChatWindow::No;
}
+ if (FileTransferManager::isSupportedBy(disco)) {
+ chatWindow_->setFileTransferEnabled(ChatWindow::Yes);
+ } else {
+ chatWindow_->setFileTransferEnabled(ChatWindow::No);
+ }
} else {
SWIFT_LOG(debug) << "No disco info :(" << std::endl;
@@ -130,4 +164,17 @@ void ChatController::setToJID(const JID& jid) {
}
+void ChatController::setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info) {
+ ChatControllerBase::setAvailableServerFeatures(info);
+ if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::BlockingCommandFeature)) {
+ boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList();
+
+ blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&ChatController::handleBlockingStateChanged, this));
+ blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&ChatController::handleBlockingStateChanged, this));
+ blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&ChatController::handleBlockingStateChanged, this));
+
+ handleBlockingStateChanged();
+ }
+}
+
bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) {
return false;
@@ -147,6 +194,8 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me
}
chatStateTracker_->handleMessageReceived(message);
- chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>());
+ chatStateNotifier_->receivedMessageFromContact(!!message->getPayload<ChatState>());
+ // handle XEP-0184 Message Receipts
+ // incomming receipts
if (boost::shared_ptr<DeliveryReceipt> receipt = message->getPayload<DeliveryReceipt>()) {
SWIFT_LOG(debug) << "received receipt for id: " << receipt->getReceivedID() << std::endl;
@@ -155,4 +204,11 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me
requestedReceipts_.erase(receipt->getReceivedID());
}
+ // incomming errors in response to send out receipts
+ } else if (message->getPayload<DeliveryReceiptRequest>() && (message->getType() == Message::Error)) {
+ if (requestedReceipts_.find(message->getID()) != requestedReceipts_.end()) {
+ chatWindow_->setMessageReceiptState(requestedReceipts_[message->getID()], ChatWindow::ReceiptFailed);
+ requestedReceipts_.erase(message->getID());
+ }
+ // incoming receipt requests
} else if (message->getPayload<DeliveryReceiptRequest>()) {
if (receivingPresenceFromUs_) {
@@ -165,6 +221,9 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me
}
-void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) {
+void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction& highlight) {
eventController_->handleIncomingEvent(messageEvent);
+ if (!messageEvent->getConcluded()) {
+ highlighter_->handleHighlightAction(highlight);
+ }
}
@@ -189,20 +248,90 @@ void ChatController::handleSettingChanged(const std::string& settingPath) {
void ChatController::checkForDisplayingDisplayReceiptsAlert() {
+ boost::optional<ChatWindow::AlertID> newDeliverReceiptAlert;
if (userWantsReceipts_ && (contactSupportsReceipts_ == ChatWindow::No)) {
- chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "This chat doesn't support delivery receipts."));
+ newDeliverReceiptAlert = chatWindow_->addAlert(QT_TRANSLATE_NOOP("", "This chat doesn't support delivery receipts."));
} else if (userWantsReceipts_ && (contactSupportsReceipts_ == ChatWindow::Maybe)) {
- chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent."));
+ newDeliverReceiptAlert = chatWindow_->addAlert(QT_TRANSLATE_NOOP("", "This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent."));
+ } else {
+ if (deliveryReceiptAlert_) {
+ chatWindow_->removeAlert(*deliveryReceiptAlert_);
+ deliveryReceiptAlert_.reset();
+ }
+ }
+ if (newDeliverReceiptAlert) {
+ if (deliveryReceiptAlert_) {
+ chatWindow_->removeAlert(*deliveryReceiptAlert_);
+ }
+ deliveryReceiptAlert_ = newDeliverReceiptAlert;
+ }
+}
+
+void ChatController::handleBlockingStateChanged() {
+ boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList();
+ if (blockList->getState() == BlockList::Available) {
+ if (isInMUC_ ? blockList->isBlocked(toJID_) : blockList->isBlocked(toJID_.toBare())) {
+ if (!blockedContactAlert_) {
+ blockedContactAlert_ = chatWindow_->addAlert(QT_TRANSLATE_NOOP("", "You've currently blocked this contact. To continue your conversation you have to unblock the contact first."));
+ }
+ chatWindow_->setInputEnabled(false);
+ chatWindow_->setBlockingState(ChatWindow::IsBlocked);
+
+ // disconnect typing events to prevent chat state notifciations to blocked contacts
+ chatWindow_->onUserTyping.disconnect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
+ chatWindow_->onUserCancelsTyping.disconnect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
+ } else {
+ if (blockedContactAlert_) {
+ chatWindow_->removeAlert(*blockedContactAlert_);
+ blockedContactAlert_.reset();
+ }
+ chatWindow_->setInputEnabled(true);
+ chatWindow_->setBlockingState(ChatWindow::IsUnblocked);
+
+ chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
+ chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
+ }
+ }
+}
+
+void ChatController::handleBlockUserRequest() {
+ if (isInMUC_) {
+ eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, toJID_));
} else {
- chatWindow_->cancelAlert();
+ eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, toJID_.toBare()));
+ }
+}
+
+void ChatController::handleUnblockUserRequest() {
+ if (isInMUC_) {
+ eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Unblocked, toJID_));
+ } else {
+ eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Unblocked, toJID_.toBare()));
+ }
+}
+
+void ChatController::handleInviteToChat(const std::vector<JID>& droppedJIDs) {
+ boost::shared_ptr<UIEvent> event(new RequestInviteToMUCUIEvent(toJID_.toBare(), droppedJIDs, RequestInviteToMUCUIEvent::Impromptu));
+ eventStream_->send(event);
+}
+
+void ChatController::handleWindowClosed() {
+ onWindowClosed();
+}
+
+void ChatController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
+ boost::shared_ptr<InviteToMUCUIEvent> inviteEvent = boost::dynamic_pointer_cast<InviteToMUCUIEvent>(event);
+ if (inviteEvent && inviteEvent->getRoom() == toJID_.toBare()) {
+ onConvertToMUC(detachChatWindow(), inviteEvent->getInvites(), inviteEvent->getReason());
}
}
+
void ChatController::postSendMessage(const std::string& body, boost::shared_ptr<Stanza> sentStanza) {
boost::shared_ptr<Replace> replace = sentStanza->getPayload<Replace>();
if (replace) {
eraseIf(unackedStanzas_, PairSecondEquals<boost::shared_ptr<Stanza>, std::string>(myLastMessageUIID_));
- replaceMessage(body, myLastMessageUIID_, boost::posix_time::microsec_clock::universal_time());
+ replaceMessage(body, myLastMessageUIID_, true, boost::posix_time::microsec_clock::universal_time(), HighlightAction());
} 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());
+ myLastMessageUIID_ = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), avatarManager_->getAvatarPath(selfJID_), boost::posix_time::microsec_clock::universal_time(), HighlightAction());
}
@@ -252,4 +381,12 @@ void ChatController::handleNewFileTransferController(FileTransferController* ftc
}
+void ChatController::handleWhiteboardSessionRequest(bool senderIsSelf) {
+ lastWbID_ = chatWindow_->addWhiteboardRequest(senderIsSelf);
+}
+
+void ChatController::handleWhiteboardStateChange(const ChatWindow::WhiteboardSessionState state) {
+ chatWindow_->setWhiteboardSessionStatus(lastWbID_, state);
+}
+
void ChatController::handleFileTransferCancel(std::string id) {
SWIFT_LOG(debug) << "handleFileTransferCancel(" << id << ")" << std::endl;
@@ -271,5 +408,5 @@ void ChatController::handleFileTransferStart(std::string id, std::string descrip
void ChatController::handleFileTransferAccept(std::string id, std::string filename) {
- SWIFT_LOG(debug) "handleFileTransferAccept(" << id << ", " << filename << ")" << std::endl;
+ SWIFT_LOG(debug) << "handleFileTransferAccept(" << id << ", " << filename << ")" << std::endl;
if (ftControllers.find(id) != ftControllers.end()) {
ftControllers[id]->accept(filename);
@@ -284,4 +421,16 @@ void ChatController::handleSendFileRequest(std::string filename) {
}
+void ChatController::handleWhiteboardSessionAccept() {
+ eventStream_->send(boost::make_shared<AcceptWhiteboardSessionUIEvent>(toJID_));
+}
+
+void ChatController::handleWhiteboardSessionCancel() {
+ eventStream_->send(boost::make_shared<CancelWhiteboardSessionUIEvent>(toJID_));
+}
+
+void ChatController::handleWhiteboardWindowShow() {
+ eventStream_->send(boost::make_shared<ShowWhiteboardUIEvent>(toJID_));
+}
+
std::string ChatController::senderDisplayNameFromMessage(const JID& from) {
return nickResolver_->jidToNick(from);
@@ -303,4 +452,9 @@ std::string ChatController::getStatusChangeString(boost::shared_ptr<Presence> pr
}
}
+ Idle::ref idle;
+ if ((idle = presence->getPayload<Idle>())) {
+ response += str(format(QT_TRANSLATE_NOOP("", " and has been idle since %1%")) % dateTimeToLocalString(idle->getSince()));
+ }
+
if (!response.empty()) {
response = str(format(response) % nick);
@@ -337,7 +491,7 @@ void ChatController::handlePresenceChange(boost::shared_ptr<Presence> newPresenc
if (newStatusChangeString != lastStatusChangeString_) {
if (lastWasPresence_) {
- chatWindow_->replaceLastMessage(newStatusChangeString);
+ chatWindow_->replaceLastMessage(chatMessageParser_->parseMessageBody(newStatusChangeString), ChatWindow::UpdateTimestamp);
} else {
- chatWindow_->addPresenceMessage(newStatusChangeString);
+ chatWindow_->addPresenceMessage(chatMessageParser_->parseMessageBody(newStatusChangeString), ChatWindow::DefaultDirection);
}
lastStatusChangeString_ = newStatusChangeString;
@@ -350,3 +504,23 @@ boost::optional<boost::posix_time::ptime> ChatController::getMessageTimestamp(bo
}
+void ChatController::logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool /* isIncoming */) {
+ HistoryMessage::Type type;
+ if (mucRegistry_->isMUC(fromJID.toBare()) || mucRegistry_->isMUC(toJID.toBare())) {
+ type = HistoryMessage::PrivateMessage;
+ }
+ else {
+ type = HistoryMessage::Chat;
+ }
+
+ if (historyController_) {
+ historyController_->addMessage(message, fromJID, toJID, type, timeStamp);
+ }
+}
+
+ChatWindow* ChatController::detachChatWindow() {
+ chatWindow_->onUserTyping.disconnect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
+ chatWindow_->onUserCancelsTyping.disconnect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
+ return ChatControllerBase::detachChatWindow();
+}
+
}