From a59af6c6daa72dd491189335cf2d255a788eb0f6 Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Sun, 28 Mar 2010 14:25:13 +0100
Subject: Chat list in the gui (mostly non-functional).


diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 104df22..d83c42e 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -8,6 +8,7 @@
 #include "Swift/Controllers/EventController.h"
 #include "Swift/Controllers/Chat/MUCController.h"
 #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
+#include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h"
 #include "Swiften/Presence/PresenceSender.h"
 #include "Swiften/Elements/ChatState.h"
 #include "Swiften/MUC/MUCBookmarkManager.h"
@@ -17,7 +18,7 @@ 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, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream) : jid_(jid) {
+ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory) : jid_(jid) {
 	eventController_ = eventController;
 	stanzaChannel_ = stanzaChannel;
 	iqRouter_ = iqRouter;
@@ -30,9 +31,11 @@ ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRo
 	presenceSender_ = presenceSender;
 	uiEventStream_ = uiEventStream;
 	mucBookmarkManager_ = new MUCBookmarkManager(iqRouter);
-	mucBookmarkManager_->onBookmarksChanged.connect(boost::bind(&ChatsManager::handleMUCBookmarksChanged, this));
+	mucBookmarkManager_->onBookmarkAdded.connect(boost::bind(&ChatsManager::handleMUCBookmarkAdded, this, _1));
+	mucBookmarkManager_->onBookmarkRemoved.connect(boost::bind(&ChatsManager::handleMUCBookmarkRemoved, this, _1));
 	presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1, _2));
 	uiEventStream_->onUIEvent.connect(boost::bind(&ChatsManager::handleUIEvent, this, _1));
+	chatListWindow_ = chatListWindowFactory->createWindow(uiEventStream_);
 }
 
 ChatsManager::~ChatsManager() {
@@ -45,15 +48,18 @@ ChatsManager::~ChatsManager() {
 	delete mucBookmarkManager_;
 }
 
-void ChatsManager::handleMUCBookmarksChanged() {
-	foreach (boost::shared_ptr<MUCBookmark> bookmark, mucBookmarkManager_->getBookmarks()) {
-		std::map<JID, MUCController*>::iterator it = mucControllers_.find(bookmark->getRoom());
-		if (it == mucControllers_.end()) {
-			//FIXME: need vcard stuff here to get a nick
-			String nick = bookmark->getNick() ? bookmark->getNick().get() : "Swift user";
-			handleJoinMUCRequest(bookmark->getRoom(), nick);
-		}
+void ChatsManager::handleMUCBookmarkAdded(boost::shared_ptr<MUCBookmark> bookmark) {
+	std::map<JID, MUCController*>::iterator it = mucControllers_.find(bookmark->getRoom());
+	if (it == mucControllers_.end()) {
+		//FIXME: need vcard stuff here to get a nick
+		String nick = bookmark->getNick() ? bookmark->getNick().get() : "Swift user";
+		handleJoinMUCRequest(bookmark->getRoom(), nick);
 	}
+	chatListWindow_->addMUCBookmark(bookmark);
+}
+
+void ChatsManager::handleMUCBookmarkRemoved(boost::shared_ptr<MUCBookmark> bookmark) {
+	chatListWindow_->removeMUCBookmark(bookmark);
 }
 
 void ChatsManager::handleUserLeftMUC(MUCController* mucController) {
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index a1614f8..575be5f 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -11,6 +11,7 @@
 #include "Swiften/JID/JID.h"
 #include "Swiften/MUC/MUCRegistry.h"
 #include "Swift/Controllers/UIEvents/UIEventStream.h"
+#include "Swiften/MUC/MUCBookmark.h"
 
 namespace Swift {
 	class EventController;
@@ -25,10 +26,12 @@ namespace Swift {
 	class IQRouter;
 	class PresenceSender;
 	class MUCBookmarkManager;
+	class ChatListWindow;
+	class ChatListWindowFactory;
 
 	class ChatsManager : public MUCRegistry {
 		public:
-			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream);
+			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory);
 			~ChatsManager();
 			void setAvatarManager(AvatarManager* avatarManager);
 			void setEnabled(bool enabled);
@@ -40,7 +43,8 @@ namespace Swift {
 			void rebindControllerJID(const JID& from, const JID& to);
 			void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> lastPresence);
 			void handleUIEvent(boost::shared_ptr<UIEvent> event);
-			void handleMUCBookmarksChanged();
+			void handleMUCBookmarkAdded(boost::shared_ptr<MUCBookmark> bookmark);
+			void handleMUCBookmarkRemoved(boost::shared_ptr<MUCBookmark> bookmark);
 			void handleUserLeftMUC(MUCController* mucController);
 			ChatController* getChatController(const JID &contact);
 			virtual bool isMUC(const JID& muc) const;
@@ -60,5 +64,6 @@ namespace Swift {
 			UIEventStream* uiEventStream_;
 			MUCBookmarkManager* mucBookmarkManager_;
 			boost::shared_ptr<DiscoInfo> serverDiscoInfo_;
+			ChatListWindow* chatListWindow_;
 	};
 }
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 310d28f..ee11b70 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -55,7 +55,7 @@ static const String CLIENT_VERSION = "0.3";
 static const String CLIENT_NODE = "http://swift.im";
 
 
-MainController::MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, TreeWidgetFactory *treeWidgetFactory, EventWindowFactory* eventWindowFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray, SoundPlayer* soundPlayer, XMLConsoleWidgetFactory* xmlConsoleWidgetFactory)
+MainController::MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, TreeWidgetFactory *treeWidgetFactory, EventWindowFactory* eventWindowFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray, SoundPlayer* soundPlayer, XMLConsoleWidgetFactory* xmlConsoleWidgetFactory, ChatListWindowFactory* chatListWindowFactory)
 		: timerFactory_(&boostIOServiceThread_.getIOService()), idleDetector_(&idleQuerier_, &timerFactory_, 100), client_(NULL), presenceSender_(NULL), chatWindowFactory_(chatWindowFactory), mainWindowFactory_(mainWindowFactory), loginWindowFactory_(loginWindowFactory), treeWidgetFactory_(treeWidgetFactory), settings_(settings), xmppRosterController_(NULL), rosterController_(NULL), loginWindow_(NULL), clientVersionResponder_(NULL), nickResolver_(NULL), discoResponder_(NULL) {
 	application_ = application;
 	presenceOracle_ = NULL;
@@ -63,6 +63,7 @@ MainController::MainController(ChatWindowFactory* chatWindowFactory, MainWindowF
 	chatsManager_ = NULL;
 	eventController_ = NULL;
 	eventWindowFactory_ = eventWindowFactory;
+	chatListWindowFactory_ = chatListWindowFactory;
 	uiEventStream_ = new UIEventStream();
 
 	avatarStorage_ = new AvatarFileStorage(application_->getAvatarDir());
@@ -135,20 +136,22 @@ void MainController::handleConnected() {
 		nickResolver_ = new NickResolver(xmppRoster_);		
 		lastSentPresence_ = boost::shared_ptr<Presence>();
 		
-		chatsManager_ = new ChatsManager(jid_, client_, client_, eventController_, chatWindowFactory_, treeWidgetFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_);
-		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));
-
-		avatarManager_ = new AvatarManager(client_, client_, avatarStorage_, chatsManager_);
-
-		chatsManager_->setAvatarManager(avatarManager_);
+		
 
+		avatarManager_ = new AvatarManager(client_, client_, avatarStorage_);
 
 		rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, treeWidgetFactory_, nickResolver_, presenceOracle_, eventController_, uiEventStream_);
 		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
 		rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
-				rosterController_->onStartChatRequest.connect(boost::bind(&ChatsManager::handleChatRequest, chatsManager_, _1));
+
+		chatsManager_ = new ChatsManager(jid_, client_, client_, eventController_, chatWindowFactory_, treeWidgetFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_);
+		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));
+		chatsManager_->setAvatarManager(avatarManager_);
+		rosterController_->onStartChatRequest.connect(boost::bind(&ChatsManager::handleChatRequest, chatsManager_, _1));
 		rosterController_->onJoinMUCRequest.connect(boost::bind(&ChatsManager::handleJoinMUCRequest, chatsManager_, _1, _2));
 
+		avatarManager_->setMUCRegistry(chatsManager_);
+
 		xmppRosterController_ = new XMPPRosterController(client_, xmppRoster_);
 		xmppRosterController_->requestRoster();
 
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 387fcb6..75a8e5a 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -26,6 +26,7 @@ namespace Swift {
 	class ChatWindowFactory;
 	class ChatController;
 	class ChatsManager;
+	class ChatListWindowFactory;
 	class EventController;
 	class MainWindowFactory;
 	class MainWindow;
@@ -54,7 +55,7 @@ namespace Swift {
 
 	class MainController {
 		public:
-			MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, TreeWidgetFactory* treeWidgetFactory, EventWindowFactory* eventWindowFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray, SoundPlayer* soundPlayer, XMLConsoleWidgetFactory* xmlConsoleWidgetFactory);
+			MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, TreeWidgetFactory* treeWidgetFactory, EventWindowFactory* eventWindowFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray, SoundPlayer* soundPlayer, XMLConsoleWidgetFactory* xmlConsoleWidgetFactory, ChatListWindowFactory* chatListWindowFactory_);
 			~MainController();
 
 
@@ -117,5 +118,6 @@ namespace Swift {
 			boost::shared_ptr<Presence> queuedPresence_;
 			String password_;
 			String certificateFile_;
+			ChatListWindowFactory* chatListWindowFactory_;
 	};
 }
diff --git a/Swift/Controllers/UIInterfaces/ChatListWindow.h b/Swift/Controllers/UIInterfaces/ChatListWindow.h
new file mode 100644
index 0000000..2883d5c
--- /dev/null
+++ b/Swift/Controllers/UIInterfaces/ChatListWindow.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/MUC/MUCBookmark.h"
+
+namespace Swift {
+	class ChatListWindow {
+		public:
+			virtual void addMUCBookmark(boost::shared_ptr<MUCBookmark> bookmark) = 0;
+			virtual void removeMUCBookmark(boost::shared_ptr<MUCBookmark> bookmark) = 0;
+	};
+}
diff --git a/Swift/Controllers/UIInterfaces/ChatListWindowFactory.h b/Swift/Controllers/UIInterfaces/ChatListWindowFactory.h
new file mode 100644
index 0000000..05e5a55
--- /dev/null
+++ b/Swift/Controllers/UIInterfaces/ChatListWindowFactory.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "Swift/Controllers/UIInterfaces/ChatListWindow.h"
+
+namespace Swift {
+	class UIEventStream;
+	class ChatListWindowFactory {
+		public:
+			virtual ChatListWindow* createWindow(UIEventStream* uiEventStream) = 0;	
+	};
+}
diff --git a/Swift/QtUI/ChatList/ChatListDelegate.cpp b/Swift/QtUI/ChatList/ChatListDelegate.cpp
new file mode 100644
index 0000000..6bfc9b3
--- /dev/null
+++ b/Swift/QtUI/ChatList/ChatListDelegate.cpp
@@ -0,0 +1 @@
+#include "Swift/QtUI/ChatList/ChatListDelegate.h"
diff --git a/Swift/QtUI/ChatList/ChatListDelegate.h b/Swift/QtUI/ChatList/ChatListDelegate.h
new file mode 100644
index 0000000..e2e5b6f
--- /dev/null
+++ b/Swift/QtUI/ChatList/ChatListDelegate.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <QStyledItemDelegate>
+
+namespace Swift {
+
+	class ChatListDelegate : public QStyledItemDelegate {
+
+	};
+
+}
+
diff --git a/Swift/QtUI/ChatList/ChatListGroupItem.h b/Swift/QtUI/ChatList/ChatListGroupItem.h
new file mode 100644
index 0000000..82c9616
--- /dev/null
+++ b/Swift/QtUI/ChatList/ChatListGroupItem.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <QList>
+
+#include "Swift/QtUI/ChatList/ChatListItem.h"
+
+namespace Swift {
+	class ChatListGroupItem : public ChatListItem {
+		public:
+			ChatListGroupItem(const QString& name, ChatListGroupItem* parent) : ChatListItem(parent), name_(name) {};
+			void addItem(ChatListItem* item) {items_.push_back(item);};
+			void remove(int index) {items_.removeAt(index);};
+			int rowCount() {return items_.size();};
+			ChatListItem* item(int i) {return items_[i];};
+			int row(ChatListItem* item) {return items_.indexOf(item);};
+			QVariant data(int role) {return "Bob";};
+		private:
+			QString name_;
+			QList<ChatListItem*> items_;
+	};
+}
diff --git a/Swift/QtUI/ChatList/ChatListItem.h b/Swift/QtUI/ChatList/ChatListItem.h
new file mode 100644
index 0000000..f9e9fe7
--- /dev/null
+++ b/Swift/QtUI/ChatList/ChatListItem.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <QVariant>
+
+namespace Swift {
+	class ChatListGroupItem;
+	class ChatListItem {
+		public:
+			ChatListItem(ChatListGroupItem* parent) {parent_ = parent;};
+			ChatListGroupItem* parent() {return parent_;};
+			virtual QVariant data(int role) = 0;
+		private:
+			ChatListGroupItem* parent_;
+	};
+}
diff --git a/Swift/QtUI/ChatList/ChatListMUCItem.h b/Swift/QtUI/ChatList/ChatListMUCItem.h
new file mode 100644
index 0000000..53574b5
--- /dev/null
+++ b/Swift/QtUI/ChatList/ChatListMUCItem.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <QList>
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/MUC/MUCBookmark.h"
+
+#include "Swift/QtUI/ChatList/ChatListItem.h"
+
+namespace Swift {
+	class ChatListMUCItem : public ChatListItem {
+		public:
+			ChatListMUCItem(boost::shared_ptr<MUCBookmark> bookmark, ChatListGroupItem* parent) : ChatListItem(parent), bookmark_(bookmark) {};
+			boost::shared_ptr<MUCBookmark> getBookmark() {return bookmark_;};
+			QVariant data(int role) {};
+		private:
+			boost::shared_ptr<MUCBookmark> bookmark_;
+			QList<ChatListItem*> items_;
+	};
+}
diff --git a/Swift/QtUI/ChatList/ChatListModel.cpp b/Swift/QtUI/ChatList/ChatListModel.cpp
new file mode 100644
index 0000000..246d45d
--- /dev/null
+++ b/Swift/QtUI/ChatList/ChatListModel.cpp
@@ -0,0 +1,64 @@
+#include "Swift/QtUI/ChatList/ChatListModel.h"
+
+#include "Swift/QtUI/ChatList/ChatListMUCItem.h"
+
+namespace Swift {
+
+ChatListModel::ChatListModel() {
+	root_ = new ChatListGroupItem("", NULL);
+	mucBookmarks_ = new ChatListGroupItem("MUC Bookmarks", root_);
+}
+
+void ChatListModel::addMUCBookmark(boost::shared_ptr<Swift::MUCBookmark> bookmark) {
+	emit layoutAboutToBeChanged();
+	mucBookmarks_->addItem(new ChatListMUCItem(bookmark, mucBookmarks_));
+	emit layoutChanged();
+}
+
+void ChatListModel::removeMUCBookmark(boost::shared_ptr<Swift::MUCBookmark> bookmark) {
+	for (int i = 0; i < mucBookmarks_->rowCount(); i++) {
+		ChatListMUCItem* item = dynamic_cast<ChatListMUCItem*>(mucBookmarks_->item(i));
+		if (item->getBookmark() == bookmark) {
+			emit layoutAboutToBeChanged();
+			mucBookmarks_->remove(i);
+			emit layoutChanged();
+			break;
+		}
+	}
+}
+
+int ChatListModel::columnCount(const QModelIndex& /*parent*/) const {
+	return 1;
+}
+
+QVariant ChatListModel::data(const QModelIndex& index, int role) const {
+	return index.isValid() ? static_cast<ChatListItem*>(index.internalPointer())->data(role) : QVariant();
+}
+
+QModelIndex ChatListModel::index(int row, int column, const QModelIndex & parent) const {
+	if (!hasIndex(row, column, parent)) {
+		return QModelIndex();
+	}
+
+	ChatListGroupItem *parentItem = parent.isValid() ? static_cast<ChatListGroupItem*>(parent.internalPointer()) : root_;
+
+	return row < parentItem->rowCount() ? createIndex(row, column, parentItem->item(row)) : QModelIndex();
+}
+
+QModelIndex ChatListModel::parent(const QModelIndex& index) const {
+	if (!index.isValid()) {
+		return QModelIndex();
+	}
+	ChatListGroupItem* parent = static_cast<ChatListGroupItem*>(index.internalPointer())->parent();
+	return (parent == root_) ? QModelIndex() : createIndex(parent->parent()->row(parent), 0, parent);
+}
+
+int ChatListModel::rowCount(const QModelIndex& parentIndex) const {
+	ChatListGroupItem* parent = root_;
+	if (parentIndex.isValid()) {
+		parent = static_cast<ChatListGroupItem*>(parentIndex.internalPointer());
+	}
+	return parent ? parent->rowCount() : 0;
+}
+
+}
diff --git a/Swift/QtUI/ChatList/ChatListModel.h b/Swift/QtUI/ChatList/ChatListModel.h
new file mode 100644
index 0000000..da65547
--- /dev/null
+++ b/Swift/QtUI/ChatList/ChatListModel.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <QAbstractItemModel>
+#include <QList>
+
+#include "Swiften/MUC/MUCBookmark.h"
+
+#include "Swift/QtUI/ChatList/ChatListGroupItem.h"
+
+namespace Swift {
+	class ChatListModel : public QAbstractItemModel {
+		Q_OBJECT
+		public:
+			ChatListModel();
+			void addMUCBookmark(boost::shared_ptr<MUCBookmark> bookmark);
+			void removeMUCBookmark(boost::shared_ptr<MUCBookmark> bookmark);
+			int columnCount(const QModelIndex& parent = QModelIndex()) const;
+			QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
+			QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
+			QModelIndex parent(const QModelIndex& index) const;
+			int rowCount(const QModelIndex& parent = QModelIndex()) const;
+		private:
+			ChatListGroupItem* mucBookmarks_;
+			ChatListGroupItem* root_;
+	};
+
+}
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.cpp b/Swift/QtUI/ChatList/QtChatListWindow.cpp
new file mode 100644
index 0000000..b9d645d
--- /dev/null
+++ b/Swift/QtUI/ChatList/QtChatListWindow.cpp
@@ -0,0 +1,38 @@
+#include "Swift/QtUI/ChatList/QtChatListWindow.h"
+
+namespace Swift {
+
+QtChatListWindow::QtChatListWindow(UIEventStream *uiEventStream, QWidget* parent) : QTreeView(parent) {
+	eventStream_ = uiEventStream;
+	model_ = new ChatListModel();
+	setModel(model_);
+	delegate_ = new ChatListDelegate();
+	setItemDelegate(delegate_);
+	setHeaderHidden(true);
+	#ifdef SWIFT_PLATFORM_MACOSX
+	setAlternatingRowColors(true);
+#endif
+	setAnimated(true);
+	setIndentation(0);
+	setRootIsDecorated(true);
+	connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleItemActivated(const QModelIndex&)));
+}
+
+QtChatListWindow::~QtChatListWindow() {
+	delete model_;
+	delete delegate_;
+}
+
+void QtChatListWindow::handleItemActivated(const QModelIndex& item) {
+
+}
+
+void QtChatListWindow::addMUCBookmark(boost::shared_ptr<MUCBookmark> bookmark) {
+	model_->addMUCBookmark(bookmark);
+}
+
+void QtChatListWindow::removeMUCBookmark(boost::shared_ptr<MUCBookmark> bookmark) {
+	model_->removeMUCBookmark(bookmark);
+}
+
+}
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.h b/Swift/QtUI/ChatList/QtChatListWindow.h
new file mode 100644
index 0000000..b7fe6d2
--- /dev/null
+++ b/Swift/QtUI/ChatList/QtChatListWindow.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <QTreeView>
+
+#import "Swift/Controllers/UIInterfaces/ChatListWindow.h"
+#import "Swift/Controllers/UIEvents/UIEventStream.h"
+#import "Swift/QtUI/ChatList/ChatListModel.h"
+#import "Swift/QtUI/ChatList/ChatListDelegate.h"
+
+namespace Swift {
+
+	class QtChatListWindow : public QTreeView, public ChatListWindow {
+		Q_OBJECT
+		public:
+			QtChatListWindow(UIEventStream *uiEventStream, QWidget* parent = NULL);
+			virtual ~QtChatListWindow();
+			void addMUCBookmark(boost::shared_ptr<MUCBookmark> bookmark);
+			void removeMUCBookmark(boost::shared_ptr<MUCBookmark> bookmark);
+		private slots:
+			void handleItemActivated(const QModelIndex&);
+		private:
+			UIEventStream* eventStream_;
+			ChatListModel* model_;
+			ChatListDelegate* delegate_;
+	};
+
+}
diff --git a/Swift/QtUI/ChatList/QtChatListWindowFactory.cpp b/Swift/QtUI/ChatList/QtChatListWindowFactory.cpp
new file mode 100644
index 0000000..5540206
--- /dev/null
+++ b/Swift/QtUI/ChatList/QtChatListWindowFactory.cpp
@@ -0,0 +1,16 @@
+#include "Swift/QtUI/ChatList/QtChatListWindowFactory.h"
+
+#include "Swift/QtUI/QtMainWindowFactory.h"
+#include "Swift/QtUI/QtMainWindow.h"
+
+namespace Swift {
+
+QtChatListWindowFactory::QtChatListWindowFactory(QtMainWindowFactory* mainWindowFactory) {
+	mainWindowFactory_ = mainWindowFactory;
+}
+
+ChatListWindow* QtChatListWindowFactory::createWindow(UIEventStream* uiEventStream) {
+	return ((QtMainWindow*)mainWindowFactory_->getLastCreatedWindow())->getChatListWindow();
+}
+
+}
diff --git a/Swift/QtUI/ChatList/QtChatListWindowFactory.h b/Swift/QtUI/ChatList/QtChatListWindowFactory.h
new file mode 100644
index 0000000..47efe14
--- /dev/null
+++ b/Swift/QtUI/ChatList/QtChatListWindowFactory.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h"
+
+namespace Swift {
+	class QtMainWindowFactory;
+	class QtChatListWindowFactory : public ChatListWindowFactory{
+		public:
+			QtChatListWindowFactory(QtMainWindowFactory* mainWindowFactory);
+			ChatListWindow* createWindow(UIEventStream* uiEventStream);
+		private:
+			QtMainWindowFactory* mainWindowFactory_;
+	};
+}
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
index 5a6c620..b8d9a5c 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -52,8 +52,11 @@ QtMainWindow::QtMainWindow(UIEventStream* uiEventStream, QtTreeWidgetFactory *tr
 	eventWindow_ = new QtEventWindow(uiEventStream_);
 	connect(eventWindow_, SIGNAL(onNewEventCountUpdated(int)), this, SLOT(handleEventCountUpdated(int)));
 	
+	chatListWindow_ = new QtChatListWindow(uiEventStream_);
+
 	tabs_->addTab(eventWindow_, "Events");
-	
+	tabs_->addTab(chatListWindow_, "Chats");
+
 	this->setLayout(mainLayout);
 	
 	QMenu* viewMenu = new QMenu(tr("View"), this);
@@ -81,6 +84,10 @@ QtEventWindow* QtMainWindow::getEventWindow() {
 	return eventWindow_;
 }
 
+QtChatListWindow* QtMainWindow::getChatListWindow() {
+	return chatListWindow_;
+}
+
 void QtMainWindow::handleEventCountUpdated(int count) {
 	QColor eventTabColor = (count == 0) ? QColor(-1, -1, -1) : QColor(255, 0, 0); // invalid resets to default
 	int eventIndex = 1;
diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h
index 940930e..ac3e444 100644
--- a/Swift/QtUI/QtMainWindow.h
+++ b/Swift/QtUI/QtMainWindow.h
@@ -6,6 +6,7 @@
 #include "Swift/Controllers/MainWindow.h"
 #include "Swift/QtUI/QtRosterHeader.h"
 #include "Swift/QtUI/EventViewer/QtEventWindow.h"
+#include "Swift/QtUI/ChatList/QtChatListWindow.h"
 
 #include <vector>
 
@@ -35,6 +36,7 @@ namespace Swift {
 			void setMyStatusText(const String& status);
 			void setMyStatusType(const StatusShow::Type type);
 			QtEventWindow* getEventWindow();
+			QtChatListWindow* getChatListWindow();
 		private slots:
 			void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage);
 			void handleShowOfflineToggled(bool);
@@ -56,6 +58,7 @@ namespace Swift {
 			QWidget* contactsTabWidget_;
 			QWidget* eventsTabWidget_;
 			QtEventWindow* eventWindow_;
+			QtChatListWindow* chatListWindow_;
 			UIEventStream* uiEventStream_;
 	};
 }
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index 6c9dc11..1c60c12 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -9,6 +9,7 @@
 #include "QtSystemTray.h"
 #include "QtSoundPlayer.h"
 #include "QtXMLConsoleWidgetFactory.h"
+#include "ChatList/QtChatListWindowFactory.h"
 #include "EventViewer/QtEventWindowFactory.h"
 #include <boost/bind.hpp>
 #include <QSplitter>
@@ -54,11 +55,12 @@ QtSwift::QtSwift(bool netbookMode) : autoUpdater_(NULL) {
 	rosterWindowFactory_ = new QtMainWindowFactory(treeWidgetFactory_);
 	eventWindowFactory_ = new QtEventWindowFactory(rosterWindowFactory_);
 	xmlConsoleWidgetFactory_ = new QtXMLConsoleWidgetFactory(tabs_);
+	chatListWindowFactory_ = new QtChatListWindowFactory(rosterWindowFactory_);
 	soundPlayer_ = new QtSoundPlayer();
 	if (splitter_) {
 		splitter_->show();
 	}
-	mainController_ = new MainController(chatWindowFactory_, rosterWindowFactory_, loginWindowFactory_, treeWidgetFactory_, eventWindowFactory_, settings_, application_, systemTray_, soundPlayer_, xmlConsoleWidgetFactory_);
+	mainController_ = new MainController(chatWindowFactory_, rosterWindowFactory_, loginWindowFactory_, treeWidgetFactory_, eventWindowFactory_, settings_, application_, systemTray_, soundPlayer_, xmlConsoleWidgetFactory_, chatListWindowFactory_);
 
 	PlatformAutoUpdaterFactory autoUpdaterFactory;
 	if (autoUpdaterFactory.isSupported()) {
@@ -82,6 +84,7 @@ QtSwift::~QtSwift() {
 	delete tabs_;
 	delete xmlConsoleWidgetFactory_;
 	delete eventWindowFactory_;
+	delete chatListWindowFactory_;
 }
 
 }
diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h
index 5a3f9b3..7471bc2 100644
--- a/Swift/QtUI/QtSwift.h
+++ b/Swift/QtUI/QtSwift.h
@@ -22,6 +22,7 @@ namespace Swift {
 	class QtSystemTray;
 	class QtSoundPlayer;
 	class QtEventWindowFactory;
+	class QtChatListWindowFactory;
 		
 	class QtSwift : public QObject {
 		Q_OBJECT
@@ -32,6 +33,7 @@ namespace Swift {
 			MainController *mainController_;
 			QtTreeWidgetFactory *treeWidgetFactory_;
 			QtChatWindowFactory *chatWindowFactory_;
+			QtChatListWindowFactory *chatListWindowFactory_;
 			QtMainWindowFactory *rosterWindowFactory_;
 			QtLoginWindowFactory *loginWindowFactory_;
 			QtXMLConsoleWidgetFactory* xmlConsoleWidgetFactory_;
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index e6a62aa..d2e6c37 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -84,6 +84,10 @@ sources = [
     "EventViewer/QtEventWindowFactory.cpp",
     "EventViewer/QtEventWindow.cpp",
     "EventViewer/QtEvent.cpp",
+    "ChatList/QtChatListWindow.cpp",
+    "ChatList/QtChatListWindowFactory.cpp",
+    "ChatList/ChatListModel.cpp",
+    "ChatList/ChatListDelegate.cpp",
     "QtSubscriptionRequestWindow.cpp",
     "QtRosterHeader.cpp",
     "qrc_DefaultTheme.cc",
diff --git a/Swiften/Avatars/AvatarManager.cpp b/Swiften/Avatars/AvatarManager.cpp
index 42451af..3825ffd 100644
--- a/Swiften/Avatars/AvatarManager.cpp
+++ b/Swiften/Avatars/AvatarManager.cpp
@@ -27,6 +27,10 @@ AvatarManager::~AvatarManager() {
 
 }
 
+void AvatarManager::setMUCRegistry(MUCRegistry* mucRegistry) {
+	mucRegistry_ = mucRegistry;
+}
+
 void AvatarManager::handlePresenceReceived(boost::shared_ptr<Presence> presence) {
 	boost::shared_ptr<VCardUpdate> update = presence->getPayload<VCardUpdate>();
 	if (!update) {
@@ -97,7 +101,7 @@ boost::filesystem::path AvatarManager::getAvatarPath(const JID& jid) const {
 
 JID AvatarManager::getAvatarJID(const JID& jid) const {
 	JID bareFrom = jid.toBare();
-	return (mucRegistry_->isMUC(bareFrom) ? jid : bareFrom);
+	return (mucRegistry_ && mucRegistry_->isMUC(bareFrom)) ? jid : bareFrom;
 }
 
 
diff --git a/Swiften/Avatars/AvatarManager.h b/Swiften/Avatars/AvatarManager.h
index 448526e..fd308d9 100644
--- a/Swiften/Avatars/AvatarManager.h
+++ b/Swiften/Avatars/AvatarManager.h
@@ -19,9 +19,11 @@ namespace Swift {
 
 	class AvatarManager {
 		public:
-			AvatarManager(StanzaChannel*, IQRouter*, AvatarStorage*, MUCRegistry*);
+			AvatarManager(StanzaChannel*, IQRouter*, AvatarStorage*, MUCRegistry* = NULL);
 			virtual ~AvatarManager();
 
+			virtual void setMUCRegistry(MUCRegistry*);
+
 			virtual String getAvatarHash(const JID&) const;
 			virtual boost::filesystem::path getAvatarPath(const JID&) const;
 			virtual void setAvatar(const JID&, const ByteArray& avatar);
diff --git a/Swiften/MUC/MUCBookmarkManager.cpp b/Swiften/MUC/MUCBookmarkManager.cpp
index f789300..f9295e2 100644
--- a/Swiften/MUC/MUCBookmarkManager.cpp
+++ b/Swiften/MUC/MUCBookmarkManager.cpp
@@ -11,6 +11,7 @@ MUCBookmarkManager::MUCBookmarkManager(IQRouter* iqRouter) {
 void MUCBookmarkManager::addBookmark(boost::shared_ptr<MUCBookmark> bookmark) {
 	bookmarks_.push_back(bookmark);
 	flush();
+	onBookmarkAdded(bookmark);
 }
 
 
@@ -19,6 +20,7 @@ void MUCBookmarkManager::removeBookmark(boost::shared_ptr<MUCBookmark> bookmark)
 	for (it = bookmarks_.begin(); it != bookmarks_.end(); it++) {
 		if ((*it).get() == bookmark.get()) {
 			bookmarks_.erase(it);
+			onBookmarkRemoved(bookmark);
 			return;
 		}
 	}
diff --git a/Swiften/MUC/MUCBookmarkManager.h b/Swiften/MUC/MUCBookmarkManager.h
index e1f8708..ade2e3e 100644
--- a/Swiften/MUC/MUCBookmarkManager.h
+++ b/Swiften/MUC/MUCBookmarkManager.h
@@ -18,7 +18,8 @@ namespace Swift {
 			void flush();
 			/** Returns pointers to the bookmarks. These can be edited, and then flush()ed.*/
 			const std::vector<boost::shared_ptr<MUCBookmark> >& getBookmarks(); 
-			boost::signal<void ()> onBookmarksChanged;
+			boost::signal<void (boost::shared_ptr<MUCBookmark>)> onBookmarkAdded;
+			boost::signal<void (boost::shared_ptr<MUCBookmark>)> onBookmarkRemoved;
 		private:
 			
 			std::vector<boost::shared_ptr<MUCBookmark> > bookmarks_;
-- 
cgit v0.10.2-6-g49f6