summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swift/Controllers/Chat/MUCController.cpp')
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp475
1 files changed, 403 insertions, 72 deletions
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index e4209f4..fe90c60 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -1,4 +1,4 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
@@ -11,28 +11,38 @@
#include <boost/algorithm/string.hpp>
-#include <Swift/Controllers/Intl.h>
+#include <Swiften/Avatars/AvatarManager.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/foreach.h>
#include <Swiften/Base/format.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Client/StanzaChannel.h>
+#include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swiften/Elements/Delay.h>
+#include <Swiften/MUC/MUC.h>
#include <Swiften/Network/Timer.h>
#include <Swiften/Network/TimerFactory.h>
-#include <Swiften/Base/foreach.h>
+#include <Swiften/Roster/XMPPRoster.h>
+
#include <SwifTools/TabComplete.h>
-#include <Swiften/Base/foreach.h>
-#include <Swift/Controllers/XMPPEvents/EventController.h>
-#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
-#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
-#include <Swift/Controllers/UIEvents/UIEventStream.h>
-#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
-#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
-#include <Swift/Controllers/Roster/GroupRosterItem.h>
+
+#include <Swift/Controllers/Chat/ChatMessageParser.h>
+#include <Swift/Controllers/Highlighter.h>
+#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/Roster/ContactRosterItem.h>
-#include <Swiften/Avatars/AvatarManager.h>
-#include <Swiften/Elements/Delay.h>
-#include <Swiften/MUC/MUC.h>
-#include <Swiften/Client/StanzaChannel.h>
+#include <Swift/Controllers/Roster/GroupRosterItem.h>
+#include <Swift/Controllers/Roster/ItemOperations/SetAvatar.h>
+#include <Swift/Controllers/Roster/ItemOperations/SetPresence.h>
+#include <Swift/Controllers/Roster/ItemOperations/SetMUC.h>
#include <Swift/Controllers/Roster/Roster.h>
-#include <Swift/Controllers/Roster/SetAvatar.h>
-#include <Swift/Controllers/Roster/SetPresence.h>
-#include <Swiften/Disco/EntityCapsProvider.h>
-
+#include <Swift/Controllers/Roster/RosterVCardProvider.h>
+#include <Swift/Controllers/UIEvents/InviteToMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestInviteToMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
+#include <Swift/Controllers/XMPPEvents/EventController.h>
#define MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS 60000
@@ -57,6 +67,14 @@ MUCController::MUCController (
TimerFactory* timerFactory,
EventController* eventController,
- EntityCapsProvider* entityCapsProvider) :
- ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider), muc_(muc), nick_(nick), desiredNick_(nick), password_(password) {
+ EntityCapsProvider* entityCapsProvider,
+ XMPPRoster* roster,
+ HistoryController* historyController,
+ MUCRegistry* mucRegistry,
+ HighlightManager* highlightManager,
+ boost::shared_ptr<ChatMessageParser> chatMessageParser,
+ bool isImpromptu,
+ AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider,
+ VCardManager* vcardManager) :
+ ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider), muc_(muc), nick_(nick), desiredNick_(nick), password_(password), renameCounter_(0), isImpromptu_(isImpromptu), isImpromptuAlreadyConfigured_(false) {
parting_ = true;
joined_ = false;
@@ -65,18 +83,20 @@ MUCController::MUCController (
doneGettingHistory_ = false;
events_ = uiEventStream;
+ xmppRoster_ = roster;
roster_ = new Roster(false, true);
+ rosterVCardProvider_ = new RosterVCardProvider(roster_, vcardManager, JID::WithResource);
completer_ = new TabComplete();
chatWindow_->setRosterModel(roster_);
chatWindow_->setTabComplete(completer_);
- chatWindow_->setName(muc->getJID().getNode());
chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this));
chatWindow_->onOccupantSelectionChanged.connect(boost::bind(&MUCController::handleWindowOccupantSelectionChanged, this, _1));
chatWindow_->onOccupantActionSelected.connect(boost::bind(&MUCController::handleActionRequestedOnOccupant, this, _1, _2));
chatWindow_->onChangeSubjectRequest.connect(boost::bind(&MUCController::handleChangeSubjectRequest, this, _1));
+ chatWindow_->onBookmarkRequest.connect(boost::bind(&MUCController::handleBookmarkRequest, this));
chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this, _1));
chatWindow_->onConfigurationFormCancelled.connect(boost::bind(&MUCController::handleConfigurationCancelled, this));
chatWindow_->onDestroyRequest.connect(boost::bind(&MUCController::handleDestroyRoomRequest, this));
- chatWindow_->onInvitePersonToThisMUCRequest.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, this, _1, _2));
+ chatWindow_->onInviteToChat.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, this, _1));
chatWindow_->onGetAffiliationsRequest.connect(boost::bind(&MUCController::handleGetAffiliationsRequest, this));
chatWindow_->onChangeAffiliationsRequest.connect(boost::bind(&MUCController::handleChangeAffiliationsRequest, this, _1));
@@ -84,12 +104,13 @@ MUCController::MUCController (
muc_->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1));
muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
+ muc_->onOccupantNicknameChanged.connect(boost::bind(&MUCController::handleOccupantNicknameChanged, this, _1, _2));
muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
- muc_->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
- muc_->onOccupantAffiliationChanged.connect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3));
- muc_->onConfigurationFailed.connect(boost::bind(&MUCController::handleConfigurationFailed, this, _1));
- muc_->onConfigurationFormReceived.connect(boost::bind(&MUCController::handleConfigurationFormReceived, this, _1));
muc_->onRoleChangeFailed.connect(boost::bind(&MUCController::handleOccupantRoleChangeFailed, this, _1, _2, _3));
muc_->onAffiliationListReceived.connect(boost::bind(&MUCController::handleAffiliationListReceived, this, _1, _2));
+ muc_->onConfigurationFailed.connect(boost::bind(&MUCController::handleConfigurationFailed, this, _1));
+ muc_->onConfigurationFormReceived.connect(boost::bind(&MUCController::handleConfigurationFormReceived, this, _1));
+ highlighter_->setMode(isImpromptu_ ? Highlighter::ChatMode : Highlighter::MUCMode);
+ highlighter_->setNick(nick_);
if (timerFactory) {
loginCheckTimer_ = boost::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS));
@@ -97,5 +118,13 @@ MUCController::MUCController (
loginCheckTimer_->start();
}
- chatWindow_->convertToMUC();
+ if (isImpromptu) {
+ muc_->onUnlocked.connect(boost::bind(&MUCController::handleRoomUnlocked, this));
+ chatWindow_->convertToMUC(ChatWindow::ImpromptuMUC);
+ } else {
+ muc_->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
+ muc_->onOccupantAffiliationChanged.connect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3));
+ chatWindow_->convertToMUC(ChatWindow::StandardMUC);
+ chatWindow_->setName(muc->getJID().getNode());
+ }
setOnline(true);
if (avatarManager_ != NULL) {
@@ -103,8 +132,11 @@ MUCController::MUCController (
}
handleBareJIDCapsChanged(muc->getJID());
+ eventStream_->onUIEvent.connect(boost::bind(&MUCController::handleUIEvent, this, _1));
}
MUCController::~MUCController() {
+ eventStream_->onUIEvent.disconnect(boost::bind(&MUCController::handleUIEvent, this, _1));
chatWindow_->setRosterModel(NULL);
+ delete rosterVCardProvider_;
delete roster_;
if (loginCheckTimer_) {
@@ -125,5 +157,5 @@ void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item
MUCOccupant::Affiliation affiliation = muc_->getOccupant(getNick()).getAffiliation();
MUCOccupant::Role role = muc_->getOccupant(getNick()).getRole();
- if (role == MUCOccupant::Moderator)
+ if (role == MUCOccupant::Moderator && !isImpromptu_)
{
if (affiliation == MUCOccupant::Admin || affiliation == MUCOccupant::Owner) {
@@ -140,4 +172,5 @@ void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item
actions.push_back(ChatWindow::AddContact);
}
+ actions.push_back(ChatWindow::ShowProfile);
}
chatWindow_->setAvailableOccupantActions(actions);
@@ -158,4 +191,5 @@ void MUCController::handleActionRequestedOnOccupant(ChatWindow::OccupantAction a
case ChatWindow::MakeVisitor: muc_->changeOccupantRole(mucJID, MUCOccupant::Visitor);break;
case ChatWindow::AddContact: if (occupant.getRealJID()) events_->send(boost::make_shared<RequestAddUserDialogUIEvent>(realJID, occupant.getNick()));break;
+ case ChatWindow::ShowProfile: events_->send(boost::make_shared<ShowProfileForRosterItemUIEvent>(mucJID));break;
}
}
@@ -189,7 +223,13 @@ void MUCController::rejoin() {
}
//FIXME: check for received activity
+#ifdef SWIFT_EXPERIMENTAL_HISTORY
+ if (lastActivity_ == boost::posix_time::not_a_date_time && historyController_) {
+ lastActivity_ = historyController_->getLastTimeStampFromMUC(selfJID_, toJID_);
+ }
+#endif
if (lastActivity_ == boost::posix_time::not_a_date_time) {
muc_->joinAs(nick_);
- } else {
+ }
+ else {
muc_->joinWithContextSince(nick_, lastActivity_);
}
@@ -205,7 +245,33 @@ const std::string& MUCController::getNick() {
}
+const boost::optional<std::string> MUCController::getPassword() const {
+ return password_;
+}
+
+bool MUCController::isImpromptu() const {
+ return isImpromptu_;
+}
+
+std::map<std::string, JID> MUCController::getParticipantJIDs() const {
+ std::map<std::string, JID> participants;
+ typedef std::pair<std::string, MUCOccupant> MUCOccupantPair;
+ std::map<std::string, MUCOccupant> occupants = muc_->getOccupants();
+ foreach(const MUCOccupantPair& occupant, occupants) {
+ if (occupant.first != nick_) {
+ participants[occupant.first] = occupant.second.getRealJID().is_initialized() ? occupant.second.getRealJID().get().toBare() : JID();
+ }
+ }
+ return participants;
+}
+
+void MUCController::sendInvites(const std::vector<JID>& jids, const std::string& reason) const {
+ foreach (const JID& jid, jids) {
+ muc_->invitePerson(jid, reason, isImpromptu_);
+ }
+}
+
void MUCController::handleJoinTimeoutTick() {
receivedActivity();
- chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "Room %1% is not responding. This operation may never complete.")) % toJID_.toString()));
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "Room %1% is not responding. This operation may never complete.")) % toJID_.toString())), ChatWindow::DefaultDirection);
}
@@ -216,4 +282,7 @@ void MUCController::receivedActivity() {
}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wswitch-enum"
+
void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) {
receivedActivity();
@@ -255,24 +324,46 @@ void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) {
}
errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't join room: %1%.")) % errorMessage);
- chatWindow_->addErrorMessage(errorMessage);
+ chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage));
parting_ = true;
- if (!rejoinNick.empty()) {
- nick_ = rejoinNick;
+ if (!rejoinNick.empty() && renameCounter_ < 10) {
+ renameCounter_++;
+ setNick(rejoinNick);
rejoin();
}
}
+#pragma clang diagnostic pop
+
void MUCController::handleJoinComplete(const std::string& nick) {
receivedActivity();
+ renameCounter_ = 0;
joined_ = true;
- std::string joinMessage = str(format(QT_TRANSLATE_NOOP("", "You have entered room %1% as %2%.")) % toJID_.toString() % nick);
- nick_ = nick;
- chatWindow_->addSystemMessage(joinMessage);
+ std::string joinMessage;
+ if (isImpromptu_) {
+ joinMessage = str(format(QT_TRANSLATE_NOOP("", "You have joined the chat as %1%.")) % nick);
+ } else {
+ joinMessage = str(format(QT_TRANSLATE_NOOP("", "You have entered room %1% as %2%.")) % toJID_.toString() % nick);
+ }
+ setNick(nick);
+ chatWindow_->replaceLastMessage(chatMessageParser_->parseMessageBody(joinMessage), ChatWindow::UpdateTimestamp);
+
+#ifdef SWIFT_EXPERIMENTAL_HISTORY
+ addRecentLogs();
+#endif
+
clearPresenceQueue();
shouldJoinOnReconnect_ = true;
setEnabled(true);
+ if (isImpromptu_) {
+ setAvailableRoomActions(MUCOccupant::NoAffiliation, MUCOccupant::Participant);
+ } else {
MUCOccupant occupant = muc_->getOccupant(nick);
setAvailableRoomActions(occupant.getAffiliation(), occupant.getRole());
+ }
onUserJoined();
+
+ if (isImpromptu_) {
+ setImpromptuWindowTitle();
+ }
}
@@ -281,6 +372,5 @@ void MUCController::handleAvatarChanged(const JID& jid) {
return;
}
- std::string path = avatarManager_->getAvatarPath(jid).string();
- roster_->applyOnItems(SetAvatar(jid, path, JID::WithResource));
+ roster_->applyOnItems(SetAvatar(jid, avatarManager_->getAvatarPath(jid), JID::WithResource));
}
@@ -305,15 +395,21 @@ void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {
NickJoinPart event(occupant.getNick(), Join);
appendToJoinParts(joinParts_, event);
- std::string groupName(roleToGroupName(occupant.getRole()));
- roster_->addContact(jid, realJID, occupant.getNick(), groupName, avatarManager_->getAvatarPath(jid).string());
- roster_->getGroup(groupName)->setManualSort(roleToSortName(occupant.getRole()));
+ MUCOccupant::Role role = MUCOccupant::Participant;
+ MUCOccupant::Affiliation affiliation = MUCOccupant::NoAffiliation;
+ if (!isImpromptu_) {
+ role = occupant.getRole();
+ affiliation = occupant.getAffiliation();
+ }
+ std::string groupName(roleToGroupName(role));
+ roster_->addContact(jid, realJID, occupant.getNick(), groupName, avatarManager_->getAvatarPath(jid));
+ roster_->applyOnItems(SetMUC(jid, role, affiliation));
+ roster_->getGroup(groupName)->setManualSort(roleToSortName(role));
if (joined_) {
std::string joinString;
- MUCOccupant::Role role = occupant.getRole();
if (role != MUCOccupant::NoRole && role != MUCOccupant::Participant) {
- joinString = str(format(QT_TRANSLATE_NOOP("", "%1% has entered the room as a %2%.")) % occupant.getNick() % roleToFriendlyName(role));
+ joinString = str(format(QT_TRANSLATE_NOOP("", "%1% has entered the %3% as a %2%.")) % occupant.getNick() % roleToFriendlyName(role) % (isImpromptu_ ? QT_TRANSLATE_NOOP("", "chat") : QT_TRANSLATE_NOOP("", "room")));
}
else {
- joinString = str(format(QT_TRANSLATE_NOOP("", "%1% has entered the room.")) % occupant.getNick());
+ joinString = str(format(QT_TRANSLATE_NOOP("", "%1% has entered the %2%.")) % occupant.getNick() % (isImpromptu_ ? QT_TRANSLATE_NOOP("", "chat") : QT_TRANSLATE_NOOP("", "room")));
}
if (shouldUpdateJoinParts()) {
@@ -321,5 +417,9 @@ void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {
} else {
addPresenceMessage(joinString);
+ }
+ if (isImpromptu_) {
+ setImpromptuWindowTitle();
+ onActivity("");
}
}
@@ -331,5 +431,5 @@ void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {
void MUCController::addPresenceMessage(const std::string& message) {
lastWasPresence_ = true;
- chatWindow_->addPresenceMessage(message);
+ chatWindow_->addPresenceMessage(chatMessageParser_->parseMessageBody(message), ChatWindow::DefaultDirection);
}
@@ -369,4 +469,5 @@ std::string MUCController::roleToFriendlyName(MUCOccupant::Role role) {
case MUCOccupant::NoRole: return "";
}
+ assert(false);
return "";
}
@@ -379,9 +480,10 @@ std::string MUCController::roleToSortName(MUCOccupant::Role role) {
case MUCOccupant::NoRole: return "4";
}
+ assert(false);
return "5";
}
JID MUCController::nickToJID(const std::string& nick) {
- return JID(toJID_.getNode(), toJID_.getDomain(), nick);
+ return muc_->getJID().withResource(nick);
}
@@ -398,6 +500,5 @@ void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> mes
clearPresenceQueue();
boost::shared_ptr<Message> message = messageEvent->getStanza();
- if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && messageTargetsMe(message) && !message->getPayload<Delay>() && messageEvent->isReadable()
-) {
+ if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && messageTargetsMe(message) && !message->getPayload<Delay>() && messageEvent->isReadable()) {
chatWindow_->flash();
}
@@ -405,4 +506,7 @@ void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> mes
messageEvent->setTargetsMe(false);
}
+ if (messageEvent->isReadable() && isImpromptu_) {
+ chatWindow_->flash(); /* behave like a regular char*/
+ }
if (joined_) {
std::string nick = message->getFrom().getResource();
@@ -416,5 +520,5 @@ void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> mes
if (message->hasSubject() && message->getBody().empty()) {
- chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "The room subject is now: %1%")) % message->getSubject()));;
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "The room subject is now: %1%")) % message->getSubject())), ChatWindow::DefaultDirection);;
chatWindow_->setSubject(message->getSubject());
doneGettingHistory_ = true;
@@ -426,12 +530,18 @@ void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> mes
if (!doneGettingHistory_) {
+ checkDuplicates(message);
messageEvent->conclude();
}
}
-void MUCController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) {
+void MUCController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction& highlight) {
boost::shared_ptr<Message> message = messageEvent->getStanza();
- if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && messageTargetsMe(message) && !message->getPayload<Delay>()) {
+ if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && !message->getPayload<Delay>()) {
+ if (messageTargetsMe(message) || isImpromptu_) {
eventController_->handleIncomingEvent(messageEvent);
+ if (!messageEvent->getConcluded()) {
+ highlighter_->handleHighlightAction(highlight);
+ }
+ }
}
}
@@ -447,7 +557,8 @@ void MUCController::handleOccupantRoleChanged(const std::string& nick, const MUC
}
std::string group(roleToGroupName(occupant.getRole()));
- roster_->addContact(jid, realJID, nick, group, avatarManager_->getAvatarPath(jid).string());
+ roster_->addContact(jid, realJID, nick, group, avatarManager_->getAvatarPath(jid));
roster_->getGroup(group)->setManualSort(roleToSortName(occupant.getRole()));
- chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "%1% is now a %2%")) % nick % roleToFriendlyName(occupant.getRole())));
+ roster_->applyOnItems(SetMUC(jid, occupant.getRole(), occupant.getAffiliation()));
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "%1% is now a %2%")) % nick % roleToFriendlyName(occupant.getRole()))), ChatWindow::DefaultDirection);
if (nick == nick_) {
setAvailableRoomActions(occupant.getAffiliation(), occupant.getRole());
@@ -460,4 +571,7 @@ void MUCController::handleOccupantAffiliationChanged(const std::string& nick, co
setAvailableRoomActions(affiliation, muc_->getOccupant(nick_).getRole());
}
+ JID jid(nickToJID(nick));
+ MUCOccupant occupant = muc_->getOccupant(nick);
+ roster_->applyOnItems(SetMUC(jid, occupant.getRole(), affiliation));
}
@@ -469,5 +583,4 @@ std::string MUCController::roleToGroupName(MUCOccupant::Role role) {
case MUCOccupant::Visitor: result = QT_TRANSLATE_NOOP("", "Visitors"); break;
case MUCOccupant::NoRole: result = QT_TRANSLATE_NOOP("", "Occupants"); break;
- default: assert(false);
}
return result;
@@ -482,9 +595,14 @@ void MUCController::setOnline(bool online) {
} else {
if (shouldJoinOnReconnect_) {
- chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "Trying to enter room %1%")) % toJID_.toString()));
+ renameCounter_ = 0;
+ if (isImpromptu_) {
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(QT_TRANSLATE_NOOP("", "Trying to join chat")), ChatWindow::DefaultDirection);
+ } else {
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "Trying to enter room %1%")) % toJID_.toString())), ChatWindow::DefaultDirection);
+ }
if (loginCheckTimer_) {
loginCheckTimer_->start();
}
- nick_ = desiredNick_;
+ setNick(desiredNick_);
rejoin();
}
@@ -523,6 +641,20 @@ void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::Leaving
case MUC::LeavePart: break;
}
+ if (isImpromptu_) {
+ partMessage = str(format(QT_TRANSLATE_NOOP("", "%1% has left the chat%2%")) % occupant.getNick() % partType);
+ } else {
partMessage = str(format(QT_TRANSLATE_NOOP("", "%1% has left the room%2%")) % occupant.getNick() % partType);
}
+ }
+ else if (isImpromptu_) {
+ switch (type) {
+ case MUC::LeaveKick:
+ case MUC::LeaveBan: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "You have been removed from this chat"); break;
+ case MUC::LeaveNotMember: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "You have been removed from this chat"); break;
+ case MUC::LeaveDestroy: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "This chat has ended"); break;
+ case MUC::Disconnect:
+ case MUC::LeavePart: partMessage = QT_TRANSLATE_NOOP("", "You have left the chat");
+ }
+ }
else {
switch (type) {
@@ -555,4 +687,46 @@ void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::Leaving
clearPresenceQueue();
}
+
+ if (isImpromptu_) {
+ setImpromptuWindowTitle();
+ }
+}
+
+void MUCController::handleOccupantNicknameChanged(const std::string& oldNickname, const std::string& newNickname) {
+ addPresenceMessage(generateNicknameChangeString(oldNickname, newNickname));
+ JID oldJID = muc_->getJID().withResource(oldNickname);
+ JID newJID = muc_->getJID().withResource(newNickname);
+
+ // adjust occupants
+ currentOccupants_.erase(oldNickname);
+ currentOccupants_.insert(newNickname);
+
+ // adjust completer
+ completer_->removeWord(oldNickname);
+ completer_->addWord(newNickname);
+
+ // update contact
+ roster_->removeContact(oldJID);
+ MUCOccupant occupant = muc_->getOccupant(newNickname);
+
+ JID realJID;
+ if (occupant.getRealJID()) {
+ realJID = occupant.getRealJID().get();
+ }
+ MUCOccupant::Role role = MUCOccupant::Participant;
+ MUCOccupant::Affiliation affiliation = MUCOccupant::NoAffiliation;
+ if (!isImpromptu_) {
+ role = occupant.getRole();
+ affiliation = occupant.getAffiliation();
+ }
+ std::string groupName(roleToGroupName(role));
+ roster_->addContact(newJID, realJID, newNickname, groupName, avatarManager_->getAvatarPath(newJID));
+ roster_->applyOnItems(SetMUC(newJID, role, affiliation));
+ if (avatarManager_ != NULL) {
+ handleAvatarChanged(newJID);
+ }
+
+ clearPresenceQueue();
+ onUserNicknameChanged(oldNickname, newNickname);
}
@@ -580,5 +754,5 @@ boost::optional<boost::posix_time::ptime> MUCController::getMessageTimestamp(boo
void MUCController::updateJoinParts() {
- chatWindow_->replaceLastMessage(generateJoinPartString(joinParts_));
+ chatWindow_->replaceLastMessage(chatMessageParser_->parseMessageBody(generateJoinPartString(joinParts_, isImpromptu())), ChatWindow::UpdateTimestamp);
}
@@ -593,5 +767,6 @@ void MUCController::appendToJoinParts(std::vector<NickJoinPart>& joinParts, cons
case Join: type = (type == Part) ? PartThenJoin : Join; break;
case Part: type = (type == Join) ? JoinThenPart : Part; break;
- default: /*Nothing to see here */;break;
+ case PartThenJoin: break;
+ case JoinThenPart: break;
}
(*it).type = type;
@@ -620,5 +795,5 @@ std::string MUCController::concatenateListOfNames(const std::vector<NickJoinPart
}
-std::string MUCController::generateJoinPartString(const std::vector<NickJoinPart>& joinParts) {
+std::string MUCController::generateJoinPartString(const std::vector<NickJoinPart>& joinParts, bool isImpromptu) {
std::vector<NickJoinPart> sorted[4];
std::string eventStrings[4];
@@ -635,32 +810,32 @@ std::string MUCController::generateJoinPartString(const std::vector<NickJoinPart
case Join:
if (sorted[i].size() > 1) {
- eventString = QT_TRANSLATE_NOOP("", "%1% have entered the room");
+ eventString = (isImpromptu ? QT_TRANSLATE_NOOP("", "%1% have joined the chat") : QT_TRANSLATE_NOOP("", "%1% have entered the room"));
}
else {
- eventString = QT_TRANSLATE_NOOP("", "%1% has entered the room");
+ eventString = (isImpromptu ? QT_TRANSLATE_NOOP("", "%1% has joined the chat") : QT_TRANSLATE_NOOP("", "%1% has entered the room"));
}
break;
case Part:
if (sorted[i].size() > 1) {
- eventString = QT_TRANSLATE_NOOP("", "%1% have left the room");
+ eventString = (isImpromptu ? QT_TRANSLATE_NOOP("", "%1% have left the chat") : QT_TRANSLATE_NOOP("", "%1% have left the room"));
}
else {
- eventString = QT_TRANSLATE_NOOP("", "%1% has left the room");
+ eventString = (isImpromptu ? QT_TRANSLATE_NOOP("", "%1% have left the chat") : QT_TRANSLATE_NOOP("", "%1% has left the room"));
}
break;
case JoinThenPart:
if (sorted[i].size() > 1) {
- eventString = QT_TRANSLATE_NOOP("", "%1% have entered then left the room");
+ eventString = (isImpromptu ? QT_TRANSLATE_NOOP("", "%1% have joined then left the chat") : QT_TRANSLATE_NOOP("", "%1% have entered then left the room"));
}
else {
- eventString = QT_TRANSLATE_NOOP("", "%1% has entered then left the room");
+ eventString = (isImpromptu ? QT_TRANSLATE_NOOP("", "%1% has joined then left the chat") : QT_TRANSLATE_NOOP("", "%1% has entered then left the room"));
}
break;
case PartThenJoin:
if (sorted[i].size() > 1) {
- eventString = QT_TRANSLATE_NOOP("", "%1% have left then returned to the room");
+ eventString = (isImpromptu ? QT_TRANSLATE_NOOP("", "%1% have left then returned to the chat") : QT_TRANSLATE_NOOP("", "%1% have left then returned to the room"));
}
else {
- eventString = QT_TRANSLATE_NOOP("", "%1% has left then returned to the room");
+ eventString = (isImpromptu ? QT_TRANSLATE_NOOP("", "%1% has left then returned to the chat") : QT_TRANSLATE_NOOP("", "%1% has left then returned to the room"));
}
break;
@@ -683,8 +858,20 @@ std::string MUCController::generateJoinPartString(const std::vector<NickJoinPart
}
+std::string MUCController::generateNicknameChangeString(const std::string& oldNickname, const std::string& newNickname) {
+ return str(boost::format(QT_TRANSLATE_NOOP("", "%1% is now known as %2%.")) % oldNickname % newNickname);
+}
+
void MUCController::handleChangeSubjectRequest(const std::string& subject) {
muc_->changeSubject(subject);
}
+void MUCController::handleBookmarkRequest() {
+ const JID jid = muc_->getJID();
+ MUCBookmark bookmark(jid, jid.toBare().toString());
+ bookmark.setPassword(password_);
+ bookmark.setNick(nick_);
+ chatWindow_->showBookmarkWindow(bookmark);
+}
+
void MUCController::handleConfigureRequest(Form::ref form) {
if (form) {
@@ -699,5 +886,5 @@ void MUCController::handleConfigurationFailed(ErrorPayload::ref error) {
std::string errorMessage = getErrorMessage(error);
errorMessage = str(format(QT_TRANSLATE_NOOP("", "Room configuration failed: %1%.")) % errorMessage);
- chatWindow_->addErrorMessage(errorMessage);
+ chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage));
}
@@ -705,10 +892,22 @@ void MUCController::handleOccupantRoleChangeFailed(ErrorPayload::ref error, cons
std::string errorMessage = getErrorMessage(error);
errorMessage = str(format(QT_TRANSLATE_NOOP("", "Occupant role change failed: %1%.")) % errorMessage);
- chatWindow_->addErrorMessage(errorMessage);
+ chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage));
+}
+
+void MUCController::configureAsImpromptuRoom(Form::ref form) {
+ muc_->configureRoom(buildImpromptuRoomConfiguration(form));
+ isImpromptuAlreadyConfigured_ = true;
+ onImpromptuConfigCompleted();
}
void MUCController::handleConfigurationFormReceived(Form::ref form) {
+ if (isImpromptu_) {
+ if (!isImpromptuAlreadyConfigured_) {
+ configureAsImpromptuRoom(form);
+ }
+ } else {
chatWindow_->showRoomConfigurationForm(form);
}
+}
void MUCController::handleConfigurationCancelled() {
@@ -720,6 +919,17 @@ void MUCController::handleDestroyRoomRequest() {
}
-void MUCController::handleInvitePersonToThisMUCRequest(const JID& jid, const std::string& reason) {
- muc_->invitePerson(jid, reason);
+void MUCController::handleInvitePersonToThisMUCRequest(const std::vector<JID>& jidsToInvite) {
+ RequestInviteToMUCUIEvent::ImpromptuMode mode = isImpromptu_ ? RequestInviteToMUCUIEvent::Impromptu : RequestInviteToMUCUIEvent::NotImpromptu;
+ boost::shared_ptr<UIEvent> event(new RequestInviteToMUCUIEvent(muc_->getJID(), jidsToInvite, mode));
+ eventStream_->send(event);
+}
+
+void MUCController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
+ boost::shared_ptr<InviteToMUCUIEvent> inviteEvent = boost::dynamic_pointer_cast<InviteToMUCUIEvent>(event);
+ if (inviteEvent && inviteEvent->getRoom() == muc_->getJID()) {
+ foreach (const JID& jid, inviteEvent->getInvites()) {
+ muc_->invitePerson(jid, inviteEvent->getReason(), isImpromptu_);
+ }
+ }
}
@@ -751,3 +961,124 @@ void MUCController::handleAffiliationListReceived(MUCOccupant::Affiliation affil
}
+void MUCController::logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) {
+ // log only incoming messages
+ if (isIncoming && historyController_) {
+ historyController_->addMessage(message, fromJID, toJID, HistoryMessage::Groupchat, timeStamp);
+ }
+}
+
+void MUCController::addRecentLogs() {
+ if (!historyController_) {
+ return;
+ }
+
+ joinContext_ = historyController_->getMUCContext(selfJID_, toJID_, lastActivity_);
+
+ foreach (const HistoryMessage& message, joinContext_) {
+ bool senderIsSelf = nick_ == message.getFromJID().getResource();
+
+ // the chatWindow uses utc timestamps
+ addMessage(message.getMessage(), senderDisplayNameFromMessage(message.getFromJID()), senderIsSelf, boost::shared_ptr<SecurityLabel>(new SecurityLabel()), avatarManager_->getAvatarPath(message.getFromJID()), message.getTime() - boost::posix_time::hours(message.getOffset()), HighlightAction());
+ }
+}
+
+void MUCController::checkDuplicates(boost::shared_ptr<Message> newMessage) {
+ std::string body = newMessage->getBody();
+ JID jid = newMessage->getFrom();
+ boost::optional<boost::posix_time::ptime> time = newMessage->getTimestamp();
+
+ reverse_foreach (const HistoryMessage& message, joinContext_) {
+ boost::posix_time::ptime messageTime = message.getTime() - boost::posix_time::hours(message.getOffset());
+ if (time && time < messageTime) {
+ break;
+ }
+ if (time && time != messageTime) {
+ continue;
+ }
+ if (message.getFromJID() != jid) {
+ continue;
+ }
+ if (message.getMessage() != body) {
+ continue;
+ }
+
+ // Mark the message as unreadable
+ newMessage->setBody("");
+ }
+}
+
+void MUCController::setNick(const std::string& nick) {
+ nick_ = nick;
+ highlighter_->setNick(nick_);
+}
+
+Form::ref MUCController::buildImpromptuRoomConfiguration(Form::ref roomConfigurationForm) {
+ Form::ref result = boost::make_shared<Form>(Form::SubmitType);
+ std::string impromptuConfigs[] = { "muc#roomconfig_enablelogging", "muc#roomconfig_persistentroom", "muc#roomconfig_publicroom", "muc#roomconfig_whois"};
+ std::set<std::string> impromptuConfigsMissing(impromptuConfigs, impromptuConfigs + 4);
+ foreach (boost::shared_ptr<FormField> field, roomConfigurationForm->getFields()) {
+ boost::shared_ptr<FormField> resultField;
+ if (field->getName() == "muc#roomconfig_enablelogging") {
+ resultField = boost::make_shared<FormField>(FormField::BooleanType, "0");
+ }
+ if (field->getName() == "muc#roomconfig_persistentroom") {
+ resultField = boost::make_shared<FormField>(FormField::BooleanType, "0");
+ }
+ if (field->getName() == "muc#roomconfig_publicroom") {
+ resultField = boost::make_shared<FormField>(FormField::BooleanType, "0");
+ }
+ if (field->getName() == "muc#roomconfig_whois") {
+ resultField = boost::make_shared<FormField>(FormField::ListSingleType, "anyone");
+ }
+
+ if (field->getName() == "FORM_TYPE") {
+ resultField = boost::make_shared<FormField>(FormField::HiddenType, "http://jabber.org/protocol/muc#roomconfig");
+ }
+
+ if (resultField) {
+ impromptuConfigsMissing.erase(field->getName());
+ resultField->setName(field->getName());
+ result->addField(resultField);
+ }
+ }
+
+ foreach (const std::string& config, impromptuConfigsMissing) {
+ if (config == "muc#roomconfig_publicroom") {
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(QT_TRANSLATE_NOOP("", "This server doesn't support hiding your chat from other users.")), ChatWindow::DefaultDirection);
+ } else if (config == "muc#roomconfig_whois") {
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(QT_TRANSLATE_NOOP("", "This server doesn't support sharing people's real identity in this chat.")), ChatWindow::DefaultDirection);
+ }
+ }
+
+ return result;
+}
+
+void MUCController::setImpromptuWindowTitle() {
+ std::string title;
+ typedef std::pair<std::string, MUCOccupant> StringMUCOccupantPair;
+ std::map<std::string, MUCOccupant> occupants = muc_->getOccupants();
+ if (occupants.size() <= 1) {
+ title = QT_TRANSLATE_NOOP("", "Empty Chat");
+ } else {
+ foreach (StringMUCOccupantPair pair, occupants) {
+ if (pair.first != nick_) {
+ title += (title.empty() ? "" : ", ") + pair.first;
+ }
+ }
+ }
+ chatWindow_->setName(title);
+}
+
+void MUCController::handleRoomUnlocked() {
+ // Handle buggy MUC implementations where the joined room already exists and is unlocked.
+ // Configure the room again in this case.
+ if (!isImpromptuAlreadyConfigured_) {
+ if (isImpromptu_ && (muc_->getOccupant(nick_).getAffiliation() == MUCOccupant::Owner)) {
+ muc_->requestConfigurationForm();
+ } else if (isImpromptu_) {
+ onImpromptuConfigCompleted();
+ }
+ }
+}
+
}