summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swift/Controllers')
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp20
-rw-r--r--Swift/Controllers/Roster/RosterController.cpp2
-rw-r--r--Swift/Controllers/UIInterfaces/ChatWindow.h1
-rw-r--r--Swift/Controllers/UnitTest/MockChatWindow.h1
4 files changed, 16 insertions, 8 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index 10cf54b..65d65a6 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -1,508 +1,514 @@
/*
* 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 <boost/bind.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <stdio.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/StanzaChannel.h>
+#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/NickResolver.h>
+#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Disco/EntityCapsProvider.h>
-#include <Swiften/Base/foreach.h>
-#include <Swiften/Base/DateTime.h>
#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Elements/DeliveryReceiptRequest.h>
#include <Swiften/Elements/Idle.h>
-#include <Swiften/Base/Log.h>
-#include <Swiften/Client/ClientBlockListManager.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/FileTransfer/FileTransferController.h>
#include <Swift/Controllers/StatusUtil.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UIEvents/SendFileUIEvent.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 <Swift/Controllers/Highlighter.h>
#include <Swift/Controllers/Chat/ChatMessageParser.h>
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, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, 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;
chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider);
chatStateTracker_ = new ChatStateTracker();
nickResolver_ = nickResolver;
presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1));
chatStateTracker_->onChatStateChange.connect(boost::bind(&ChatWindow::setContactChatState, chatWindow_, _1));
stanzaChannel_->onStanzaAcked.connect(boost::bind(&ChatController::handleStanzaAcked, this, _1));
nickResolver_->onNickChanged.connect(boost::bind(&ChatController::handleContactNickChanged, this, _1, _2));
std::string nick = nickResolver_->jidToNick(toJID_);
chatWindow_->setName(nick);
std::string startMessage;
Presence::ref theirPresence;
if (isInMUC) {
startMessage = str(format(QT_TRANSLATE_NOOP("", "Starting chat with %1% in chatroom %2%")) % nick % contact.toBare().toString());
theirPresence = presenceOracle->getLastPresence(contact);
} else {
startMessage = str(format(QT_TRANSLATE_NOOP("", "Starting chat with %1% - %2%")) % nick % contact.toBare().toString());
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()) {
startMessage += " (" + theirPresence->getStatus() + ")";
}
lastShownStatus_ = theirPresence ? theirPresence->getShow() : StatusShow::None;
chatStateNotifier_->setContactIsOnline(theirPresence && theirPresence->getType() == Presence::Available);
startMessage += ".";
chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(startMessage), ChatWindow::DefaultDirection);
chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
chatWindow_->onFileTransferStart.connect(boost::bind(&ChatController::handleFileTransferStart, this, _1, _2));
chatWindow_->onFileTransferAccept.connect(boost::bind(&ChatController::handleFileTransferAccept, this, _1, _2));
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));
handleBareJIDCapsChanged(toJID_);
settings_->onSettingChanged.connect(boost::bind(&ChatController::handleSettingChanged, this, _1));
eventStream_->onUIEvent.connect(boost::bind(&ChatController::handleUIEvent, this, _1));
}
void ChatController::handleContactNickChanged(const JID& jid, const std::string& /*oldNick*/) {
if (jid.toBare() == toJID_.toBare()) {
chatWindow_->setName(nickResolver_->jidToNick(jid));
}
}
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));
delete chatStateNotifier_;
delete chatStateTracker_;
}
JID ChatController::getBaseJID() {
return isInMUC_ ? toJID_ : ChatControllerBase::getBaseJID();
}
void ChatController::cancelReplaces() {
lastWasPresence_ = false;
}
void ChatController::handleBareJIDCapsChanged(const JID& /*jid*/) {
DiscoInfo::ref disco = entityCapsProvider_->getCaps(toJID_);
if (disco) {
if (disco->hasFeature(DiscoInfo::MessageCorrectionFeature)) {
chatWindow_->setCorrectionEnabled(ChatWindow::Yes);
} else {
chatWindow_->setCorrectionEnabled(ChatWindow::No);
}
if (disco->hasFeature(DiscoInfo::MessageDeliveryReceiptsFeature)) {
contactSupportsReceipts_ = ChatWindow::Yes;
} else {
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;
chatWindow_->setCorrectionEnabled(ChatWindow::Maybe);
contactSupportsReceipts_ = ChatWindow::Maybe;
}
checkForDisplayingDisplayReceiptsAlert();
}
void ChatController::setToJID(const JID& jid) {
chatStateNotifier_->setContact(jid);
ChatControllerBase::setToJID(jid);
Presence::ref presence;
if (isInMUC_) {
presence = presenceOracle_->getLastPresence(jid);
} else {
presence = jid.isBare() ? presenceOracle_->getHighestPriorityPresence(jid.toBare()) : presenceOracle_->getLastPresence(jid);
}
chatStateNotifier_->setContactIsOnline(presence && presence->getType() == Presence::Available);
handleBareJIDCapsChanged(toJID_);
}
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::handleBlockingItemAdded, this, _1));
blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&ChatController::handleBlockingItemRemoved, this, _1));
handleBlockingStateChanged();
}
}
bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) {
return false;
}
void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) {
if (messageEvent->isReadable()) {
chatWindow_->flash();
lastWasPresence_ = false;
}
boost::shared_ptr<Message> message = messageEvent->getStanza();
JID from = message->getFrom();
if (!from.equals(toJID_, JID::WithResource)) {
if (toJID_.equals(from, JID::WithoutResource) && toJID_.isBare()){
setToJID(from);
}
}
chatStateTracker_->handleMessageReceived(message);
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;
if (requestedReceipts_.find(receipt->getReceivedID()) != requestedReceipts_.end()) {
chatWindow_->setMessageReceiptState(requestedReceipts_[receipt->getReceivedID()], ChatWindow::ReceiptReceived);
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_) {
boost::shared_ptr<Message> receiptMessage = boost::make_shared<Message>();
receiptMessage->setTo(toJID_);
receiptMessage->addPayload(boost::make_shared<DeliveryReceipt>(message->getID()));
stanzaChannel_->sendMessage(receiptMessage);
}
}
}
void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction& highlight) {
eventController_->handleIncomingEvent(messageEvent);
if (!messageEvent->getConcluded()) {
highlighter_->handleHighlightAction(highlight);
}
}
void ChatController::preSendMessageRequest(boost::shared_ptr<Message> message) {
chatStateNotifier_->addChatStateRequest(message);
if (userWantsReceipts_ && (contactSupportsReceipts_ != ChatWindow::No) && message) {
message->addPayload(boost::make_shared<DeliveryReceiptRequest>());
}
}
void ChatController::setContactIsReceivingPresence(bool isReceivingPresence) {
receivingPresenceFromUs_ = isReceivingPresence;
}
void ChatController::handleSettingChanged(const std::string& settingPath) {
if (settingPath == SettingConstants::REQUEST_DELIVERYRECEIPTS.getKey()) {
userWantsReceipts_ = settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS);
checkForDisplayingDisplayReceiptsAlert();
}
}
void ChatController::checkForDisplayingDisplayReceiptsAlert() {
if (userWantsReceipts_ && (contactSupportsReceipts_ == ChatWindow::No)) {
chatWindow_->setAlert(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."));
} else {
chatWindow_->cancelAlert();
}
}
void ChatController::handleBlockingStateChanged() {
boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList();
if (blockList->getState() == BlockList::Available) {
if (isInMUC_ ? blockList->isBlocked(toJID_) : blockList->isBlocked(toJID_.toBare())) {
chatWindow_->setAlert(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);
} else {
chatWindow_->setBlockingState(ChatWindow::IsUnblocked);
}
}
}
void ChatController::handleBlockingItemAdded(const JID& jid) {
if (toJID_ == (isInMUC_ ? jid: jid.toBare())) {
chatWindow_->setAlert(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);
}
}
void ChatController::handleBlockingItemRemoved(const JID& jid) {
if (toJID_ == (isInMUC_ ? jid: jid.toBare())) {
// FIXME: Support for different types of alerts.
chatWindow_->cancelAlert();
chatWindow_->setInputEnabled(true);
chatWindow_->setBlockingState(ChatWindow::IsUnblocked);
}
}
void ChatController::handleBlockUserRequest() {
if (isInMUC_) {
eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, toJID_));
} else {
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::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(), HighlightAction());
} else {
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());
}
if (stanzaChannel_->getStreamManagementEnabled() && !myLastMessageUIID_.empty() ) {
chatWindow_->setAckState(myLastMessageUIID_, ChatWindow::Pending);
unackedStanzas_[sentStanza] = myLastMessageUIID_;
}
if (sentStanza->getPayload<DeliveryReceiptRequest>()) {
requestedReceipts_[sentStanza->getID()] = myLastMessageUIID_;
chatWindow_->setMessageReceiptState(myLastMessageUIID_, ChatWindow::ReceiptRequested);
}
lastWasPresence_ = false;
chatStateNotifier_->userSentMessage();
}
void ChatController::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) {
std::map<boost::shared_ptr<Stanza>, std::string>::iterator unackedStanza = unackedStanzas_.find(stanza);
if (unackedStanza != unackedStanzas_.end()) {
chatWindow_->setAckState(unackedStanza->second, ChatWindow::Received);
unackedStanzas_.erase(unackedStanza);
}
}
void ChatController::setOnline(bool online) {
if (!online) {
std::map<boost::shared_ptr<Stanza>, std::string>::iterator it = unackedStanzas_.begin();
for ( ; it != unackedStanzas_.end(); ++it) {
chatWindow_->setAckState(it->second, ChatWindow::Failed);
}
unackedStanzas_.clear();
Presence::ref fakeOffline(new Presence());
fakeOffline->setFrom(toJID_);
fakeOffline->setType(Presence::Unavailable);
chatStateTracker_->handlePresenceChange(fakeOffline);
}
ChatControllerBase::setOnline(online);
}
void ChatController::handleNewFileTransferController(FileTransferController* ftc) {
std::string nick = senderDisplayNameFromMessage(ftc->getOtherParty());
std::string ftID = ftc->setChatWindow(chatWindow_, nick);
ftControllers[ftID] = 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;
if (ftControllers.find(id) != ftControllers.end()) {
ftControllers[id]->cancel();
} else {
std::cerr << "unknown file transfer UI id" << std::endl;
}
}
void ChatController::handleFileTransferStart(std::string id, std::string description) {
SWIFT_LOG(debug) << "handleFileTransferStart(" << id << ", " << description << ")" << std::endl;
if (ftControllers.find(id) != ftControllers.end()) {
ftControllers[id]->start(description);
} else {
std::cerr << "unknown file transfer UI id" << std::endl;
}
}
void ChatController::handleFileTransferAccept(std::string id, std::string filename) {
SWIFT_LOG(debug) << "handleFileTransferAccept(" << id << ", " << filename << ")" << std::endl;
if (ftControllers.find(id) != ftControllers.end()) {
ftControllers[id]->accept(filename);
} else {
std::cerr << "unknown file transfer UI id" << std::endl;
}
}
void ChatController::handleSendFileRequest(std::string filename) {
SWIFT_LOG(debug) << "ChatController::handleSendFileRequest(" << filename << ")" << std::endl;
eventStream_->send(boost::make_shared<SendFileUIEvent>(getToJID(), 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);
}
std::string ChatController::getStatusChangeString(boost::shared_ptr<Presence> presence) {
std::string nick = senderDisplayNameFromMessage(presence->getFrom());
std::string response;
if (!presence || presence->getType() == Presence::Unavailable || presence->getType() == Presence::Error) {
response = QT_TRANSLATE_NOOP("", "%1% has gone offline");
} else if (presence->getType() == Presence::Available) {
StatusShow::Type show = presence->getShow();
if (show == StatusShow::Online || show == StatusShow::FFC) {
response = QT_TRANSLATE_NOOP("", "%1% has become available");
} else if (show == StatusShow::Away || show == StatusShow::XA) {
response = QT_TRANSLATE_NOOP("", "%1% has gone away");
} else if (show == StatusShow::DND) {
response = QT_TRANSLATE_NOOP("", "%1% is now busy");
}
}
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);
}
if (!presence->getStatus().empty()) {
response += " (" + presence->getStatus() + ")";
}
return response + ".";
}
void ChatController::handlePresenceChange(boost::shared_ptr<Presence> newPresence) {
bool me = false;
if (toJID_.isBare()) {
newPresence = presenceOracle_->getHighestPriorityPresence(toJID_);
if ((newPresence ? newPresence->getShow() : StatusShow::None) != lastShownStatus_) {
me = true;
}
} else if (toJID_.equals(newPresence->getFrom(), JID::WithResource)) {
me = true;
}
if (!me) {
return;
}
if (!newPresence) {
newPresence = boost::make_shared<Presence>();
newPresence->setType(Presence::Unavailable);
}
lastShownStatus_ = newPresence->getShow();
chatStateTracker_->handlePresenceChange(newPresence);
chatStateNotifier_->setContactIsOnline(newPresence->getType() == Presence::Available);
std::string newStatusChangeString = getStatusChangeString(newPresence);
if (newStatusChangeString != lastStatusChangeString_) {
if (lastWasPresence_) {
chatWindow_->replaceLastMessage(chatMessageParser_->parseMessageBody(newStatusChangeString));
} else {
chatWindow_->addPresenceMessage(chatMessageParser_->parseMessageBody(newStatusChangeString), ChatWindow::DefaultDirection);
}
lastStatusChangeString_ = newStatusChangeString;
lastWasPresence_ = true;
}
}
boost::optional<boost::posix_time::ptime> ChatController::getMessageTimestamp(boost::shared_ptr<Message> message) const {
return message->getTimestamp();
}
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();
}
}
diff --git a/Swift/Controllers/Roster/RosterController.cpp b/Swift/Controllers/Roster/RosterController.cpp
index 4016f81..d2d024c 100644
--- a/Swift/Controllers/Roster/RosterController.cpp
+++ b/Swift/Controllers/Roster/RosterController.cpp
@@ -1,369 +1,369 @@
/*
* Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <Swift/Controllers/Roster/RosterController.h>
#include <boost/bind.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <Swiften/Base/foreach.h>
#include <Swiften/Base/format.h>
#include <Swiften/Base/Path.h>
#include <Swiften/Client/ClientBlockListManager.h>
#include <Swiften/Client/NickManager.h>
#include <Swiften/Client/NickResolver.h>
#include <Swiften/Disco/EntityCapsManager.h>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/FileTransfer/FileTransferManager.h>
#include <Swiften/JID/JID.h>
#include <Swiften/Jingle/JingleSessionManager.h>
#include <Swiften/Presence/PresenceOracle.h>
#include <Swiften/Presence/SubscriptionManager.h>
#include <Swiften/Queries/IQRouter.h>
#include <Swiften/Roster/GetRosterRequest.h>
#include <Swiften/Roster/SetRosterRequest.h>
#include <Swiften/Roster/XMPPRoster.h>
#include <Swiften/Roster/XMPPRosterItem.h>
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/Roster/GroupRosterItem.h>
#include <Swift/Controllers/Roster/OfflineRosterFilter.h>
#include <Swift/Controllers/Roster/Roster.h>
#include <Swift/Controllers/Roster/RosterVCardProvider.h>
#include <Swift/Controllers/Roster/ItemOperations/AppearOffline.h>
#include <Swift/Controllers/Roster/ItemOperations/SetAvatar.h>
#include <Swift/Controllers/Roster/ItemOperations/SetAvailableFeatures.h>
#include <Swift/Controllers/Roster/ItemOperations/SetBlockingState.h>
#include <Swift/Controllers/Roster/ItemOperations/SetName.h>
#include <Swift/Controllers/Roster/ItemOperations/SetPresence.h>
#include <Swift/Controllers/Roster/ItemOperations/SetVCard.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/UIEvents/AddContactUIEvent.h>
#include <Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h>
#include <Swift/Controllers/UIEvents/RenameGroupUIEvent.h>
#include <Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h>
#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIInterfaces/MainWindow.h>
#include <Swift/Controllers/UIInterfaces/MainWindowFactory.h>
#include <Swift/Controllers/XMPPEvents/ErrorEvent.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h>
namespace Swift {
/**
* The controller does not gain ownership of these parameters.
*/
RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, FileTransferOverview* fileTransferOverview, ClientBlockListManager* clientBlockListManager, VCardManager* vcardManager)
: myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview), clientBlockListManager_(clientBlockListManager) {
assert(fileTransferOverview);
iqRouter_ = iqRouter;
presenceOracle_ = presenceOracle;
subscriptionManager_ = subscriptionManager;
eventController_ = eventController;
settings_ = settings;
expandiness_ = new RosterGroupExpandinessPersister(roster_, settings);
mainWindow_->setRosterModel(roster_);
rosterVCardProvider_ = new RosterVCardProvider(roster_, vcardManager, JID::WithoutResource);
changeStatusConnection_ = mainWindow_->onChangeStatusRequest.connect(boost::bind(&RosterController::handleChangeStatusRequest, this, _1, _2));
signOutConnection_ = mainWindow_->onSignOutRequest.connect(boost::bind(boost::ref(onSignOutRequest)));
xmppRoster_->onJIDAdded.connect(boost::bind(&RosterController::handleOnJIDAdded, this, _1));
xmppRoster_->onJIDUpdated.connect(boost::bind(&RosterController::handleOnJIDUpdated, this, _1, _2, _3));
xmppRoster_->onJIDRemoved.connect(boost::bind(&RosterController::handleOnJIDRemoved, this, _1));
xmppRoster_->onRosterCleared.connect(boost::bind(&RosterController::handleRosterCleared, this));
subscriptionManager_->onPresenceSubscriptionRequest.connect(boost::bind(&RosterController::handleSubscriptionRequest, this, _1, _2));
presenceOracle_->onPresenceChange.connect(boost::bind(&RosterController::handleIncomingPresence, this, _1));
uiEventConnection_ = uiEventStream->onUIEvent.connect(boost::bind(&RosterController::handleUIEvent, this, _1));
avatarManager_ = avatarManager;
avatarManager_->onAvatarChanged.connect(boost::bind(&RosterController::handleAvatarChanged, this, _1));
mainWindow_->setMyAvatarPath(pathToString(avatarManager_->getAvatarPath(myJID_)));
nickManager_->onOwnNickChanged.connect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1));
mainWindow_->setMyJID(jid);
mainWindow_->setMyNick(nickManager_->getOwnNick());
entityCapsManager_->onCapsChanged.connect(boost::bind(&RosterController::handleOnCapsChanged, this, _1));
settings_->onSettingChanged.connect(boost::bind(&RosterController::handleSettingChanged, this, _1));
handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE));
}
RosterController::~RosterController() {
settings_->onSettingChanged.disconnect(boost::bind(&RosterController::handleSettingChanged, this, _1));
nickManager_->onOwnNickChanged.disconnect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1));
delete offlineFilter_;
delete expandiness_;
mainWindow_->setRosterModel(NULL);
if (mainWindow_->canDelete()) {
delete mainWindow_;
}
delete rosterVCardProvider_;
delete roster_;
}
void RosterController::setEnabled(bool enabled) {
if (!enabled) {
roster_->applyOnItems(AppearOffline());
}
}
void RosterController::handleShowOfflineToggled(bool state) {
if (state) {
roster_->removeFilter(offlineFilter_);
} else {
roster_->addFilter(offlineFilter_);
}
}
void RosterController::handleChangeStatusRequest(StatusShow::Type show, const std::string &statusText) {
onChangeStatusRequest(show, statusText);
}
void RosterController::handleOnJIDAdded(const JID& jid) {
std::vector<std::string> groups = xmppRoster_->getGroupsForJID(jid);
std::string name = nickResolver_->jidToNick(jid);
if (!groups.empty()) {
foreach(const std::string& group, groups) {
roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid));
}
}
else {
roster_->addContact(jid, jid, name, QT_TRANSLATE_NOOP("", "Contacts"), avatarManager_->getAvatarPath(jid));
}
applyAllPresenceTo(jid);
}
void RosterController::applyAllPresenceTo(const JID& jid) {
foreach (Presence::ref presence, presenceOracle_->getAllPresence(jid)) {
roster_->applyOnItems(SetPresence(presence));
}
}
void RosterController::handleRosterCleared() {
roster_->removeAll();
}
void RosterController::handleOnJIDRemoved(const JID& jid) {
roster_->removeContact(jid);
}
void RosterController::handleOnJIDUpdated(const JID& jid, const std::string& oldName, const std::vector<std::string>& passedOldGroups) {
if (oldName != xmppRoster_->getNameForJID(jid)) {
roster_->applyOnItems(SetName(nickResolver_->jidToNick(jid), jid));
}
std::vector<std::string> groups = xmppRoster_->getGroupsForJID(jid);
std::vector<std::string> oldGroups = passedOldGroups;
std::string name = nickResolver_->jidToNick(jid);
std::string contactsGroup = QT_TRANSLATE_NOOP("", "Contacts");
if (oldGroups.empty()) {
oldGroups.push_back(contactsGroup);
}
if (groups.empty()) {
groups.push_back(contactsGroup);
}
foreach(const std::string& group, groups) {
if (std::find(oldGroups.begin(), oldGroups.end(), group) == oldGroups.end()) {
roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid));
}
}
foreach(const std::string& group, oldGroups) {
if (std::find(groups.begin(), groups.end(), group) == groups.end()) {
roster_->removeContactFromGroup(jid, group);
if (roster_->getGroup(group)->getChildren().size() == 0) {
roster_->removeGroup(group);
}
}
}
applyAllPresenceTo(jid);
}
void RosterController::handleSettingChanged(const std::string& settingPath) {
if (settingPath == SettingConstants::SHOW_OFFLINE.getKey()) {
handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE));
}
}
void RosterController::handleBlockingStateChanged() {
if (clientBlockListManager_->getBlockList()->getState() == BlockList::Available) {
foreach(const JID& jid, clientBlockListManager_->getBlockList()->getItems()) {
roster_->applyOnItems(SetBlockingState(jid, ContactRosterItem::IsBlocked));
}
}
}
void RosterController::handleBlockingItemAdded(const JID& jid) {
roster_->applyOnItems(SetBlockingState(jid, ContactRosterItem::IsBlocked));
}
void RosterController::handleBlockingItemRemoved(const JID& jid) {
roster_->applyOnItems(SetBlockingState(jid, ContactRosterItem::IsUnblocked));
}
void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
if (boost::shared_ptr<AddContactUIEvent> addContactEvent = boost::dynamic_pointer_cast<AddContactUIEvent>(event)) {
RosterItemPayload item;
item.setName(addContactEvent->getName());
item.setJID(addContactEvent->getJID());
item.setGroups(std::vector<std::string>(addContactEvent->getGroups().begin(), addContactEvent->getGroups().end()));
boost::shared_ptr<RosterPayload> roster(new RosterPayload());
roster->addItem(item);
SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
subscriptionManager_->requestSubscription(addContactEvent->getJID());
}
else if (boost::shared_ptr<RemoveRosterItemUIEvent> removeEvent = boost::dynamic_pointer_cast<RemoveRosterItemUIEvent>(event)) {
RosterItemPayload item(removeEvent->getJID(), "", RosterItemPayload::Remove);
boost::shared_ptr<RosterPayload> roster(new RosterPayload());
roster->addItem(item);
SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
}
else if (boost::shared_ptr<RenameRosterItemUIEvent> renameEvent = boost::dynamic_pointer_cast<RenameRosterItemUIEvent>(event)) {
JID contact(renameEvent->getJID());
RosterItemPayload item(contact, renameEvent->getNewName(), xmppRoster_->getSubscriptionStateForJID(contact));
item.setGroups(xmppRoster_->getGroupsForJID(contact));
boost::shared_ptr<RosterPayload> roster(new RosterPayload());
roster->addItem(item);
SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
}
else if (boost::shared_ptr<RenameGroupUIEvent> renameGroupEvent = boost::dynamic_pointer_cast<RenameGroupUIEvent>(event)) {
std::vector<XMPPRosterItem> items = xmppRoster_->getItems();
std::string group = renameGroupEvent->getGroup();
// FIXME: We should handle contacts groups specially to avoid clashes
if (group == QT_TRANSLATE_NOOP("", "Contacts")) {
group = "";
}
foreach(XMPPRosterItem& item, items) {
std::vector<std::string> groups = item.getGroups();
if ( (group.empty() && groups.empty()) || std::find(groups.begin(), groups.end(), group) != groups.end()) {
groups.erase(std::remove(groups.begin(), groups.end(), group), groups.end());
if (std::find(groups.begin(), groups.end(), renameGroupEvent->getNewName()) == groups.end()) {
groups.push_back(renameGroupEvent->getNewName());
}
item.setGroups(groups);
updateItem(item);
}
}
}
else if (boost::shared_ptr<SendFileUIEvent> sendFileEvent = boost::dynamic_pointer_cast<SendFileUIEvent>(event)) {
//TODO add send file dialog to ChatView of receipient jid
ftOverview_->sendFile(sendFileEvent->getJID(), sendFileEvent->getFilename());
}
}
void RosterController::setContactGroups(const JID& jid, const std::vector<std::string>& groups) {
updateItem(XMPPRosterItem(jid, xmppRoster_->getNameForJID(jid), groups, xmppRoster_->getSubscriptionStateForJID(jid)));
}
void RosterController::updateItem(const XMPPRosterItem& item) {
RosterItemPayload itemPayload(item.getJID(), item.getName(), item.getSubscription());
itemPayload.setGroups(item.getGroups());
RosterPayload::ref roster = boost::make_shared<RosterPayload>();
roster->addItem(itemPayload);
SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_);
request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
}
void RosterController::initBlockingCommand() {
boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList();
blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&RosterController::handleBlockingStateChanged, this));
blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&RosterController::handleBlockingItemAdded, this, _1));
blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&RosterController::handleBlockingItemRemoved, this, _1));
roster_->setBlockingSupported(true);
if (blockList->getState() == BlockList::Available) {
foreach(const JID& jid, blockList->getItems()) {
roster_->applyOnItems(SetBlockingState(jid, ContactRosterItem::IsBlocked));
}
}
}
void RosterController::handleRosterSetError(ErrorPayload::ref error, boost::shared_ptr<RosterPayload> rosterPayload) {
if (!error) {
return;
}
std::string text = str(format(QT_TRANSLATE_NOOP("", "Server %1% rejected contact list change to item '%2%'")) % myJID_.getDomain() % rosterPayload->getItems()[0].getJID().toString());
if (!error->getText().empty()) {
text += ": " + error->getText();
}
boost::shared_ptr<ErrorEvent> errorEvent(new ErrorEvent(JID(myJID_.getDomain()), text));
eventController_->handleIncomingEvent(errorEvent);
}
void RosterController::handleIncomingPresence(Presence::ref newPresence) {
if (newPresence->getType() == Presence::Error) {
return;
}
roster_->applyOnItems(SetPresence(newPresence));
}
void RosterController::handleSubscriptionRequest(const JID& jid, const std::string& message) {
if (xmppRoster_->containsJID(jid) && (xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::To || xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::Both)) {
subscriptionManager_->confirmSubscription(jid);
return;
}
SubscriptionRequestEvent* eventPointer = new SubscriptionRequestEvent(jid, message);
eventPointer->onAccept.connect(boost::bind(&RosterController::handleSubscriptionRequestAccepted, this, eventPointer));
eventPointer->onDecline.connect(boost::bind(&RosterController::handleSubscriptionRequestDeclined, this, eventPointer));
boost::shared_ptr<StanzaEvent> event(eventPointer);
eventController_->handleIncomingEvent(event);
}
void RosterController::handleSubscriptionRequestAccepted(SubscriptionRequestEvent* event) {
subscriptionManager_->confirmSubscription(event->getJID());
if (!xmppRoster_->containsJID(event->getJID()) || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::None || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::From) {
subscriptionManager_->requestSubscription(event->getJID());
}
}
void RosterController::handleSubscriptionRequestDeclined(SubscriptionRequestEvent* event) {
subscriptionManager_->cancelSubscription(event->getJID());
}
void RosterController::handleAvatarChanged(const JID& jid) {
boost::filesystem::path path = avatarManager_->getAvatarPath(jid);
roster_->applyOnItems(SetAvatar(jid, path));
if (jid.equals(myJID_, JID::WithoutResource)) {
mainWindow_->setMyAvatarPath(pathToString(path));
}
}
boost::optional<XMPPRosterItem> RosterController::getItem(const JID& jid) const {
return xmppRoster_->getItem(jid);
}
std::set<std::string> RosterController::getGroups() const {
return xmppRoster_->getGroups();
}
void RosterController::handleOnCapsChanged(const JID& jid) {
DiscoInfo::ref info = entityCapsManager_->getCaps(jid);
if (info) {
std::set<ContactRosterItem::Feature> features;
- if (info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) && info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) {
+ if (FileTransferManager::isSupportedBy(info)) {
features.insert(ContactRosterItem::FileTransferFeature);
}
if (info->hasFeature(DiscoInfo::WhiteboardFeature)) {
features.insert(ContactRosterItem::WhiteboardFeature);
}
roster_->applyOnItems(SetAvailableFeatures(jid, features));
}
}
}
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index 096a59a..ba4b397 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -1,211 +1,212 @@
/*
* Copyright (c) 2010-2014 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <vector>
#include <string>
#include <boost/optional.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <Swiften/Base/boost_bsignals.h>
#include <Swiften/Elements/SecurityLabelsCatalog.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/Elements/Form.h>
#include <Swiften/Elements/MUCOccupant.h>
#include <Swiften/MUC/MUCBookmark.h>
#include <Swift/Controllers/HighlightManager.h>
namespace Swift {
class AvatarManager;
class TreeWidget;
class Roster;
class TabComplete;
class RosterItem;
class ContactRosterItem;
class FileTransferController;
class UserSearchWindow;
class ChatWindow {
public:
class ChatMessagePart {
public:
virtual ~ChatMessagePart() {}
};
class ChatMessage {
public:
ChatMessage() {}
ChatMessage(const std::string& text) {
append(boost::make_shared<ChatTextMessagePart>(text));
}
void append(const boost::shared_ptr<ChatMessagePart>& part) {
parts_.push_back(part);
}
const std::vector<boost::shared_ptr<ChatMessagePart> >& getParts() const {
return parts_;
}
private:
std::vector<boost::shared_ptr<ChatMessagePart> > parts_;
};
class ChatTextMessagePart : public ChatMessagePart {
public:
ChatTextMessagePart(const std::string& text) : text(text) {}
std::string text;
};
class ChatURIMessagePart : public ChatMessagePart {
public:
ChatURIMessagePart(const std::string& target) : target(target) {}
std::string target;
};
class ChatEmoticonMessagePart : public ChatMessagePart {
public:
std::string imagePath;
std::string alternativeText;
};
class ChatHighlightingMessagePart : public ChatMessagePart {
public:
std::string foregroundColor;
std::string backgroundColor;
std::string text;
};
enum AckState {Pending, Received, Failed};
enum ReceiptState {ReceiptRequested, ReceiptReceived, ReceiptFailed};
enum Tristate {Yes, No, Maybe};
enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor, AddContact, ShowProfile};
enum RoomAction {ChangeSubject, Configure, Affiliations, Destroy, Invite};
enum FileTransferState {WaitingForAccept, Negotiating, Transferring, Canceled, Finished, FTFailed};
enum WhiteboardSessionState {WhiteboardAccepted, WhiteboardTerminated, WhiteboardRejected};
enum BlockingState {BlockingUnsupported, IsBlocked, IsUnblocked};
enum Direction { UnknownDirection, DefaultDirection };
enum MUCType { StandardMUC, ImpromptuMUC };
ChatWindow() {}
virtual ~ChatWindow() {}
/** Add message to window.
* @return id of added message (for acks).
*/
virtual std::string addMessage(const ChatMessage& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0;
/** Adds action to window.
* @return id of added message (for acks);
*/
virtual std::string addAction(const ChatMessage& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0;
virtual void addSystemMessage(const ChatMessage& message, Direction direction) = 0;
virtual void addPresenceMessage(const ChatMessage& message, Direction direction) = 0;
virtual void addErrorMessage(const ChatMessage& message) = 0;
virtual void replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0;
virtual void replaceWithAction(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0;
// File transfer related stuff
virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0;
virtual void setFileTransferProgress(std::string, const int percentageDone) = 0;
virtual void setFileTransferStatus(std::string, const FileTransferState state, const std::string& msg = "") = 0;
virtual void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true, bool isImpromptu = false, bool isContinuation = false) = 0;
virtual std::string addWhiteboardRequest(bool senderIsSelf) = 0;
virtual void setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state) = 0;
// message receipts
virtual void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) = 0;
virtual void setContactChatState(ChatState::ChatStateType state) = 0;
virtual void setName(const std::string& name) = 0;
virtual void show() = 0;
virtual void activate() = 0;
virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) = 0;
virtual void setSecurityLabelsEnabled(bool enabled) = 0;
virtual void setCorrectionEnabled(Tristate enabled) = 0;
+ virtual void setFileTransferEnabled(Tristate enabled) = 0;
virtual void setUnreadMessageCount(int count) = 0;
virtual void convertToMUC(MUCType mucType) = 0;
// virtual TreeWidget *getTreeWidget() = 0;
virtual void setSecurityLabelsError() = 0;
virtual SecurityLabelsCatalog::Item getSelectedSecurityLabel() = 0;
virtual void setInputEnabled(bool enabled) = 0;
virtual void setRosterModel(Roster* model) = 0;
virtual void setTabComplete(TabComplete* completer) = 0;
virtual void replaceLastMessage(const ChatMessage& message) = 0;
virtual void setAckState(const std::string& id, AckState state) = 0;
virtual void flash() = 0;
virtual void setSubject(const std::string& subject) = 0;
virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) = 0;
virtual void setAvailableRoomActions(const std::vector<RoomAction> &actions) = 0;
virtual void setBlockingState(BlockingState state) = 0;
virtual void setCanInitiateImpromptuChats(bool supportsImpromptu) = 0;
virtual void showBookmarkWindow(const MUCBookmark& bookmark) = 0;
/**
* Set an alert on the window.
* @param alertText Description of alert (required).
* @param buttonText Button text to use (optional, no button is shown if empty).
*/
virtual void setAlert(const std::string& alertText, const std::string& buttonText = "") = 0;
/**
* Removes an alert.
*/
virtual void cancelAlert() = 0;
/**
* Actions that can be performed on the selected occupant.
*/
virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions) = 0;
/**
* A room configuration has been requested, show the form.
* If the form is cancelled, must emit onConfigurationFormCancelled().
*/
virtual void showRoomConfigurationForm(Form::ref) = 0;
boost::signal<void ()> onClosed;
boost::signal<void ()> onAllMessagesRead;
boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest;
boost::signal<void ()> onSendCorrectionMessageRequest;
boost::signal<void ()> onUserTyping;
boost::signal<void ()> onUserCancelsTyping;
boost::signal<void ()> onAlertButtonClicked;
boost::signal<void (ContactRosterItem*)> onOccupantSelectionChanged;
boost::signal<void (ChatWindow::OccupantAction, ContactRosterItem*)> onOccupantActionSelected;
boost::signal<void (const std::string&)> onChangeSubjectRequest;
boost::signal<void ()> onBookmarkRequest;
boost::signal<void (Form::ref)> onConfigureRequest;
boost::signal<void ()> onDestroyRequest;
boost::signal<void (const std::vector<JID>&)> onInviteToChat;
boost::signal<void ()> onConfigurationFormCancelled;
boost::signal<void ()> onGetAffiliationsRequest;
boost::signal<void (MUCOccupant::Affiliation, const JID&)> onSetAffiliationRequest;
boost::signal<void (const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& changes)> onChangeAffiliationsRequest;
boost::signal<void ()> onLogCleared;
// File transfer related
boost::signal<void (std::string /* id */)> onFileTransferCancel;
boost::signal<void (std::string /* id */, std::string /* description */)> onFileTransferStart;
boost::signal<void (std::string /* id */, std::string /* path */)> onFileTransferAccept;
boost::signal<void (std::string /* path */)> onSendFileRequest;
//Whiteboard related
boost::signal<void ()> onWhiteboardSessionAccept;
boost::signal<void ()> onWhiteboardSessionCancel;
boost::signal<void ()> onWhiteboardWindowShow;
// Blocking Command related
boost::signal<void ()> onBlockUserRequest;
boost::signal<void ()> onUnblockUserRequest;
};
}
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index c2e2c9f..e4c2548 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -1,94 +1,95 @@
/*
* Copyright (c) 2010-2014 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <boost/shared_ptr.hpp>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
#include <Swiften/Base/foreach.h>
namespace Swift {
class MockChatWindow : public ChatWindow {
public:
MockChatWindow() : labelsEnabled_(false) {}
virtual ~MockChatWindow();
virtual std::string addMessage(const ChatMessage& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {
lastMessageBody_ = bodyFromMessage(message); return "id";}
virtual std::string addAction(const ChatMessage& /*message*/, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {return "id";}
virtual void addSystemMessage(const ChatMessage& /*message*/, Direction /*direction*/) {}
virtual void addPresenceMessage(const ChatMessage& /*message*/, Direction /*direction*/) {}
virtual void addErrorMessage(const ChatMessage& /*message*/) {}
virtual void replaceMessage(const ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {}
virtual void replaceWithAction(const ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {}
virtual void replaceLastMessage(const ChatMessage& /*message*/) {}
// File transfer related stuff
virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/,const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/) { return 0; }
virtual void setFileTransferProgress(std::string /*id*/, const int /*alreadyTransferedBytes*/) { }
virtual void setFileTransferStatus(std::string /*id*/, const FileTransferState /*state*/, const std::string& /*msg*/) { }
virtual void setMessageReceiptState(const std::string &/* id */, ReceiptState /* state */) { }
virtual void setContactChatState(ChatState::ChatStateType /*state*/) {}
virtual void setName(const std::string& name) {name_ = name;}
virtual void show() {}
virtual void activate() {}
virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) {labels_ = labels;}
virtual void setSecurityLabelsEnabled(bool enabled) {labelsEnabled_ = enabled;}
virtual void setUnreadMessageCount(int /*count*/) {}
virtual void convertToMUC(MUCType /*mucType*/) {}
virtual void setSecurityLabelsError() {}
virtual SecurityLabelsCatalog::Item getSelectedSecurityLabel() {return label_;}
virtual void setInputEnabled(bool /*enabled*/) {}
virtual void setRosterModel(Roster* roster) { roster_ = roster; }
Roster* getRosterModel() { return roster_; }
virtual void setTabComplete(TabComplete*) {}
void setAckState(const std::string& /*id*/, AckState /*state*/) {}
virtual void flash() {}
virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {}
virtual void cancelAlert() {}
virtual void setCorrectionEnabled(Tristate /*enabled*/) {}
+ virtual void setFileTransferEnabled(Tristate /*enabled*/) {}
void setAvailableOccupantActions(const std::vector<OccupantAction>&/* actions*/) {}
void setSubject(const std::string& /*subject*/) {}
virtual void showRoomConfigurationForm(Form::ref) {}
virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool = true, bool = false, bool = false) {}
virtual std::string addWhiteboardRequest(bool) {return "";}
virtual void setWhiteboardSessionStatus(std::string, const ChatWindow::WhiteboardSessionState){}
virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) {}
virtual void setAvailableRoomActions(const std::vector<RoomAction> &) {}
virtual void setBlockingState(BlockingState) {}
virtual void setCanInitiateImpromptuChats(bool /*supportsImpromptu*/) {}
virtual void showBookmarkWindow(const MUCBookmark& /*bookmark*/) {}
std::string bodyFromMessage(const ChatMessage& message) {
boost::shared_ptr<ChatTextMessagePart> text;
foreach (boost::shared_ptr<ChatMessagePart> part, message.getParts()) {
if ((text = boost::dynamic_pointer_cast<ChatTextMessagePart>(part))) {
return text->text;
}
}
return "";
}
std::string name_;
std::string lastMessageBody_;
std::vector<SecurityLabelsCatalog::Item> labels_;
bool labelsEnabled_;
SecurityLabelsCatalog::Item label_;
Roster* roster_;
};
}