summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2010-07-23 14:47:48 (GMT)
committerKevin Smith <git@kismith.co.uk>2010-07-23 15:01:33 (GMT)
commit4cc96003c6702168da2faa955e3c771272211e32 (patch)
treeb15355b09676583b2fb468f616f3dd284c52eac9
parente00480d2f4326decd23ff7665fcb1af5e752c8ec (diff)
downloadswift-4cc96003c6702168da2faa955e3c771272211e32.zip
swift-4cc96003c6702168da2faa955e3c771272211e32.tar.bz2
Recognise when leaving a MUC (disconnect or kick).
Also cleans up some outstanding MUC issues. Resolves: #288 Resolves: #392 Resolves: #279 Resolves: #114
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.h2
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp10
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp48
-rw-r--r--Swift/Controllers/Chat/MUCController.h2
-rw-r--r--Swift/QtUI/Roster/RosterModel.cpp2
-rw-r--r--Swiften/MUC/MUC.cpp30
-rw-r--r--Swiften/MUC/MUC.h4
-rw-r--r--Swiften/Roster/GroupRosterItem.cpp21
-rw-r--r--Swiften/Roster/GroupRosterItem.h1
-rw-r--r--Swiften/Roster/Roster.cpp7
-rw-r--r--Swiften/Roster/Roster.h1
11 files changed, 105 insertions, 23 deletions
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index f0d4b7a..61d0ab7 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -41,7 +41,7 @@ namespace Swift {
void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info);
void handleIncomingMessage(boost::shared_ptr<MessageEvent> message);
void addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath, const boost::posix_time::ptime& time);
- void setEnabled(bool enabled);
+ virtual void setEnabled(bool enabled);
virtual void setToJID(const JID& jid) {toJID_ = jid;};
protected:
ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController);
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 2ed7051..921cc6f 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -139,12 +139,18 @@ void ChatsManager::setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info) {
}
}
+/**
+ * This is to be called on connect/disconnect.
+ */
void ChatsManager::setEnabled(bool enabled) {
foreach (JIDChatControllerPair controllerPair, chatControllers_) {
controllerPair.second->setEnabled(enabled);
}
foreach (JIDMUCControllerPair controllerPair, mucControllers_) {
controllerPair.second->setEnabled(enabled);
+ if (enabled) {
+ controllerPair.second->rejoin();
+ }
}
}
@@ -201,9 +207,9 @@ void ChatsManager::rebindControllerJID(const JID& from, const JID& to) {
void ChatsManager::handleJoinMUCRequest(const JID &muc, const boost::optional<String>& nickMaybe) {
std::map<JID, MUCController*>::iterator it = mucControllers_.find(muc);
if (it != mucControllers_.end()) {
- //FIXME: What's correct behaviour here?
+ it->second->rejoin();
} else {
- String nick = nickMaybe ? nickMaybe.get() : "Swift user";
+ String nick = nickMaybe ? nickMaybe.get() : jid_.getNode();
MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, presenceSender_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_);
mucControllers_[muc] = controller;
controller->setAvailableServerFeatures(serverDiscoInfo_);
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index 1e790b7..650a915 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -46,10 +46,9 @@ MUCController::MUCController (
bool useDelayForLatency,
TimerFactory* timerFactory,
EventController* eventController) :
- ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController),
- muc_(new MUC(stanzaChannel, presenceSender, muc)),
- nick_(nick) {
- parting_ = false;
+ ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController), muc_(new MUC(stanzaChannel, presenceSender, muc)), nick_(nick) {
+ parting_ = true;
+ joined_ = false;
events_ = uiEventStream;
roster_ = new Roster(true);
@@ -69,11 +68,9 @@ MUCController::MUCController (
loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this));
loginCheckTimer_->start();
}
-
- muc_->joinAs(nick);
chatWindow_->convertToMUC();
chatWindow_->addSystemMessage("Trying to join room " + toJID_.toString());
- joined_ = false;
+ rejoin();
if (avatarManager_ != NULL) {
avatarChangedConnection_ = (avatarManager_->onAvatarChanged.connect(boost::bind(&MUCController::handleAvatarChanged, this, _1, _2)));
}
@@ -90,6 +87,17 @@ MUCController::~MUCController() {
delete completer_;
}
+/**
+ * Join the MUC if not already in it.
+ */
+void MUCController::rejoin() {
+ if (parting_) {
+ joined_ = false;
+ parting_ = false;
+ muc_->joinAs(nick_);
+ }
+}
+
void MUCController::handleJoinTimeoutTick() {
receivedActivity();
chatWindow_->addSystemMessage("Room " + toJID_.toString() + " is not responding. This operation may never complete");
@@ -122,7 +130,7 @@ void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) {
chatWindow_->addErrorMessage(errorMessage);
if (!rejoinNick.isEmpty()) {
nick_ = rejoinNick;
- muc_->joinAs(rejoinNick);
+ rejoin();
}
}
@@ -132,6 +140,7 @@ void MUCController::handleJoinComplete(const String& nick) {
String joinMessage = "You have joined room " + toJID_.toString() + " as " + nick;
nick_ = nick;
chatWindow_->addSystemMessage(joinMessage);
+ setEnabled(true);
}
void MUCController::handleAvatarChanged(const JID& jid, const String&) {
@@ -194,7 +203,7 @@ bool MUCController::messageTargetsMe(boost::shared_ptr<Message> message) {
void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) {
boost::shared_ptr<Message> message = messageEvent->getStanza();
- if (messageTargetsMe(message)) {
+ if (joined_ && messageTargetsMe(message)) {
eventController_->handleIncomingEvent(messageEvent);
}
String nick = message->getFrom().getResource();
@@ -235,15 +244,32 @@ String MUCController::roleToGroupName(MUCOccupant::Role role) {
return result;
}
+void MUCController::setEnabled(bool enabled) {
+ ChatControllerBase::setEnabled(enabled);
+ if (!enabled) {
+ roster_->removeAll();
+ /* handleUserLeft won't throw a part back up unless this is called
+ when it doesn't yet know we've left - which only happens on
+ disconnect, so call with disconnect here so if the signal does
+ bubble back up, it'll be with the right type.*/
+ muc_->handleUserLeft(MUC::Disconnect);
+ }
+}
+
void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType, const String& reason) {
completer_->removeWord(occupant.getNick());
- String partMessage = occupant.getNick() + " has left the room";
+ String partMessage = (occupant.getNick() != nick_) ? occupant.getNick() + " has left the room" : "You have left the room";
if (!reason.isEmpty()) {
partMessage += " (" + reason + ")";
}
partMessage += ".";
chatWindow_->addSystemMessage(partMessage);
- roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick()));
+ if (occupant.getNick() != nick_) {
+ roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick()));
+ } else {
+ parting_ = true;
+ setEnabled(false);
+ }
}
void MUCController::handleOccupantPresenceChange(boost::shared_ptr<Presence> presence) {
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index 65ef84c..7d40ec2 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -35,6 +35,8 @@ namespace Swift {
MUCController(const JID& self, const JID &muc, const String &nick, StanzaChannel* stanzaChannel, PresenceSender* presenceSender, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController);
~MUCController();
boost::signal<void ()> onUserLeft;
+ virtual void setEnabled(bool enabled);
+ void rejoin();
protected:
void preSendMessageRequest(boost::shared_ptr<Message> message);
diff --git a/Swift/QtUI/Roster/RosterModel.cpp b/Swift/QtUI/Roster/RosterModel.cpp
index d705d34..c2d4147 100644
--- a/Swift/QtUI/Roster/RosterModel.cpp
+++ b/Swift/QtUI/Roster/RosterModel.cpp
@@ -177,7 +177,7 @@ QModelIndex RosterModel::index(RosterItem* item) const {
/* Recursive check that it's ok to create such an item
Assuming there are more contacts in a group than groups in a
group, this could save a decent chunk of search time at startup.*/
- if (parent != roster_->getRoot() && !index(parent).isValid()) {
+ if (parent == NULL || (parent != roster_->getRoot() && !index(parent).isValid())) {
return QModelIndex();
}
for (size_t i = 0; i < parent->getDisplayedChildren().size(); i++) {
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
index b4265b5..91ba043 100644
--- a/Swiften/MUC/MUC.cpp
+++ b/Swiften/MUC/MUC.cpp
@@ -44,6 +44,18 @@ void MUC::part() {
presenceSender->removeDirectedPresenceReceiver(ownMUCJID);
}
+void MUC::handleUserLeft(LeavingType type) {
+ std::map<String,MUCOccupant>::iterator i = occupants.find(ownMUCJID.getResource());
+ if (i != occupants.end()) {
+ MUCOccupant me = i->second;
+ occupants.erase(i);
+ onOccupantLeft(me, type, "");
+ }
+ occupants.clear();
+ joinComplete_ = false;
+ presenceSender->removeDirectedPresenceReceiver(ownMUCJID);
+}
+
void MUC::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
if (!isFromMUC(presence->getFrom())) {
return;
@@ -79,14 +91,18 @@ void MUC::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
//170 is room logging to http
//TODO: Nick changes
if (presence->getType() == Presence::Unavailable) {
- std::map<String,MUCOccupant>::iterator i = occupants.find(nick);
- if (i != occupants.end()) {
- //TODO: part type
- onOccupantLeft(i->second, Part, "");
- occupants.erase(i);
+ if (presence->getFrom() == ownMUCJID) {
+ handleUserLeft(Part);
+ return;
+ } else {
+ std::map<String,MUCOccupant>::iterator i = occupants.find(nick);
+ if (i != occupants.end()) {
+ //TODO: part type
+ onOccupantLeft(i->second, Part, "");
+ occupants.erase(i);
+ }
}
- }
- else if (presence->getType() == Presence::Available) {
+ } else if (presence->getType() == Presence::Available) {
std::map<String, MUCOccupant>::iterator it = occupants.find(nick);
MUCOccupant occupant(nick, role, affiliation);
if (it != occupants.end()) {
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index 40fc2f6..bccc26c 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -25,7 +25,7 @@ namespace Swift {
class MUC {
public:
enum JoinResult { JoinSucceeded, JoinFailed };
- enum LeavingType { Part };
+ enum LeavingType { Part, Disconnect };
public:
MUC(StanzaChannel* stanzaChannel, PresenceSender* presenceSender, const JID &muc);
@@ -36,6 +36,8 @@ namespace Swift {
String getCurrentNick();
void part();
void handleIncomingMessage(boost::shared_ptr<Message> message);
+ /** Expose public so it can be called when e.g. user goes offline */
+ void handleUserLeft(LeavingType);
public:
boost::signal<void (const String& /*nick*/)> onJoinComplete;
diff --git a/Swiften/Roster/GroupRosterItem.cpp b/Swiften/Roster/GroupRosterItem.cpp
index e5490e2..8e4be35 100644
--- a/Swiften/Roster/GroupRosterItem.cpp
+++ b/Swiften/Roster/GroupRosterItem.cpp
@@ -55,6 +55,27 @@ void GroupRosterItem::addChild(RosterItem* item) {
}
/**
+ * Does not emit a changed signal.
+ */
+void GroupRosterItem::removeAll() {
+ std::vector<RosterItem*>::iterator it = children_.begin();
+ displayedChildren_.clear();
+ while (it != children_.end()) {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(*it);
+ if (contact) {
+ delete contact;
+ }
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(*it);
+ if (group) {
+ group->removeAll();
+ delete group;
+ }
+ it++;
+ }
+ children_.clear();
+}
+
+/**
* Returns the removed item - but only if it's the only one, otherwise
* the return result is undefined.
*/
diff --git a/Swiften/Roster/GroupRosterItem.h b/Swiften/Roster/GroupRosterItem.h
index aca2b05..096d053 100644
--- a/Swiften/Roster/GroupRosterItem.h
+++ b/Swiften/Roster/GroupRosterItem.h
@@ -22,6 +22,7 @@ class GroupRosterItem : public RosterItem {
const std::vector<RosterItem*>& getDisplayedChildren() const;
void addChild(RosterItem* item);
ContactRosterItem* removeChild(const JID& jid);
+ void removeAll();
void setDisplayed(RosterItem* item, bool displayed);
boost::signal<void ()> onChildrenChanged;
static bool itemLessThan(const RosterItem* left, const RosterItem* right);
diff --git a/Swiften/Roster/Roster.cpp b/Swiften/Roster/Roster.cpp
index b7ed6c4..e91b843 100644
--- a/Swiften/Roster/Roster.cpp
+++ b/Swiften/Roster/Roster.cpp
@@ -82,6 +82,13 @@ struct JIDEqualsTo {
JID jid;
};
+void Roster::removeAll() {
+ root_->removeAll();
+ itemMap_.clear();
+ onChildrenChanged(root_);
+ onDataChanged(root_);
+}
+
void Roster::removeContact(const JID& jid) {
std::vector<ContactRosterItem*>* items = &itemMap_[fullJIDMapping_ ? jid : jid.toBare()];
items->erase(std::remove_if(items->begin(), items->end(), JIDEqualsTo(jid)), items->end());
diff --git a/Swiften/Roster/Roster.h b/Swiften/Roster/Roster.h
index 346157e..d54a12e 100644
--- a/Swiften/Roster/Roster.h
+++ b/Swiften/Roster/Roster.h
@@ -31,6 +31,7 @@ class Roster {
void addContact(const JID& jid, const JID& displayJID, const String& name, const String& group);
void removeContact(const JID& jid);
void removeContactFromGroup(const JID& jid, const String& group);
+ void removeAll();
void applyOnItems(const RosterItemOperation& operation);
void applyOnAllItems(const RosterItemOperation& operation);
void applyOnItem(const RosterItemOperation& operation, const JID& jid);