From 004dfd8d4305b767b624be10072597ef3e311753 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sun, 16 Jan 2011 20:56:10 +0100
Subject: Use a dedicated Join MUC dialog.


diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 722b98f..b7e8432 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -9,15 +9,20 @@
 #include <boost/bind.hpp>
 
 #include "Swift/Controllers/Chat/ChatController.h"
+#include "Swift/Controllers/Chat/MUCSearchController.h"
 #include "Swift/Controllers/XMPPEvents/EventController.h"
 #include "Swift/Controllers/Chat/MUCController.h"
 #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
 #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
+#include "Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h"
 #include "Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h"
 #include "Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h"
 #include "Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h"
 #include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h"
+#include "Swift/Controllers/UIInterfaces/JoinMUCWindow.h"
+#include "Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h"
 #include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Client/NickResolver.h"
 #include "Swiften/MUC/MUCManager.h"
 #include "Swiften/Elements/ChatState.h"
 #include "Swiften/MUC/MUCBookmarkManager.h"
@@ -27,7 +32,30 @@ namespace Swift {
 typedef std::pair<JID, ChatController*> JIDChatControllerPair;
 typedef std::pair<JID, MUCController*> JIDMUCControllerPair;
 
-ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager) : jid_(jid), useDelayForLatency_(useDelayForLatency), mucRegistry_(mucRegistry), entityCapsProvider_(entityCapsProvider), mucManager(mucManager) {
+ChatsManager::ChatsManager(
+		JID jid, StanzaChannel* stanzaChannel, 
+		IQRouter* iqRouter, 
+		EventController* eventController, 
+		ChatWindowFactory* chatWindowFactory, 
+		JoinMUCWindowFactory* joinMUCWindowFactory, 
+		NickResolver* nickResolver, 
+		PresenceOracle* presenceOracle, 
+		PresenceSender* presenceSender, 
+		UIEventStream* uiEventStream, 
+		ChatListWindowFactory* chatListWindowFactory, 
+		bool useDelayForLatency, 
+		TimerFactory* timerFactory, 
+		MUCRegistry* mucRegistry, 
+		EntityCapsProvider* entityCapsProvider, 
+		MUCManager* mucManager,
+		MUCSearchWindowFactory* mucSearchWindowFactory,
+		SettingsProvider* settings) : 
+			jid_(jid), 
+			joinMUCWindowFactory_(joinMUCWindowFactory), 
+			useDelayForLatency_(useDelayForLatency), 
+			mucRegistry_(mucRegistry), 
+			entityCapsProvider_(entityCapsProvider), 
+			mucManager(mucManager)  {
 	timerFactory_ = timerFactory;
 	eventController_ = eventController;
 	stanzaChannel_ = stanzaChannel;
@@ -43,10 +71,14 @@ ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRo
 	presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1));
 	uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&ChatsManager::handleUIEvent, this, _1));
 	chatListWindow_ = chatListWindowFactory->createChatListWindow(uiEventStream_);
+	joinMUCWindow_ = NULL;
+	mucSearchController_ = new MUCSearchController(jid_, mucSearchWindowFactory, iqRouter, settings);
+	mucSearchController_->onMUCSelected.connect(boost::bind(&ChatsManager::handleMUCSelectedAfterSearch, this, _1));
 	setupBookmarks();
 }
 
 ChatsManager::~ChatsManager() {
+	delete joinMUCWindow_;
 	foreach (JIDChatControllerPair controllerPair, chatControllers_) {
 		delete controllerPair.second;
 	}
@@ -54,6 +86,7 @@ ChatsManager::~ChatsManager() {
 		delete controllerPair.second;
 	}
 	delete mucBookmarkManager_;
+	delete mucSearchController_;
 }
 
 void ChatsManager::setupBookmarks() {
@@ -80,7 +113,7 @@ void ChatsManager::handleMUCBookmarkAdded(const MUCBookmark& bookmark) {
 	std::map<JID, MUCController*>::iterator it = mucControllers_.find(bookmark.getRoom());
 	if (it == mucControllers_.end() && bookmark.getAutojoin()) {
 		//FIXME: need vcard stuff here to get a nick
-		handleJoinMUCRequest(bookmark.getRoom(), bookmark.getNick());
+		handleJoinMUCRequest(bookmark.getRoom(), bookmark.getNick(), false);
 	}
 	chatListWindow_->addMUCBookmark(bookmark);
 }
@@ -106,11 +139,6 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {
 		handleChatRequest(chatEvent->getContact());
 		return;
 	}
-	boost::shared_ptr<JoinMUCUIEvent> joinMUCEvent = boost::dynamic_pointer_cast<JoinMUCUIEvent>(event);
-	if (joinMUCEvent) {
-		handleJoinMUCRequest(joinMUCEvent->getJID(), joinMUCEvent->getNick());
-		return;
-	}
 	boost::shared_ptr<RemoveMUCBookmarkUIEvent> removeMUCBookmarkEvent = boost::dynamic_pointer_cast<RemoveMUCBookmarkUIEvent>(event);
 	if (removeMUCBookmarkEvent) {
 		mucBookmarkManager_->removeBookmark(removeMUCBookmarkEvent->getBookmark());
@@ -121,10 +149,23 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {
 		mucBookmarkManager_->addBookmark(addMUCBookmarkEvent->getBookmark());
 		return;
 	}
+
 	boost::shared_ptr<EditMUCBookmarkUIEvent> editMUCBookmarkEvent = boost::dynamic_pointer_cast<EditMUCBookmarkUIEvent>(event);
 	if (editMUCBookmarkEvent) {
 		mucBookmarkManager_->replaceBookmark(editMUCBookmarkEvent->getOldBookmark(), editMUCBookmarkEvent->getNewBookmark());
-		return;
+	}
+	else if (JoinMUCUIEvent::ref joinEvent = boost::dynamic_pointer_cast<JoinMUCUIEvent>(event)) {
+		handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getNick(), false);
+	}
+	else if (boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) {
+		if (!joinMUCWindow_) {
+			joinMUCWindow_ = joinMUCWindowFactory_->createJoinMUCWindow();
+			joinMUCWindow_->onJoinMUC.connect(boost::bind(&ChatsManager::handleJoinMUCRequest, this, _1, _2, _3));
+			joinMUCWindow_->onSearchMUC.connect(boost::bind(&ChatsManager::handleSearchMUCRequest, this));
+		}
+		joinMUCWindow_->setMUC("");
+		joinMUCWindow_->setNick(nickResolver_->jidToNick(jid_));
+		joinMUCWindow_->show();
 	}
 }
 
@@ -239,7 +280,16 @@ void ChatsManager::rebindControllerJID(const JID& from, const JID& to) {
 	chatControllers_[to]->setToJID(to);
 }
 
-void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<String>& nickMaybe) {
+void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<String>& nickMaybe, bool autoJoin) {
+	if (autoJoin) {
+		MUCBookmark bookmark(mucJID, mucJID.getNode());
+		bookmark.setAutojoin(true);
+		if (nickMaybe) {
+			bookmark.setNick(*nickMaybe);
+		}
+		mucBookmarkManager_->addBookmark(bookmark);
+	}
+
 	std::map<JID, MUCController*>::iterator it = mucControllers_.find(mucJID);
 	if (it != mucControllers_.end()) {
 		it->second->rejoin();
@@ -254,6 +304,10 @@ void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional
 	mucControllers_[mucJID]->activateChatWindow();
 }
 
+void ChatsManager::handleSearchMUCRequest() {
+	mucSearchController_->openSearchWindow();
+}
+
 void ChatsManager::handleIncomingMessage(boost::shared_ptr<Message> message) {
 	JID jid = message->getFrom();
 	boost::shared_ptr<MessageEvent> event(new MessageEvent(message));
@@ -278,4 +332,11 @@ void ChatsManager::handleIncomingMessage(boost::shared_ptr<Message> message) {
 	getChatControllerOrCreate(jid)->handleIncomingMessage(event);
 }
 
+void ChatsManager::handleMUCSelectedAfterSearch(const JID& muc) {
+	if (joinMUCWindow_) {
+		joinMUCWindow_->setMUC(muc.toString());
+	}
+}
+
+
 }
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index 2b771eb..62b14d9 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -25,6 +25,8 @@ namespace Swift {
 	class MUCController;
 	class MUCManager;
 	class ChatWindowFactory;
+	class JoinMUCWindow;
+	class JoinMUCWindowFactory;
 	class NickResolver;
 	class PresenceOracle;
 	class AvatarManager;
@@ -37,10 +39,13 @@ namespace Swift {
 	class TimerFactory;
 	class EntityCapsProvider;
 	class DirectedPresenceSender;
+	class MUCSearchWindowFactory;
+	class SettingsProvider;
+	class MUCSearchController;
 
 	class ChatsManager {
 		public:
-			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager);
+			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, SettingsProvider* settings);
 			virtual ~ChatsManager();
 			void setAvatarManager(AvatarManager* avatarManager);
 			void setOnline(bool enabled);
@@ -48,7 +53,9 @@ namespace Swift {
 			void handleIncomingMessage(boost::shared_ptr<Message> message);
 		private:
 			void handleChatRequest(const String& contact);
-			void handleJoinMUCRequest(const JID& muc, const boost::optional<String>& nick);
+			void handleJoinMUCRequest(const JID& muc, const boost::optional<String>& nick, bool autoJoin);
+			void handleSearchMUCRequest();
+			void handleMUCSelectedAfterSearch(const JID&);
 			void rebindControllerJID(const JID& from, const JID& to);
 			void handlePresenceChange(boost::shared_ptr<Presence> newPresence);
 			void handleUIEvent(boost::shared_ptr<UIEvent> event);
@@ -68,6 +75,7 @@ namespace Swift {
 			StanzaChannel* stanzaChannel_;
 			IQRouter* iqRouter_;
 			ChatWindowFactory* chatWindowFactory_;
+			JoinMUCWindowFactory* joinMUCWindowFactory_;
 			NickResolver* nickResolver_;
 			PresenceOracle* presenceOracle_;
 			AvatarManager* avatarManager_;
@@ -76,11 +84,13 @@ namespace Swift {
 			MUCBookmarkManager* mucBookmarkManager_;
 			boost::shared_ptr<DiscoInfo> serverDiscoInfo_;
 			ChatListWindow* chatListWindow_;
+			JoinMUCWindow* joinMUCWindow_;
 			boost::bsignals::scoped_connection uiEventConnection_;
 			bool useDelayForLatency_;
 			TimerFactory* timerFactory_;
 			MUCRegistry* mucRegistry_;
 			EntityCapsProvider* entityCapsProvider_;
 			MUCManager* mucManager;
+			MUCSearchController* mucSearchController_;
 	};
 }
diff --git a/Swift/Controllers/Chat/MUCSearchController.cpp b/Swift/Controllers/Chat/MUCSearchController.cpp
index c254e51..c85f793 100644
--- a/Swift/Controllers/Chat/MUCSearchController.cpp
+++ b/Swift/Controllers/Chat/MUCSearchController.cpp
@@ -11,12 +11,9 @@
 #include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
 
-#include <Swiften/Disco/GetDiscoInfoRequest.h>
 #include <Swiften/Disco/GetDiscoItemsRequest.h>
-
+#include <Swiften/Base/Log.h>
 #include <Swift/Controllers/UIEvents/UIEventStream.h>
-#include <Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h>
-#include <Swift/Controllers/UIInterfaces/MUCSearchWindow.h>
 #include <Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h>
 #include <Swift/Controllers/DiscoServiceWalker.h>
 #include <Swiften/Client/NickResolver.h>
@@ -25,57 +22,43 @@ namespace Swift {
 
 static const String SEARCHED_SERVICES = "searchedServices";
 
-MUCSearchController::MUCSearchController(const JID& jid, UIEventStream* uiEventStream, MUCSearchWindowFactory* factory, IQRouter* iqRouter, SettingsProvider* settings, NickResolver *nickResolver) : jid_(jid) {
-	iqRouter_ = iqRouter;
-	settings_ = settings;
-	uiEventStream_ = uiEventStream;
-	nickResolver_ = nickResolver;
+MUCSearchController::MUCSearchController(const JID& jid, MUCSearchWindowFactory* factory, IQRouter* iqRouter, SettingsProvider* settings) : jid_(jid), factory_(factory), iqRouter_(iqRouter), settings_(settings), window_(NULL), walker_(NULL) {
 	itemsInProgress_ = 0;
-	uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&MUCSearchController::handleUIEvent, this, _1));
-	window_ = NULL;
-	factory_ = factory;
-	loadServices();
+	loadSavedServices();
 }
 
 MUCSearchController::~MUCSearchController() {
-	foreach (DiscoServiceWalker* walker, walksInProgress_) {
-		delete walker;
-	}
+	delete walker_;
 	delete window_;
 }
 
-void MUCSearchController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
-	boost::shared_ptr<RequestMUCSearchUIEvent> searchEvent = boost::dynamic_pointer_cast<RequestMUCSearchUIEvent>(event);
-	if (searchEvent) {
-		if (!window_) {
-			window_ = factory_->createMUCSearchWindow(uiEventStream_);
-			window_->onAddService.connect(boost::bind(&MUCSearchController::handleAddService, this, _1));
-			window_->addSavedServices(savedServices_);
-			handleAddService(JID(jid_.getDomain()));
-		}
-		window_->setMUC("");
-		window_->setNick(nickResolver_->jidToNick(jid_));
-		window_->show();
-		return;
+void MUCSearchController::openSearchWindow() {
+	if (!window_) {
+		window_ = factory_->createMUCSearchWindow();
+		window_->onSearchService.connect(boost::bind(&MUCSearchController::handleSearchService, this, _1));
+		window_->onFinished.connect(boost::bind(&MUCSearchController::handleMUCSearchFinished, this, _1));
+		window_->addSavedServices(savedServices_);
+		handleSearchService(JID(jid_.getDomain()));
 	}
+	window_->show();
 }
 
-void MUCSearchController::loadServices() {
+void MUCSearchController::loadSavedServices() {
 	savedServices_.clear();
 	foreach (String stringItem, settings_->getStringSetting(SEARCHED_SERVICES).split('\n')) {
 		savedServices_.push_back(JID(stringItem));
 	}
 }
 
-void MUCSearchController::addAndSaveServices(const JID& jid) {
+void MUCSearchController::addToSavedServices(const JID& jid) {
 	savedServices_.erase(std::remove(savedServices_.begin(), savedServices_.end(), jid), savedServices_.end());
-	savedServices_.push_back(jid);
+	savedServices_.push_front(jid);
+
 	String collapsed;
-	bool storeThis = savedServices_.size() < 15;
+	int i = 0;
 	foreach (JID jidItem, savedServices_) {
-		if (!storeThis) {
-			storeThis = true;
-			continue;
+		if (i >= 15) {
+			break;
 		}
 		if (!collapsed.isEmpty()) {
 			collapsed += "\n";
@@ -86,21 +69,32 @@ void MUCSearchController::addAndSaveServices(const JID& jid) {
 	window_->addSavedServices(savedServices_);
 }
 
-void MUCSearchController::handleAddService(const JID& jid) {
+void MUCSearchController::handleSearchService(const JID& jid) {
 	if (!jid.isValid()) {
 		//Set Window to say error this isn't valid
 		return;
 	}
-	addAndSaveServices(jid);
-	services_.push_back(jid);
-	serviceDetails_[jid].setComplete(false);
+	addToSavedServices(jid);
+
+	services_.clear();
+	serviceDetails_.clear();
+
 	window_->setSearchInProgress(true);
 	refreshView();
-	DiscoServiceWalker* walker = new DiscoServiceWalker(jid, iqRouter_);
-	walker->onServiceFound.connect(boost::bind(&MUCSearchController::handleDiscoServiceFound, this, _1, _2));
-	walker->onWalkComplete.connect(boost::bind(&MUCSearchController::handleDiscoWalkFinished, this, walker));
-	walksInProgress_.push_back(walker);
-	walker->beginWalk();
+
+	if (walker_) {
+		walker_->endWalk();
+		walker_->onServiceFound.disconnect(boost::bind(&MUCSearchController::handleDiscoServiceFound, this, _1, _2));
+		walker_->onWalkComplete.disconnect(boost::bind(&MUCSearchController::handleDiscoWalkFinished, this));
+		delete walker_;
+	}
+
+	SWIFT_LOG(debug) << "Starting walking MUC services" << std::endl;
+	itemsInProgress_ = 0;
+	walker_ = new DiscoServiceWalker(jid, iqRouter_);
+	walker_->onServiceFound.connect(boost::bind(&MUCSearchController::handleDiscoServiceFound, this, _1, _2));
+	walker_->onWalkComplete.connect(boost::bind(&MUCSearchController::handleDiscoWalkFinished, this));
+	walker_->beginWalk();
 }
 
 void MUCSearchController::handleDiscoServiceFound(const JID& jid, boost::shared_ptr<DiscoInfo> info) {
@@ -116,25 +110,27 @@ void MUCSearchController::handleDiscoServiceFound(const JID& jid, boost::shared_
 			}
 	}
 	if (isMUC) {
-		services_.erase(std::remove(services_.begin(), services_.end(), jid), services_.end()); /* Bring it back to the end on a refresh */
+		SWIFT_LOG(debug) << "MUC Service found: " << jid << std::endl;
+		services_.erase(std::remove(services_.begin(), services_.end(), jid), services_.end());
 		services_.push_back(jid);
 		serviceDetails_[jid].setName(name);
 		serviceDetails_[jid].setJID(jid);
 		serviceDetails_[jid].setComplete(false);
 		itemsInProgress_++;
+		SWIFT_LOG(debug) << "Requesting items of " << jid << " (" << itemsInProgress_ << " item requests in progress)" << std::endl;
 		GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(jid, iqRouter_);
 		discoItemsRequest->onResponse.connect(boost::bind(&MUCSearchController::handleRoomsItemsResponse, this, _1, _2, jid));
 		discoItemsRequest->send();
-	} else {
+	} 
+	else {
 		removeService(jid);
 	}
 	refreshView();
 }
 
-void MUCSearchController::handleDiscoWalkFinished(DiscoServiceWalker* walker) {
-	walksInProgress_.erase(std::remove(walksInProgress_.begin(), walksInProgress_.end(), walker), walksInProgress_.end());
+void MUCSearchController::handleDiscoWalkFinished() {
+	SWIFT_LOG(debug) << "MUC Walk finished" << std::endl;
 	updateInProgressness();
-	delete walker;
 }
 
 void MUCSearchController::removeService(const JID& jid) {
@@ -145,6 +141,7 @@ void MUCSearchController::removeService(const JID& jid) {
 
 void MUCSearchController::handleRoomsItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error, const JID& jid) {
 	itemsInProgress_--;
+	SWIFT_LOG(debug) << "Items received for " << jid << " (" << itemsInProgress_ << " item requests in progress)" << std::endl;
 	updateInProgressness();
 	if (error) {
 		handleDiscoError(jid, error);
@@ -171,7 +168,13 @@ void MUCSearchController::refreshView() {
 }
 
 void MUCSearchController::updateInProgressness() {
-	window_->setSearchInProgress(walksInProgress_.size() + itemsInProgress_ > 0);
+	window_->setSearchInProgress((walker_ && walker_->isActive()) || itemsInProgress_ > 0);
+}
+
+void MUCSearchController::handleMUCSearchFinished(const boost::optional<JID>& result) {
+	if (result) {
+		onMUCSelected(*result);
+	}
 }
 
 }
diff --git a/Swift/Controllers/Chat/MUCSearchController.h b/Swift/Controllers/Chat/MUCSearchController.h
index 6caee54..6d3afd1 100644
--- a/Swift/Controllers/Chat/MUCSearchController.h
+++ b/Swift/Controllers/Chat/MUCSearchController.h
@@ -88,32 +88,38 @@ namespace Swift {
 
 	class MUCSearchController {
 		public:
-			MUCSearchController(const JID& jid, UIEventStream* uiEventStream, MUCSearchWindowFactory* mucSearchWindowFactory, IQRouter* iqRouter, SettingsProvider* settings, NickResolver* nickResolver);
+			MUCSearchController(const JID& jid, MUCSearchWindowFactory* mucSearchWindowFactory, IQRouter* iqRouter, SettingsProvider* settings);
 			~MUCSearchController();
+
+			void openSearchWindow();
+
+		public:
+			boost::signal<void (const JID&)> onMUCSelected;
+
 		private:
-			void handleUIEvent(boost::shared_ptr<UIEvent> event);
-			void handleAddService(const JID& jid);
+			void handleSearchService(const JID& jid);
 			void handleRoomsItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error, const JID& jid);
 			void handleDiscoError(const JID& jid, ErrorPayload::ref error);
 			void handleDiscoServiceFound(const JID&, boost::shared_ptr<DiscoInfo>);
-			void handleDiscoWalkFinished(DiscoServiceWalker* walker);
+			void handleDiscoWalkFinished();
+			void handleMUCSearchFinished(const boost::optional<JID>& result);
 			void removeService(const JID& jid);
 			void refreshView();
-			void loadServices();
-			void addAndSaveServices(const JID& jid);
+			void loadSavedServices();
+			void addToSavedServices(const JID& jid);
 			void updateInProgressness();
-			UIEventStream* uiEventStream_;
-			MUCSearchWindow* window_;
+
+		private:
+			JID jid_;
 			MUCSearchWindowFactory* factory_;
+			IQRouter* iqRouter_;
 			SettingsProvider* settings_;
-			NickResolver* nickResolver_;
-			boost::bsignals::scoped_connection uiEventConnection_;
-			std::vector<JID> services_;
-			std::vector<JID> savedServices_;
+			MUCSearchWindow* window_;
+			DiscoServiceWalker* walker_;
+			std::list<JID> services_;
+			std::list<JID> savedServices_;
 			std::map<JID, MUCService> serviceDetails_;
 			std::vector<DiscoServiceWalker*> walksInProgress_;
-			IQRouter* iqRouter_;
-			JID jid_;
 			int itemsInProgress_;
 	};
 }
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index 6d20f70..be262bc 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -11,8 +11,11 @@
 #include "Swift/Controllers/Chat/ChatsManager.h"
 
 #include "Swift/Controllers/UIInterfaces/ChatWindow.h"
+#include "Swift/Controllers/Settings/DummySettingsProvider.h"
 #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
 #include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h"
+#include "Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h"
+#include "Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h"
 #include "Swiften/Client/Client.h"
 #include "Swiften/Disco/EntityCapsManager.h"
 #include "Swiften/Disco/CapsProvider.h"
@@ -68,6 +71,7 @@ public:
 		capsProvider_ = new DummyCapsProvider();
 		eventController_ = new EventController();
 		chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();
+		joinMUCWindowFactory_ = mocks_->InterfaceMock<JoinMUCWindowFactory>();
 		xmppRoster_ = new XMPPRosterImpl();
 		mucRegistry_ = new MUCRegistry();
 		nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, NULL, mucRegistry_);
@@ -79,8 +83,10 @@ public:
 		uiEventStream_ = new UIEventStream();
 		entityCapsManager_ = new EntityCapsManager(capsProvider_, stanzaChannel_);
 		chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>();
+		mucSearchWindowFactory_ = mocks_->InterfaceMock<MUCSearchWindowFactory>();
+		settings_ = new DummySettingsProvider();
 		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(NULL);
-		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_);
+		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, settings_);
 
 		avatarManager_ = new NullAvatarManager();
 		manager_->setAvatarManager(avatarManager_);
@@ -88,6 +94,7 @@ public:
 	
 	void tearDown() {
 		//delete chatListWindowFactory_;
+		delete settings_;
 		delete mocks_;
 		delete avatarManager_;
 		delete manager_;
@@ -325,6 +332,7 @@ private:
 	IQRouter* iqRouter_;
 	EventController* eventController_;
 	ChatWindowFactory* chatWindowFactory_;
+	JoinMUCWindowFactory* joinMUCWindowFactory_;
 	NickResolver* nickResolver_;
 	PresenceOracle* presenceOracle_;
 	AvatarManager* avatarManager_;
@@ -334,11 +342,13 @@ private:
 	MockRepository* mocks_;
 	UIEventStream* uiEventStream_;
 	ChatListWindowFactory* chatListWindowFactory_;
+	MUCSearchWindowFactory* mucSearchWindowFactory_;
 	MUCRegistry* mucRegistry_;
 	DirectedPresenceSender* directedPresenceSender_;
 	EntityCapsManager* entityCapsManager_;
 	CapsProvider* capsProvider_;
 	MUCManager* mucManager_;
+	DummySettingsProvider* settings_;
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest);
diff --git a/Swift/Controllers/DiscoServiceWalker.cpp b/Swift/Controllers/DiscoServiceWalker.cpp
index 505acb4..15d2aaa 100644
--- a/Swift/Controllers/DiscoServiceWalker.cpp
+++ b/Swift/Controllers/DiscoServiceWalker.cpp
@@ -141,10 +141,12 @@ void DiscoServiceWalker::markNodeCompleted(const JID& jid) {
 	servicesBeingSearched_.erase(jid);
 	/* All results are in */
 	if (servicesBeingSearched_.size() == 0) {
+		active_ = false;
 		onWalkComplete();
 	}
 	/* Check if we're on a rampage */
-	if (searchedServices_.size() >= maxSteps_) {
+	else if (searchedServices_.size() >= maxSteps_) {
+		active_ = false;
 		onWalkComplete();
 	}
 }
diff --git a/Swift/Controllers/DiscoServiceWalker.h b/Swift/Controllers/DiscoServiceWalker.h
index 167174a..00e2436 100644
--- a/Swift/Controllers/DiscoServiceWalker.h
+++ b/Swift/Controllers/DiscoServiceWalker.h
@@ -41,6 +41,10 @@ namespace Swift {
 			 */
 			void endWalk();
 
+			bool isActive() const {
+				return active_;
+			}
+
 			/** Emitted for each service found. */
 			boost::signal<void(const JID&, boost::shared_ptr<DiscoInfo>)> onServiceFound;
 
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index bc40f95..f07a964 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -98,7 +98,6 @@ MainController::MainController(
 	rosterController_ = NULL;
 	chatsManager_ = NULL;
 	eventWindowController_ = NULL;
-	mucSearchController_ = NULL;
 	userSearchControllerChat_ = NULL;
 	userSearchControllerAdd_ = NULL;
 	quitRequested_ = false;
@@ -171,8 +170,6 @@ MainController::~MainController() {
 void MainController::resetClient() {
 	resetCurrentError();
 	resetPendingReconnects();
-	delete mucSearchController_;
-	mucSearchController_ = NULL;
 	delete eventWindowController_;
 	eventWindowController_ = NULL;
 	delete chatsManager_;
@@ -235,7 +232,7 @@ void MainController::handleConnected() {
 		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
 		rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
 
-		chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager());
+		chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, settings_);
 		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));
 		chatsManager_->setAvatarManager(client_->getAvatarManager());
 
@@ -251,7 +248,6 @@ void MainController::handleConnected() {
 		client_->getDiscoManager()->setDiscoInfo(discoInfo);
 
 
-		mucSearchController_ = new MUCSearchController(jid_, uiEventStream_, uiFactory_, client_->getIQRouter(), settings_, client_->getNickResolver());
 		userSearchControllerChat_ = new UserSearchController(UserSearchController::StartChat, jid_, uiEventStream_, uiFactory_, client_->getIQRouter());
 		userSearchControllerAdd_ = new UserSearchController(UserSearchController::AddContact, jid_, uiEventStream_, uiFactory_, client_->getIQRouter());
 	}
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 900319e..a933a5a 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -139,7 +139,6 @@ namespace Swift {
 			String certificateFile_;
 			boost::shared_ptr<ErrorEvent> lastDisconnectError_;
 			bool useDelayForLatency_;
-			MUCSearchController* mucSearchController_;
 			UserSearchController* userSearchControllerChat_;
 			UserSearchController* userSearchControllerAdd_;
 			int timeBeforeNextReconnect_;
diff --git a/Swift/Controllers/UIEvents/JoinMUCUIEvent.h b/Swift/Controllers/UIEvents/JoinMUCUIEvent.h
index d294fe8..2a2cd96 100644
--- a/Swift/Controllers/UIEvents/JoinMUCUIEvent.h
+++ b/Swift/Controllers/UIEvents/JoinMUCUIEvent.h
@@ -7,6 +7,7 @@
 #pragma once
 
 #include <boost/optional.hpp>
+#include <boost/shared_ptr.hpp>
 #include "Swiften/Base/String.h"
 
 #include "Swift/Controllers/UIEvents/UIEvent.h"
@@ -14,6 +15,7 @@
 namespace Swift {
 	class JoinMUCUIEvent : public UIEvent {
 		public:
+			typedef boost::shared_ptr<JoinMUCUIEvent> ref;
 			JoinMUCUIEvent(const JID& jid, const boost::optional<String>& nick = boost::optional<String>()) : jid_(jid), nick_(nick) {};
 			boost::optional<String> getNick() {return nick_;};
 			JID getJID() {return jid_;};
diff --git a/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h b/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h
new file mode 100644
index 0000000..1415140
--- /dev/null
+++ b/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/String.h>
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
+namespace Swift {
+	class RequestJoinMUCUIEvent : public UIEvent {
+		public:
+			typedef boost::shared_ptr<RequestJoinMUCUIEvent> ref;
+
+			RequestJoinMUCUIEvent() {
+			}
+	};
+}
diff --git a/Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h b/Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h
deleted file mode 100644
index 623cd00..0000000
--- a/Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright (c) 2010 Kevin Smith
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
- */
-
-#pragma once
-
-#include "Swift/Controllers/UIEvents/UIEvent.h"
-
-namespace Swift {
-	class RequestMUCSearchUIEvent : public UIEvent {
-
-	};
-}
diff --git a/Swift/Controllers/UIInterfaces/JoinMUCWindow.h b/Swift/Controllers/UIInterfaces/JoinMUCWindow.h
new file mode 100644
index 0000000..8cf712c
--- /dev/null
+++ b/Swift/Controllers/UIInterfaces/JoinMUCWindow.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Swiften/Base/String.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+	class JoinMUCWindow {
+		public:
+			virtual ~JoinMUCWindow() {};
+
+			virtual void setNick(const String& nick) = 0;
+			virtual void setMUC(const String& nick) = 0;
+			virtual void show() = 0;
+
+			boost::signal<void (const JID& /* muc */, const String& /* nick */, bool /* autoJoin */)> onJoinMUC;
+			boost::signal<void ()> onSearchMUC;
+	};
+}
diff --git a/Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h b/Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h
new file mode 100644
index 0000000..9c8bd77
--- /dev/null
+++ b/Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <Swift/Controllers/UIInterfaces/JoinMUCWindow.h>
+
+namespace Swift {
+	class JoinMUCWindowFactory {
+		public:
+			virtual ~JoinMUCWindowFactory() {};
+
+			virtual JoinMUCWindow* createJoinMUCWindow() = 0;
+	};
+}
diff --git a/Swift/Controllers/UIInterfaces/MUCSearchWindow.h b/Swift/Controllers/UIInterfaces/MUCSearchWindow.h
index 3c0ab12..ded2a0a 100644
--- a/Swift/Controllers/UIInterfaces/MUCSearchWindow.h
+++ b/Swift/Controllers/UIInterfaces/MUCSearchWindow.h
@@ -10,10 +10,10 @@
 
 #include <vector>
 
+#include <boost/optional.hpp>
 #include "Swiften/Base/String.h"
 #include "Swiften/JID/JID.h"
-
-#include "Swift/Controllers/Chat/MUCSearchController.h"
+#include <Swift/Controllers/Chat/MUCSearchController.h>
 
 namespace Swift {
 
@@ -21,15 +21,14 @@ namespace Swift {
 		public:
 			virtual ~MUCSearchWindow() {};
 
-			virtual void setNick(const String& nick) = 0;
-			virtual void setMUC(const String& nick) = 0;
 			virtual void clearList() = 0;
 			virtual void addService(const MUCService& service) = 0;
-			virtual void addSavedServices(const std::vector<JID>& services) = 0;
+			virtual void addSavedServices(const std::list<JID>& services) = 0;
 			virtual void setSearchInProgress(bool searching) = 0;
 
 			virtual void show() = 0;
 
-			boost::signal<void (const JID&)> onAddService;
+			boost::signal<void (const JID&)> onSearchService;
+			boost::signal<void (const boost::optional<JID>&)> onFinished;
 	};
 }
diff --git a/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h b/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h
index 1f0bf90..d334dff 100644
--- a/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h
+++ b/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h
@@ -14,6 +14,6 @@ namespace Swift {
 		public:
 			virtual ~MUCSearchWindowFactory() {};
 
-			virtual MUCSearchWindow* createMUCSearchWindow(UIEventStream* eventStream) = 0;
+			virtual MUCSearchWindow* createMUCSearchWindow() = 0;
 	};
 }
diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h
index acb7638..4783dc8 100644
--- a/Swift/Controllers/UIInterfaces/UIFactory.h
+++ b/Swift/Controllers/UIInterfaces/UIFactory.h
@@ -12,11 +12,21 @@
 #include <Swift/Controllers/UIInterfaces/LoginWindowFactory.h>
 #include <Swift/Controllers/UIInterfaces/MainWindowFactory.h>
 #include <Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h>
+#include <Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h>
 #include <Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h>
 #include <Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h>
 
 namespace Swift {
-	class UIFactory : public ChatListWindowFactory, public ChatWindowFactory, public EventWindowFactory, public LoginWindowFactory, public MainWindowFactory, public MUCSearchWindowFactory, public XMLConsoleWidgetFactory, public UserSearchWindowFactory {
+	class UIFactory : 
+			public ChatListWindowFactory, 
+			public ChatWindowFactory, 
+			public EventWindowFactory, 
+			public LoginWindowFactory, 
+			public MainWindowFactory, 
+			public MUCSearchWindowFactory, 
+			public XMLConsoleWidgetFactory, 
+			public UserSearchWindowFactory, 
+			public JoinMUCWindowFactory {
 		public:
 			virtual ~UIFactory() {}
 	};
diff --git a/Swift/QtUI/MUCSearch/MUCSearchEmptyItem.cpp b/Swift/QtUI/MUCSearch/MUCSearchEmptyItem.cpp
new file mode 100644
index 0000000..b1b4175
--- /dev/null
+++ b/Swift/QtUI/MUCSearch/MUCSearchEmptyItem.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swift/QtUI/MUCSearch/MUCSearchEmptyItem.h>
+
+#include <Swift/QtUI/MUCSearch/MUCSearchServiceItem.h>
+#include <QFont>
+#include <QColor>
+
+namespace Swift {
+MUCSearchEmptyItem::MUCSearchEmptyItem(MUCSearchServiceItem* parent) : parent(parent) {
+	parent->addRoom(this);
+}
+
+MUCSearchServiceItem* MUCSearchEmptyItem::getParent() {
+	return parent;
+}
+
+QVariant MUCSearchEmptyItem::data(int role) {
+	switch (role) {
+		case Qt::DisplayRole:
+			return QVariant("No rooms found");
+		case Qt::FontRole: {
+			QFont font;
+			font.setItalic(true);
+			return font;
+		}
+		case Qt::ForegroundRole:
+			return QColor(Qt::gray);
+		default:
+			return QVariant();
+	}
+}
+
+}
diff --git a/Swift/QtUI/MUCSearch/MUCSearchEmptyItem.h b/Swift/QtUI/MUCSearch/MUCSearchEmptyItem.h
new file mode 100644
index 0000000..d752ae3
--- /dev/null
+++ b/Swift/QtUI/MUCSearch/MUCSearchEmptyItem.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <Swift/QtUI/MUCSearch/MUCSearchItem.h>
+
+namespace Swift {
+	class MUCSearchServiceItem;
+
+	class MUCSearchEmptyItem : public MUCSearchItem {
+		public:
+			MUCSearchEmptyItem(MUCSearchServiceItem* parent);
+
+			MUCSearchServiceItem* getParent();
+
+			QVariant data(int role);
+
+		private:
+			MUCSearchServiceItem* parent;
+	};
+}
diff --git a/Swift/QtUI/MUCSearch/MUCSearchModel.cpp b/Swift/QtUI/MUCSearch/MUCSearchModel.cpp
index 2526039..c657190 100644
--- a/Swift/QtUI/MUCSearch/MUCSearchModel.cpp
+++ b/Swift/QtUI/MUCSearch/MUCSearchModel.cpp
@@ -5,6 +5,7 @@
  */
 
 #include "Swift/QtUI/MUCSearch/MUCSearchModel.h"
+#include "Swift/QtUI/MUCSearch/MUCSearchEmptyItem.h"
 
 namespace Swift {
 
@@ -39,7 +40,6 @@ QModelIndex MUCSearchModel::index(int row, int column, const QModelIndex & paren
 	if (parent.isValid()) {
 		MUCSearchServiceItem* parentItem = static_cast<MUCSearchServiceItem*>(parent.internalPointer());
 		return row < parentItem->rowCount() ? createIndex(row, column, parentItem->getItem(row)) : QModelIndex();
-		
 	} else {
 		return row < services_.size() ? createIndex(row, column, services_[row]) : QModelIndex();
 	}
@@ -55,10 +55,17 @@ QModelIndex MUCSearchModel::parent(const QModelIndex& index) const {
 	if (!item) {
 		return QModelIndex();
 	}
-	if (dynamic_cast<MUCSearchServiceItem*>(item)) {
+	else if (dynamic_cast<MUCSearchServiceItem*>(item)) {
 		return QModelIndex();
 	}
-	MUCSearchServiceItem* parent = dynamic_cast<MUCSearchRoomItem*>(item)->getParent();
+
+	MUCSearchServiceItem* parent = NULL;
+	if (MUCSearchRoomItem* roomItem = dynamic_cast<MUCSearchRoomItem*>(item)) {
+		parent = roomItem->getParent();
+	}
+	else if (MUCSearchEmptyItem* emptyItem = dynamic_cast<MUCSearchEmptyItem*>(item)) {
+		parent = emptyItem->getParent();
+	}
 	if (parent) {
 		int row = services_.indexOf(parent);
 		return createIndex(row, 1, parent);
@@ -74,7 +81,8 @@ int MUCSearchModel::rowCount(const QModelIndex& parentIndex) const {
 	} 
 	if (dynamic_cast<MUCSearchServiceItem*>(static_cast<MUCSearchItem*>(parentIndex.internalPointer()))) {
 		return services_[parentIndex.row()]->rowCount();
-	} else {
+	}
+	else {
 		return 0;
 	}
 }
diff --git a/Swift/QtUI/MUCSearch/MUCSearchServiceItem.h b/Swift/QtUI/MUCSearch/MUCSearchServiceItem.h
index 860792f..411727d 100644
--- a/Swift/QtUI/MUCSearch/MUCSearchServiceItem.h
+++ b/Swift/QtUI/MUCSearch/MUCSearchServiceItem.h
@@ -15,9 +15,9 @@ namespace Swift {
 	class MUCSearchServiceItem : public MUCSearchItem {
 		public:
 			MUCSearchServiceItem(const QString& jidString) : jidString_(jidString) {}
-			void addRoom(MUCSearchRoomItem* room) {rooms_.push_back(room);}
+			void addRoom(MUCSearchItem* room) {rooms_.push_back(room);}
 			int rowCount() {return rooms_.count();}
-			MUCSearchRoomItem* getItem(int i) {return rooms_[i];}
+			MUCSearchItem* getItem(int i) {return rooms_[i];}
 			QVariant data(int role) {
 				switch (role) {
 					case Qt::DisplayRole: return QVariant(jidString_); 
@@ -26,7 +26,7 @@ namespace Swift {
 			}
 			QString getHost() {return jidString_;}
 		private:
-			QList<MUCSearchRoomItem*> rooms_;
+			QList<MUCSearchItem*> rooms_;
 			QString jidString_;
 	};
 }
diff --git a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp
index 7d2caba..3bdc433 100644
--- a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp
+++ b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp
@@ -10,43 +10,42 @@
 #include <QMovie>
 #include <QScrollBar>
 #include <QTimer>
+#include <QPushButton>
 
-#include "Swift/Controllers/UIEvents/UIEventStream.h"
 #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
 #include "Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h"
 #include "Swift/QtUI/MUCSearch/MUCSearchModel.h"
 #include "Swift/QtUI/MUCSearch/MUCSearchDelegate.h"
+#include "Swift/QtUI/MUCSearch/MUCSearchEmptyItem.h"
 #include "Swift/QtUI/QtSwiftUtil.h"
 
 namespace Swift {
 
-QtMUCSearchWindow::QtMUCSearchWindow(UIEventStream* eventStream) {
+QtMUCSearchWindow::QtMUCSearchWindow() {
+	ui_.setupUi(this);
 #ifndef Q_WS_MAC
 	setWindowIcon(QIcon(":/logo-icon-16.png"));
 #endif
-	eventStream_ = eventStream;
-	setupUi(this);
-	showEmptyRooms_->hide();
-	filterLabel_->hide();
-	filter_->hide();
+	setModal(true);
+	ui_.filter_->hide();
 	model_ = new MUCSearchModel();
 	delegate_ = new MUCSearchDelegate();
-	results_->setModel(model_);
-	results_->setItemDelegate(delegate_);
-	results_->setHeaderHidden(true);
-#ifdef SWIFT_PLATFORM_MACOSX
-	results_->setAlternatingRowColors(true);
-#endif
-	connect(service_, SIGNAL(activated(const QString&)), this, SLOT(handleSearch(const QString&)));
-	connect(room_, SIGNAL(returnPressed()), this, SLOT(handleJoin()));
-	connect(nickName_, SIGNAL(returnPressed()), room_, SLOT(setFocus()));
-	connect(searchButton_, SIGNAL(clicked()), this, SLOT(handleSearch()));
-	connect(joinButton_, SIGNAL(clicked()), this, SLOT(handleJoin()));
-	connect(results_, SIGNAL(clicked(const QModelIndex&)), this, SLOT(handleSelected(const QModelIndex&)));
-	connect(results_, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleActivated(const QModelIndex&)));
-	throbber_ = new QLabel("Searching", results_);
+	ui_.results_->setModel(model_);
+	ui_.results_->setItemDelegate(delegate_);
+	ui_.results_->setHeaderHidden(true);
+	ui_.results_->setRootIsDecorated(true);
+	ui_.results_->setAnimated(true);
+	ui_.results_->setAlternatingRowColors(true);
+	connect(ui_.service_, SIGNAL(activated(const QString&)), this, SLOT(handleSearch(const QString&)));
+	connect(ui_.results_, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleActivated(const QModelIndex&)));
+	// Not using a button box, because i can't seem to be able to make the ok button non-default (on mac)
+	connect(ui_.okButton, SIGNAL(clicked()), this, SLOT(accept()));
+	connect(ui_.cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
+
+	throbber_ = new QLabel("Searching", ui_.results_);
 	throbber_->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), throbber_));
 	throbber_->setToolTip("Searching");
+
 	hasHadScrollBars_ = false;
 	updateThrobberPosition();
 	setSearchInProgress(false);
@@ -62,8 +61,8 @@ void QtMUCSearchWindow::resizeEvent(QResizeEvent* /*event*/) {
 
 void QtMUCSearchWindow::updateThrobberPosition() {
 	bool isShown = throbber_->isVisible();
-	int resultWidth = results_->width();
-	int resultHeight = results_->height();
+	int resultWidth = ui_.results_->width();
+	int resultHeight = ui_.results_->height();
 	//throbberWidth = throbber_->movie()->scaledSize().width();
 	//throbberHeight = throbber_->movie()->scaledSize().height();
 	int throbberWidth = 16; /* This is nasty, but the above doesn't work! */
@@ -72,99 +71,43 @@ void QtMUCSearchWindow::updateThrobberPosition() {
 	 * because if you listen for the expanded/collapsed signals, you seem to get them before the scrollbars are updated.
 	 * This seems an acceptable workaround.
 	 */
-	hasHadScrollBars_ |= results_->verticalScrollBar()->isVisible();
-	int hMargin = hasHadScrollBars_ ? results_->verticalScrollBar()->width() + 2 : 2;
+	hasHadScrollBars_ |= ui_.results_->verticalScrollBar()->isVisible();
+	int hMargin = hasHadScrollBars_ ? ui_.results_->verticalScrollBar()->width() + 2 : 2;
 	int vMargin = 2; /* We don't get horizontal scrollbars */
 	throbber_->setGeometry(QRect(resultWidth - throbberWidth - hMargin, resultHeight - throbberHeight - vMargin, throbberWidth, throbberHeight)); /* include margins */
 	throbber_->setVisible(isShown);
 }
 
-void QtMUCSearchWindow::addSavedServices(const std::vector<JID>& services) {
-	service_->clear();
-	foreach (JID jid, services) {
-		service_->addItem(P2QSTRING(jid.toString()));
+void QtMUCSearchWindow::addSavedServices(const std::list<JID>& services) {
+	ui_.service_->clear();
+	foreach (const JID& jid, services) {
+		ui_.service_->addItem(P2QSTRING(jid.toString()));
 	}
-	service_->clearEditText();
-}
-
-void QtMUCSearchWindow::handleActivated(const QModelIndex& index) {
-	if (!index.isValid()) {
-		return;
+	if (!services.empty()) {
+		ui_.service_->setEditText(P2QSTRING(services.begin()->toString()));
 	}
-	MUCSearchRoomItem* roomItem = dynamic_cast<MUCSearchRoomItem*>(static_cast<MUCSearchItem*>(index.internalPointer()));
-	if (roomItem) {
-		handleSelected(index);
-		handleJoin();
+	else {
+		ui_.service_->clearEditText();
 	}
 }
 
-void QtMUCSearchWindow::handleSelected(const QModelIndex& current) {
-	if (!current.isValid()) {
+void QtMUCSearchWindow::handleActivated(const QModelIndex& index) {
+	if (!index.isValid()) {
 		return;
-	}	
-	MUCSearchRoomItem* roomItem = dynamic_cast<MUCSearchRoomItem*>(static_cast<MUCSearchItem*>(current.internalPointer()));
-	if (roomItem) {
-		room_->setText(roomItem->getNode() + "@" + roomItem->getParent()->getHost());
 	}
-
-}
-
-void QtMUCSearchWindow::handleSearch(const QString& text) {
-	if (text.isEmpty()) {
-		return;
+	if (dynamic_cast<MUCSearchRoomItem*>(static_cast<MUCSearchItem*>(index.internalPointer()))) {
+		accept();
 	}
-	onAddService(JID(Q2PSTRING(text)));
 }
 
 void QtMUCSearchWindow::handleSearch() {
-	handleSearch(service_->currentText());
-}
-
-
-void QtMUCSearchWindow::handleJoin() {
-	if (room_->text().isEmpty()) {
-		handleSelected(results_->currentIndex());
-	}
-	if (room_->text().isEmpty()) {
-		return;
-	}
-	boost::optional<String> maybeNick;
-	if (!nickName_->text().isEmpty()) {
-		lastSetNick_ = Q2PSTRING(nickName_->text());
-		maybeNick = lastSetNick_;
-	}
-
-	JID room(Q2PSTRING(room_->text()));
-	if (joinAutomatically_->isChecked()) {
-		createAutoJoin(room, maybeNick);
-	}
-	eventStream_->send(boost::shared_ptr<UIEvent>(new JoinMUCUIEvent(room, maybeNick)));
-	hide();
+	handleSearch(ui_.service_->currentText());
 }
 
-void QtMUCSearchWindow::createAutoJoin(const JID& room, boost::optional<String> passedNick) {
-	String nick = lastSetNick_;
-	if (passedNick) {
-		nick = passedNick.get();
+void QtMUCSearchWindow::handleSearch(const QString& service) {
+	if (!service.isEmpty()) {
+		onSearchService(JID(Q2PSTRING(service)));
 	}
-	MUCBookmark bookmark(room, room.getNode());
-	bookmark.setAutojoin(true);
-	if (!nick.isEmpty()) {
-		bookmark.setNick(nick);
-	}
-	//if (!password.isEmpty()) {
-	//	bookmark.setPassword(password);
-	//}
-	eventStream_->send(boost::shared_ptr<UIEvent>(new AddMUCBookmarkUIEvent(bookmark)));
-} 
-
-void QtMUCSearchWindow::setNick(const String& nick) {
-	nickName_->setText(P2QSTRING(nick));
-	lastSetNick_ = nick;
-}
-
-void QtMUCSearchWindow::setMUC(const String& nick) {
-	room_->setText(P2QSTRING(nick));
 }
 
 void QtMUCSearchWindow::show() {
@@ -179,11 +122,16 @@ void QtMUCSearchWindow::clearList() {
 void QtMUCSearchWindow::addService(const MUCService& service) {
 	updateThrobberPosition();
 	MUCSearchServiceItem* serviceItem = new MUCSearchServiceItem(P2QSTRING(service.getJID().toString()));
-	foreach (MUCService::MUCRoom room, service.getRooms()) {
-		new MUCSearchRoomItem(P2QSTRING(room.getNode()), serviceItem);
+	if (service.getRooms().size() > 0) {
+		foreach (MUCService::MUCRoom room, service.getRooms()) {
+			new MUCSearchRoomItem(P2QSTRING(room.getNode()), serviceItem);
+		}
+	}
+	else {
+		new MUCSearchEmptyItem(serviceItem);
 	}
 	model_->addService(serviceItem);
-	results_->expandAll();
+	ui_.results_->expandAll();
 }
 
 void QtMUCSearchWindow::setSearchInProgress(bool searching) {
@@ -195,4 +143,24 @@ void QtMUCSearchWindow::setSearchInProgress(bool searching) {
 	throbber_->setVisible(searching);
 }
 
+void QtMUCSearchWindow::accept() {
+	QModelIndexList selection = ui_.results_->selectionModel()->selectedIndexes();
+	if (selection.isEmpty()) {
+		onFinished(boost::optional<JID>());
+	}
+	else {
+		QModelIndex selectedItem = selection[0];
+		MUCSearchRoomItem* item = dynamic_cast<MUCSearchRoomItem*>(static_cast<MUCSearchItem*>(selectedItem.internalPointer()));
+		if (item) {
+			onFinished(JID(Q2PSTRING(item->getNode()), Q2PSTRING(item->getParent()->getHost())));
+		}
+	}
+	QDialog::accept();
+}
+
+void QtMUCSearchWindow::reject() {
+	onFinished(boost::optional<JID>());
+	QDialog::reject();
+}
+
 }
diff --git a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.h b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.h
index b8cf953..cb4585d 100644
--- a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.h
+++ b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.h
@@ -13,37 +13,36 @@
 namespace Swift {
 	class MUCSearchModel;
 	class MUCSearchDelegate;
-	class UIEventStream;
-	class QtMUCSearchWindow : public QWidget, public MUCSearchWindow, private Ui::QtMUCSearchWindow {
+
+	class QtMUCSearchWindow : public QDialog, public MUCSearchWindow {
 		Q_OBJECT
 		public:
-			QtMUCSearchWindow(UIEventStream* eventStream);
+			QtMUCSearchWindow();
 			virtual ~QtMUCSearchWindow();
 
-			virtual void setNick(const String& nick);
-			virtual void setMUC(const String& nick);
 			virtual void clearList();
 			virtual void addService(const MUCService& service);
-			virtual void addSavedServices(const std::vector<JID>& services);
+			virtual void addSavedServices(const std::list<JID>& services);
 			virtual void setSearchInProgress(bool searching);
 
 			virtual void show();
+			virtual void accept();
+			virtual void reject();
+
 		protected:
 			virtual void resizeEvent(QResizeEvent* event);
+
 		private slots:
-			void handleSearch(const QString& text);
 			void handleSearch();
-			void handleJoin();
-			void handleSelected(const QModelIndex& current);
+			void handleSearch(const QString&);
 			void handleActivated(const QModelIndex& index);
 			void updateThrobberPosition();
+
 		private:
-			void createAutoJoin(const JID& room, boost::optional<String> passedNick);
+			Ui::QtMUCSearchWindow ui_;
 			MUCSearchModel* model_;
 			MUCSearchDelegate* delegate_;
-			UIEventStream* eventStream_;
 			QLabel* throbber_;
-			String lastSetNick_;
 			bool hasHadScrollBars_;
 	};
 }
diff --git a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.ui b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.ui
index ef2524b..f1a1fd5 100644
--- a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.ui
+++ b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.ui
@@ -1,212 +1,92 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <ui version="4.0">
  <class>QtMUCSearchWindow</class>
- <widget class="QWidget" name="QtMUCSearchWindow">
+ <widget class="QDialog" name="QtMUCSearchWindow">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>523</width>
-    <height>531</height>
+    <height>368</height>
    </rect>
   </property>
-  <property name="sizePolicy">
-   <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-    <horstretch>0</horstretch>
-    <verstretch>0</verstretch>
-   </sizepolicy>
-  </property>
   <property name="windowTitle">
-   <string>Find Room</string>
+   <string>Dialog</string>
   </property>
-  <layout class="QVBoxLayout" name="verticalLayout_4">
-   <item>
-    <layout class="QVBoxLayout" name="verticalLayout_3">
-     <property name="sizeConstraint">
-      <enum>QLayout::SetDefaultConstraint</enum>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Service:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QComboBox" name="service_">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+       <horstretch>2</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="editable">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="2">
+    <widget class="QLineEdit" name="filter_">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+       <horstretch>1</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
      </property>
+     <property name="text">
+      <string/>
+     </property>
+     <property name="frame">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" colspan="3">
+    <widget class="QTreeView" name="results_"/>
+   </item>
+   <item row="2" column="0" colspan="3">
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
      <item>
-      <layout class="QHBoxLayout" name="horizontalLayout_2">
-       <item>
-        <layout class="QVBoxLayout" name="verticalLayout">
-         <item>
-          <widget class="QLabel" name="label">
-           <property name="text">
-            <string>Available Rooms</string>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QTreeView" name="results_"/>
-         </item>
-        </layout>
-       </item>
-       <item>
-        <layout class="QVBoxLayout" name="verticalLayout_2">
-         <item>
-          <widget class="QLabel" name="label_5">
-           <property name="text">
-            <string>Your nickname:</string>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLineEdit" name="nickName_"/>
-         </item>
-         <item>
-          <spacer name="verticalSpacer">
-           <property name="orientation">
-            <enum>Qt::Vertical</enum>
-           </property>
-           <property name="sizeHint" stdset="0">
-            <size>
-             <width>20</width>
-             <height>40</height>
-            </size>
-           </property>
-          </spacer>
-         </item>
-         <item>
-          <widget class="QLabel" name="label_2">
-           <property name="text">
-            <string>Search another service:</string>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QComboBox" name="service_">
-           <property name="editable">
-            <bool>true</bool>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <layout class="QHBoxLayout" name="horizontalLayout">
-           <item>
-            <spacer name="horizontalSpacer">
-             <property name="orientation">
-              <enum>Qt::Horizontal</enum>
-             </property>
-             <property name="sizeHint" stdset="0">
-              <size>
-               <width>40</width>
-               <height>20</height>
-              </size>
-             </property>
-            </spacer>
-           </item>
-           <item>
-            <widget class="QPushButton" name="searchButton_">
-             <property name="text">
-              <string>Search</string>
-             </property>
-            </widget>
-           </item>
-          </layout>
-         </item>
-         <item>
-          <spacer name="verticalSpacer_2">
-           <property name="orientation">
-            <enum>Qt::Vertical</enum>
-           </property>
-           <property name="sizeHint" stdset="0">
-            <size>
-             <width>20</width>
-             <height>40</height>
-            </size>
-           </property>
-          </spacer>
-         </item>
-         <item>
-          <widget class="QLabel" name="filterLabel_">
-           <property name="text">
-            <string>Only show rooms matching:</string>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLineEdit" name="filter_"/>
-         </item>
-         <item>
-          <widget class="QCheckBox" name="showEmptyRooms_">
-           <property name="enabled">
-            <bool>true</bool>
-           </property>
-           <property name="text">
-            <string>Show Empty Rooms</string>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <spacer name="verticalSpacer_3">
-           <property name="orientation">
-            <enum>Qt::Vertical</enum>
-           </property>
-           <property name="sizeHint" stdset="0">
-            <size>
-             <width>20</width>
-             <height>18</height>
-            </size>
-           </property>
-          </spacer>
-         </item>
-         <item>
-          <widget class="QLabel" name="label_4">
-           <property name="text">
-            <string>Join Room Directly:</string>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLineEdit" name="room_"/>
-         </item>
-         <item>
-          <widget class="QCheckBox" name="joinAutomatically_">
-           <property name="text">
-            <string>Join automatically in future</string>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
-      </layout>
+      <widget class="QPushButton" name="cancelButton">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
      </item>
      <item>
-      <layout class="QHBoxLayout" name="horizontalLayout_3">
-       <item>
-        <spacer name="horizontalSpacer_2">
-         <property name="orientation">
-          <enum>Qt::Horizontal</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>40</width>
-           <height>20</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-       <item>
-        <widget class="QPushButton" name="joinButton_">
-         <property name="text">
-          <string>Join Room</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <spacer name="horizontalSpacer_3">
-         <property name="orientation">
-          <enum>Qt::Horizontal</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>40</width>
-           <height>20</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-      </layout>
+      <widget class="QPushButton" name="okButton">
+       <property name="text">
+        <string>Ok</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
      </item>
     </layout>
    </item>
diff --git a/Swift/QtUI/QtJoinMUCWindow.cpp b/Swift/QtUI/QtJoinMUCWindow.cpp
new file mode 100644
index 0000000..55b285b
--- /dev/null
+++ b/Swift/QtUI/QtJoinMUCWindow.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "QtJoinMUCWindow.h"
+#include "QtSwiftUtil.h"
+
+namespace Swift {
+
+QtJoinMUCWindow::QtJoinMUCWindow() {
+	ui.setupUi(this);
+	connect(ui.room, SIGNAL(returnPressed()), this, SLOT(handleJoin()));
+	connect(ui.searchButton, SIGNAL(clicked()), this, SLOT(handleSearch()));
+	connect(ui.joinButton, SIGNAL(clicked()), this, SLOT(handleJoin()));
+}
+
+void QtJoinMUCWindow::handleJoin() {
+	if (ui.room->text().isEmpty()) {
+		// TODO: Error
+		return;
+	}
+	if (ui.nickName->text().isEmpty()) {
+		// TODO: Error
+		return;
+	}
+
+	lastSetNick = Q2PSTRING(ui.nickName->text());
+	JID room(Q2PSTRING(ui.room->text()));
+	onJoinMUC(room, lastSetNick, ui.joinAutomatically->isChecked()); 
+	hide();
+}
+
+void QtJoinMUCWindow::handleSearch() {
+	onSearchMUC();
+}
+
+void QtJoinMUCWindow::setNick(const String& nick) {
+	ui.nickName->setText(P2QSTRING(nick));
+	lastSetNick = nick;
+}
+
+void QtJoinMUCWindow::setMUC(const String& nick) {
+	ui.room->setText(P2QSTRING(nick));
+}
+
+void QtJoinMUCWindow::show() {
+	QWidget::show();
+	QWidget::activateWindow();
+}
+
+}
diff --git a/Swift/QtUI/QtJoinMUCWindow.h b/Swift/QtUI/QtJoinMUCWindow.h
new file mode 100644
index 0000000..2d12319
--- /dev/null
+++ b/Swift/QtUI/QtJoinMUCWindow.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/String.h>
+#include <Swift/Controllers/UIInterfaces/JoinMUCWindow.h>
+#include <Swift/QtUI/ui_QtJoinMUCWindow.h>
+
+namespace Swift {
+	class QtJoinMUCWindow : public QWidget, public JoinMUCWindow {
+			Q_OBJECT
+		public:
+			QtJoinMUCWindow();
+
+			virtual void setNick(const String& nick);
+			virtual void setMUC(const String& nick);
+
+			virtual void show();
+
+		private slots:
+			void handleJoin();
+			void handleSearch();
+
+		private:
+			Ui::QtJoinMUCWindow ui;
+			String lastSetNick;
+	};
+}
diff --git a/Swift/QtUI/QtJoinMUCWindow.ui b/Swift/QtUI/QtJoinMUCWindow.ui
new file mode 100644
index 0000000..2f3608a
--- /dev/null
+++ b/Swift/QtUI/QtJoinMUCWindow.ui
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtJoinMUCWindow</class>
+ <widget class="QWidget" name="QtJoinMUCWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>398</width>
+    <height>172</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Join Room</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string>Room:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="QToolButton" name="searchButton">
+       <property name="text">
+        <string>Search ...</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_5">
+       <property name="text">
+        <string>Nickname:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1" colspan="2">
+      <widget class="QLineEdit" name="nickName"/>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLineEdit" name="room">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="placeholderText">
+        <string>swift@rooms.swift.im</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>36</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QCheckBox" name="joinAutomatically">
+       <property name="text">
+        <string>Join automatically in future</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="joinButton">
+       <property name="text">
+        <string>Join Room</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>room</tabstop>
+  <tabstop>nickName</tabstop>
+  <tabstop>joinAutomatically</tabstop>
+  <tabstop>joinButton</tabstop>
+  <tabstop>searchButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
index 21d1474..0411c0b 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -8,6 +8,7 @@
 
 #include <boost/optional.hpp>
 #include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
 
 #include <QBoxLayout>
 #include <QComboBox>
@@ -24,9 +25,9 @@
 #include "QtSwiftUtil.h"
 #include "QtTabWidget.h"
 #include "Roster/QtTreeWidget.h"
+#include "Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h"
 #include "Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h"
 #include "Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h"
-#include "Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h"
 #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
 #include "Swift/Controllers/UIEvents/ToggleShowOfflineUIEvent.h"
 
@@ -143,7 +144,7 @@ void QtMainWindow::handleSignOutAction() {
 }
 
 void QtMainWindow::handleJoinMUCAction() {
-	uiEventStream_->send(boost::shared_ptr<UIEvent>(new RequestMUCSearchUIEvent()));
+	uiEventStream_->send(boost::make_shared<RequestJoinMUCUIEvent>());
 }
 
 void QtMainWindow::handleStatusChanged(StatusShow::Type showType, const QString &statusMessage) {
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index 25a508e..8cb9863 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -16,6 +16,7 @@
 #include "QtSettingsProvider.h"
 #include "QtMainWindow.h"
 #include "QtChatWindow.h"
+#include "QtJoinMUCWindow.h"
 #include "QtChatWindowFactory.h"
 #include "QtSwiftUtil.h"
 #include "MUCSearch/QtMUCSearchWindow.h"
@@ -70,8 +71,8 @@ ChatListWindow* QtUIFactory::createChatListWindow(UIEventStream*) {
 	return lastMainWindow->getChatListWindow();
 }
 
-MUCSearchWindow* QtUIFactory::createMUCSearchWindow(UIEventStream* eventStream) {
-		return new QtMUCSearchWindow(eventStream);
+MUCSearchWindow* QtUIFactory::createMUCSearchWindow() {
+		return new QtMUCSearchWindow();
 }
 
 ChatWindow* QtUIFactory::createChatWindow(const JID& contact, UIEventStream* eventStream) {
@@ -82,4 +83,8 @@ UserSearchWindow* QtUIFactory::createUserSearchWindow(UserSearchWindow::Type typ
 	return new QtUserSearchWindow(eventStream, type);
 };
 
+JoinMUCWindow* QtUIFactory::createJoinMUCWindow() {
+	return new QtJoinMUCWindow();
+}
+
 }
diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h
index 6ddc316..4d80338 100644
--- a/Swift/QtUI/QtUIFactory.h
+++ b/Swift/QtUI/QtUIFactory.h
@@ -31,9 +31,10 @@ namespace Swift {
 			virtual LoginWindow* createLoginWindow(UIEventStream* eventStream);
 			virtual EventWindow* createEventWindow();
 			virtual ChatListWindow* createChatListWindow(UIEventStream*);
-			virtual MUCSearchWindow* createMUCSearchWindow(UIEventStream* eventStream);
+			virtual MUCSearchWindow* createMUCSearchWindow();
 			virtual ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream);
 			virtual UserSearchWindow* createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream);
+			virtual JoinMUCWindow* createJoinMUCWindow();
 
 		private slots:
 			void handleLoginWindowGeometryChanged();
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index c0730dc..b0072a6 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -92,6 +92,7 @@ sources = [
     "SystemMessageSnippet.cpp",
     "QtElidingLabel.cpp",
     "QtLineEdit.cpp",
+    "QtJoinMUCWindow.cpp",
     "Roster/RosterModel.cpp",
     "Roster/QtTreeWidget.cpp",
 #    "Roster/QtTreeWidgetItem.cpp",
@@ -110,6 +111,7 @@ sources = [
     "MUCSearch/QtMUCSearchWindow.cpp",
     "MUCSearch/MUCSearchModel.cpp",
     "MUCSearch/MUCSearchRoomItem.cpp",
+    "MUCSearch/MUCSearchEmptyItem.cpp",
     "MUCSearch/MUCSearchDelegate.cpp",
     "UserSearch/QtUserSearchWindow.cpp",
     "UserSearch/UserSearchModel.cpp",
@@ -147,6 +149,7 @@ myenv.Uic4("UserSearch/QtUserSearchFieldsPage.ui")
 myenv.Uic4("UserSearch/QtUserSearchResultsPage.ui")
 myenv.Uic4("QtAddContactDialog.ui")
 myenv.Uic4("QtBookmarkDetailWindow.ui")
+myenv.Uic4("QtJoinMUCWindow.ui")
 myenv.Qrc("DefaultTheme.qrc")
 myenv.Qrc("Swift.qrc")
 
-- 
cgit v0.10.2-6-g49f6