From f48dbee2fb5acf7c0abc9d7897d736233b1f0d2f Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Wed, 27 Apr 2011 21:42:11 +0100
Subject: Start on recent chats list


diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index 281d968..4ac814b 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -106,6 +106,7 @@ void ChatControllerBase::handleSendMessageRequest(const std::string &body) {
 	}
 	stanzaChannel_->sendMessage(message);
 	postSendMessage(message->getBody(), boost::dynamic_pointer_cast<Stanza>(message));
+	onActivity(message->getBody());
 }
 
 void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog> catalog, ErrorPayload::ref error) {
@@ -181,6 +182,7 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m
 		}
 
 		addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp);
+		onActivity(body);
 	}
 	chatWindow_->show();
 	chatWindow_->setUnreadMessageCount(unreadMessages_.size());
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index 9573b1b..4898320 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -47,6 +47,8 @@ namespace Swift {
 			virtual void setOnline(bool online);
 			virtual void setEnabled(bool enabled);
 			virtual void setToJID(const JID& jid) {toJID_ = jid;};
+			/** Used for determining when something is recent.*/
+			boost::signal<void (const std::string& /*activity*/)> onActivity;
 		protected:
 			ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory);
 
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 972501f..bd917e7 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2011 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -99,7 +99,7 @@ void ChatsManager::setupBookmarks() {
 
 		if (chatListWindow_) {
 			chatListWindow_->setBookmarksEnabled(false);
-			chatListWindow_->clear();
+			chatListWindow_->clearBookmarks();
 		}
 	}
 }
@@ -123,6 +123,15 @@ void ChatsManager::handleMUCBookmarkRemoved(const MUCBookmark& bookmark) {
 	chatListWindow_->removeMUCBookmark(bookmark);
 }
 
+void ChatsManager::handleChatActivity(const JID& jid, const std::string& activity) {
+	/* FIXME: MUC use requires changes here. */
+	ChatListWindow::Chat chat(jid, nickResolver_->jidToNick(jid), activity, false);
+	/* FIXME: handle nick changes */
+	recentChats_.erase(std::remove(recentChats_.begin(), recentChats_.end(), chat), recentChats_.end());
+	recentChats_.push_front(chat);
+	chatListWindow_->setRecents(recentChats_);
+}
+
 void ChatsManager::handleUserLeftMUC(MUCController* mucController) {
 	std::map<JID, MUCController*>::iterator it;
 	for (it = mucControllers_.begin(); it != mucControllers_.end(); it++) {
@@ -245,6 +254,7 @@ ChatController* ChatsManager::createNewChatController(const JID& contact) {
 	ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_);
 	chatControllers_[contact] = controller;
 	controller->setAvailableServerFeatures(serverDiscoInfo_);
+	controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1));
 	return controller;
 }
 
@@ -303,6 +313,7 @@ void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional
 		controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller));
 	}
 	mucControllers_[mucJID]->activateChatWindow();
+	/* FIXME: handleChatActivity connection for recents, and changes to that method.*/
 }
 
 void ChatsManager::handleSearchMUCRequest() {
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index 34cd1bf..617f0c1 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2011 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -11,13 +11,14 @@
 #include <boost/shared_ptr.hpp>
 
 #include <string>
-#include "Swiften/Elements/DiscoInfo.h"
-#include "Swiften/Elements/Message.h"
-#include "Swiften/Elements/Presence.h"
-#include "Swiften/JID/JID.h"
-#include "Swiften/MUC/MUCRegistry.h"
-#include "Swift/Controllers/UIEvents/UIEventStream.h"
-#include "Swiften/MUC/MUCBookmark.h"
+#include <Swiften/Elements/DiscoInfo.h>
+#include <Swiften/Elements/Message.h>
+#include <Swiften/Elements/Presence.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/MUC/MUCRegistry.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIInterfaces/ChatListWindow.h>
+#include <Swiften/MUC/MUCBookmark.h>
 
 namespace Swift {
 	class EventController;
@@ -34,7 +35,6 @@ namespace Swift {
 	class IQRouter;
 	class PresenceSender;
 	class MUCBookmarkManager;
-	class ChatListWindow;
 	class ChatListWindowFactory;
 	class TimerFactory;
 	class EntityCapsProvider;
@@ -52,12 +52,6 @@ namespace Swift {
 			void setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info);
 			void handleIncomingMessage(boost::shared_ptr<Message> message);
 		private:
-			class Chat {
-				public: Chat(const JID& jid, bool isMUC, const std::string& nick) : jid(jid), isMUC(isMUC), nick(nick) {}
-				JID jid;
-				bool isMUC;
-				std::string nick;
-			};
 			void handleChatRequest(const std::string& contact);
 			void handleJoinMUCRequest(const JID& muc, const boost::optional<std::string>& nick, bool autoJoin);
 			void handleSearchMUCRequest();
@@ -69,6 +63,7 @@ namespace Swift {
 			void handleMUCBookmarkRemoved(const MUCBookmark& bookmark);
 			void handleUserLeftMUC(MUCController* mucController);
 			void handleBookmarksReady();
+			void handleChatActivity(const JID& jid, const std::string& activity);
 			void setupBookmarks();
 			void loadRecents();
 			void saveRecents();
@@ -101,6 +96,6 @@ namespace Swift {
 			EntityCapsProvider* entityCapsProvider_;
 			MUCManager* mucManager;
 			MUCSearchController* mucSearchController_;
-			std::vector<Chat> recentChats_;
+			std::list<ChatListWindow::Chat> recentChats_;
 	};
 }
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index 40f7445..c266134 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -10,6 +10,7 @@
 
 #include "Swift/Controllers/Chat/ChatsManager.h"
 
+#include "Swift/Controllers/Chat/UnitTest/MockChatListWindow.h"
 #include "Swift/Controllers/UIInterfaces/ChatWindow.h"
 #include "Swift/Controllers/Settings/DummySettingsProvider.h"
 #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
@@ -82,7 +83,8 @@ public:
 		chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>();
 		mucSearchWindowFactory_ = mocks_->InterfaceMock<MUCSearchWindowFactory>();
 		settings_ = new DummySettingsProvider();
-		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(NULL);
+		chatListWindow_ = new MockChatListWindow();
+		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_);
 		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, settings_);
 
 		avatarManager_ = new NullAvatarManager();
@@ -109,6 +111,7 @@ public:
 		delete xmppRoster_;
 		delete entityCapsManager_;
 		delete capsProvider_;
+		delete chatListWindow_;
 	}
 
 	void testFirstOpenWindowIncoming() {
@@ -346,6 +349,7 @@ private:
 	CapsProvider* capsProvider_;
 	MUCManager* mucManager_;
 	DummySettingsProvider* settings_;
+	ChatListWindow* chatListWindow_;
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest);
diff --git a/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h b/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h
new file mode 100644
index 0000000..408a490
--- /dev/null
+++ b/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swift/Controllers/UIInterfaces/ChatListWindow.h"
+
+namespace Swift {
+
+	class MockChatListWindow : public ChatListWindow {
+		public:
+			MockChatListWindow() {};
+			virtual ~MockChatListWindow() {};
+			void addMUCBookmark(const MUCBookmark& /*bookmark*/) {}
+			void removeMUCBookmark(const MUCBookmark& /*bookmark*/) {}
+			void setBookmarksEnabled(bool /*enabled*/) {}
+			void setRecents(const std::list<ChatListWindow::Chat>& /*recents*/) {}
+			void clearBookmarks() {}
+	};
+
+}
diff --git a/Swift/Controllers/UIInterfaces/ChatListWindow.h b/Swift/Controllers/UIInterfaces/ChatListWindow.h
index a2a0874..f717684 100644
--- a/Swift/Controllers/UIInterfaces/ChatListWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatListWindow.h
@@ -1,23 +1,35 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2011 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
 #pragma once
 
+#include <list>
 #include <boost/shared_ptr.hpp>
-
-#include "Swiften/MUC/MUCBookmark.h"
+#include <Swiften/MUC/MUCBookmark.h>
 
 namespace Swift {
 	class ChatListWindow {
 		public:
+			class Chat {
+				public:
+					Chat(const JID& jid, const std::string& chatName, const std::string& activity, bool isMUC, const std::string& nick = "") : jid(jid), chatName(chatName), activity(activity), isMUC(isMUC), nick(nick) {}
+					/** Assume that nicks aren't important for equality */
+					bool operator==(const Chat& other) const {return jid == other.jid && isMUC == other.isMUC;};
+					JID jid;
+					std::string chatName;
+					std::string activity;
+					bool isMUC;
+					std::string nick;
+			};
 			virtual ~ChatListWindow();
 
 			virtual void setBookmarksEnabled(bool enabled) = 0;
 			virtual void addMUCBookmark(const MUCBookmark& bookmark) = 0;
 			virtual void removeMUCBookmark(const MUCBookmark& bookmark) = 0;
-			virtual void clear() = 0;
+			virtual void setRecents(const std::list<Chat>& recents) = 0;
+			virtual void clearBookmarks() = 0;
 	};
 }
diff --git a/Swift/QtUI/ChatList/ChatListDelegate.cpp b/Swift/QtUI/ChatList/ChatListDelegate.cpp
index 274a10a..b2bfe0a 100644
--- a/Swift/QtUI/ChatList/ChatListDelegate.cpp
+++ b/Swift/QtUI/ChatList/ChatListDelegate.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2011 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -11,6 +11,7 @@
 #include "Swift/QtUI/Roster/GroupItemDelegate.h"
 #include "Swift/QtUI/ChatList/ChatListItem.h"
 #include "Swift/QtUI/ChatList/ChatListMUCItem.h"
+#include "Swift/QtUI/ChatList/ChatListRecentItem.h"
 #include "Swift/QtUI/ChatList/ChatListGroupItem.h"
 
 namespace Swift {
@@ -27,7 +28,11 @@ QSize ChatListDelegate::sizeHint(const QStyleOptionViewItem& option, const QMode
 	ChatListItem* item = static_cast<ChatListItem*>(index.internalPointer());
 	if (item && dynamic_cast<ChatListMUCItem*>(item)) {
 		return mucSizeHint(option, index);
-	} else if (item && dynamic_cast<ChatListGroupItem*>(item)) {
+	}
+	else if (item && dynamic_cast<ChatListRecentItem*>(item)) {
+		return recentSizeHint(option, index);
+	}
+	else if (item && dynamic_cast<ChatListGroupItem*>(item)) {
 		return groupDelegate_->sizeHint(option, index);
 	} 
 	return QStyledItemDelegate::sizeHint(option, index);
@@ -40,14 +45,23 @@ QSize ChatListDelegate::mucSizeHint(const QStyleOptionViewItem& /*option*/, cons
 	return QSize(150, sizeByText);
 }
 
+QSize ChatListDelegate::recentSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
+	return mucSizeHint(option, index);
+}
+
 void ChatListDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
 	ChatListItem* item = static_cast<ChatListItem*>(index.internalPointer());
 	if (item && dynamic_cast<ChatListMUCItem*>(item)) {
 		paintMUC(painter, option, dynamic_cast<ChatListMUCItem*>(item));
-	} else if (item && dynamic_cast<ChatListGroupItem*>(item)) {
+	}
+	else if (item && dynamic_cast<ChatListRecentItem*>(item)) {
+		paintRecent(painter, option, dynamic_cast<ChatListRecentItem*>(item));
+	}
+	else if (item && dynamic_cast<ChatListGroupItem*>(item)) {
 		ChatListGroupItem* group = dynamic_cast<ChatListGroupItem*>(item);
 		groupDelegate_->paint(painter, option, group->data(Qt::DisplayRole).toString(), group->rowCount(), option.state & QStyle::State_Open); 
-	} else {
+	}
+	else {
 		QStyledItemDelegate::paint(painter, option, index);
 	}
 }
@@ -78,9 +92,40 @@ void ChatListDelegate::paintMUC(QPainter* painter, const QStyleOptionViewItem& o
 	painter->setPen(QPen(QColor(160,160,160)));
 	
 	QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0));
-	DelegateCommons::drawElidedText(painter, detailRegion, item->data(DetailTextRole).toString());
+	DelegateCommons::drawElidedText(painter, detailRegion, item->data(ChatListMUCItem::DetailTextRole).toString());
 	
 	painter->restore();
 }
 
+void ChatListDelegate::paintRecent(QPainter* painter, const QStyleOptionViewItem& option, ChatListRecentItem* item) const {
+	painter->save();
+	QRect fullRegion(option.rect);
+	if ( option.state & QStyle::State_Selected ) {
+		painter->fillRect(fullRegion, option.palette.highlight());
+		painter->setPen(option.palette.highlightedText().color());
+	} else {
+		QColor nameColor = item->data(Qt::TextColorRole).value<QColor>();
+		painter->setPen(QPen(nameColor));
+	}
+
+	QFontMetrics nameMetrics(common_.nameFont);
+	painter->setFont(common_.nameFont);
+	int extraFontWidth = nameMetrics.width("H");
+	int leftOffset = common_.horizontalMargin * 2 + extraFontWidth / 2;
+	QRect textRegion(fullRegion.adjusted(leftOffset, 0, 0, 0));
+
+	int nameHeight = nameMetrics.height() + common_.verticalMargin;
+	QRect nameRegion(textRegion.adjusted(0, common_.verticalMargin, 0, 0));
+
+	DelegateCommons::drawElidedText(painter, nameRegion, item->data(Qt::DisplayRole).toString());
+
+	painter->setFont(common_.detailFont);
+	painter->setPen(QPen(QColor(160,160,160)));
+
+	QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0));
+	DelegateCommons::drawElidedText(painter, detailRegion, item->data(ChatListRecentItem::DetailTextRole).toString());
+
+	painter->restore();
+}
+
 }
diff --git a/Swift/QtUI/ChatList/ChatListDelegate.h b/Swift/QtUI/ChatList/ChatListDelegate.h
index f6c6c40..a898df4 100644
--- a/Swift/QtUI/ChatList/ChatListDelegate.h
+++ b/Swift/QtUI/ChatList/ChatListDelegate.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2011 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -12,6 +12,7 @@
 
 namespace Swift {
 	class ChatListMUCItem;
+	class ChatListRecentItem;
 	class ChatListDelegate : public QStyledItemDelegate {
 		public:
 			ChatListDelegate();
@@ -20,7 +21,9 @@ namespace Swift {
 			void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
 		private:
 			void paintMUC(QPainter* painter, const QStyleOptionViewItem& option, ChatListMUCItem* item) const;
+			void paintRecent(QPainter* painter, const QStyleOptionViewItem& option, ChatListRecentItem* item) const;
 			QSize mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const;
+			QSize recentSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const;
 
 			DelegateCommons common_;
 			GroupItemDelegate* groupDelegate_;
diff --git a/Swift/QtUI/ChatList/ChatListGroupItem.h b/Swift/QtUI/ChatList/ChatListGroupItem.h
index cc4d4af..2fb2284 100644
--- a/Swift/QtUI/ChatList/ChatListGroupItem.h
+++ b/Swift/QtUI/ChatList/ChatListGroupItem.h
@@ -13,8 +13,8 @@
 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); qStableSort(items_.begin(), items_.end(), pointerItemLessThan);};
+			ChatListGroupItem(const QString& name, ChatListGroupItem* parent, bool sorted = true) : ChatListItem(parent), name_(name), sorted_(sorted) {};
+			void addItem(ChatListItem* item) {items_.push_back(item); if (sorted_) {qStableSort(items_.begin(), items_.end(), pointerItemLessThan);}};
 			void remove(int index) {items_.removeAt(index);};
 			int rowCount() {return items_.size();};
 			ChatListItem* item(int i) {return items_[i];};
@@ -30,5 +30,6 @@ namespace Swift {
 
 			QString name_;
 			QList<ChatListItem*> items_;
+			bool sorted_;
 	};
 }
diff --git a/Swift/QtUI/ChatList/ChatListMUCItem.h b/Swift/QtUI/ChatList/ChatListMUCItem.h
index 068f5d6..f26aa67 100644
--- a/Swift/QtUI/ChatList/ChatListMUCItem.h
+++ b/Swift/QtUI/ChatList/ChatListMUCItem.h
@@ -15,14 +15,14 @@
 #include "Swift/QtUI/ChatList/ChatListItem.h"
 
 namespace Swift {
-	enum MUCItemRoles {
-		DetailTextRole = Qt::UserRole/*,
-		AvatarRole = Qt::UserRole + 1,
-		PresenceIconRole = Qt::UserRole + 2,
-		StatusShowTypeRole = Qt::UserRole + 3*/
-	};
 	class ChatListMUCItem : public ChatListItem {
 		public:
+			enum MUCItemRoles {
+				DetailTextRole = Qt::UserRole/*,
+				AvatarRole = Qt::UserRole + 1,
+				PresenceIconRole = Qt::UserRole + 2,
+				StatusShowTypeRole = Qt::UserRole + 3*/
+			};
 			ChatListMUCItem(const MUCBookmark& bookmark, ChatListGroupItem* parent);
 			const MUCBookmark& getBookmark();
 			QVariant data(int role) const;
diff --git a/Swift/QtUI/ChatList/ChatListModel.cpp b/Swift/QtUI/ChatList/ChatListModel.cpp
index ba7b766..dc60d65 100644
--- a/Swift/QtUI/ChatList/ChatListModel.cpp
+++ b/Swift/QtUI/ChatList/ChatListModel.cpp
@@ -1,22 +1,25 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2011 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-#include "Swift/QtUI/ChatList/ChatListModel.h"
+#include <Swift/QtUI/ChatList/ChatListModel.h>
 
-#include "Swift/QtUI/ChatList/ChatListMUCItem.h"
+#include <Swift/QtUI/ChatList/ChatListMUCItem.h>
+#include <Swift/QtUI/ChatList/ChatListRecentItem.h>
 
 namespace Swift {
 
 ChatListModel::ChatListModel() {
 	root_ = new ChatListGroupItem("", NULL);
 	mucBookmarks_ = new ChatListGroupItem(tr("Bookmarked Rooms"), root_);
+	recents_ = new ChatListGroupItem(tr("Recent Chats"), root_, false);
 	root_->addItem(mucBookmarks_);
+	root_->addItem(recents_);
 }
 
-void ChatListModel::clear() {
+void ChatListModel::clearBookmarks() {
 	emit layoutAboutToBeChanged();
 	mucBookmarks_->clear();
 	emit layoutChanged();
@@ -43,6 +46,15 @@ void ChatListModel::removeMUCBookmark(const Swift::MUCBookmark& bookmark) {
 	}
 }
 
+void ChatListModel::setRecents(const std::list<ChatListWindow::Chat>& recents) {
+	emit layoutAboutToBeChanged();
+	recents_->clear();
+	foreach (const ChatListWindow::Chat chat, recents) {
+		recents_->addItem(new ChatListRecentItem(chat, recents_));
+	}
+	emit layoutChanged();
+}
+
 int ChatListModel::columnCount(const QModelIndex& /*parent*/) const {
 	return 1;
 }
diff --git a/Swift/QtUI/ChatList/ChatListModel.h b/Swift/QtUI/ChatList/ChatListModel.h
index adde148..8e7828c 100644
--- a/Swift/QtUI/ChatList/ChatListModel.h
+++ b/Swift/QtUI/ChatList/ChatListModel.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2011 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -11,9 +11,10 @@
 #include <QAbstractItemModel>
 #include <QList>
 
-#include "Swiften/MUC/MUCBookmark.h"
+#include <Swiften/MUC/MUCBookmark.h>
+#include <Swift/Controllers/UIInterfaces/ChatListWindow.h>
 
-#include "Swift/QtUI/ChatList/ChatListGroupItem.h"
+#include <Swift/QtUI/ChatList/ChatListGroupItem.h>
 
 namespace Swift {
 	class ChatListModel : public QAbstractItemModel {
@@ -28,9 +29,11 @@ namespace Swift {
 			QModelIndex parent(const QModelIndex& index) const;
 			int rowCount(const QModelIndex& parent = QModelIndex()) const;
 			ChatListItem* getItemForIndex(const QModelIndex& index) const;
-			void clear();
+			void clearBookmarks();
+			void setRecents(const std::list<ChatListWindow::Chat>& recents);
 		private:
 			ChatListGroupItem* mucBookmarks_;
+			ChatListGroupItem* recents_;
 			ChatListGroupItem* root_;
 	};
 
diff --git a/Swift/QtUI/ChatList/ChatListRecentItem.cpp b/Swift/QtUI/ChatList/ChatListRecentItem.cpp
new file mode 100644
index 0000000..8b6707c
--- /dev/null
+++ b/Swift/QtUI/ChatList/ChatListRecentItem.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swift/QtUI/ChatList/ChatListRecentItem.h>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+namespace Swift {
+ChatListRecentItem::ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent) : ChatListItem(parent), chat_(chat) {
+
+}
+
+const ChatListWindow::Chat& ChatListRecentItem::getChat() {
+	return chat_;
+}
+
+QVariant ChatListRecentItem::data(int role) const {
+	switch (role) {
+		case Qt::DisplayRole: return P2QSTRING(chat_.chatName);
+		case DetailTextRole: return P2QSTRING(chat_.activity);
+			/*case Qt::TextColorRole: return textColor_;
+		case Qt::BackgroundColorRole: return backgroundColor_;
+		case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant();
+		case StatusTextRole: return statusText_;
+		case AvatarRole: return avatar_;
+		case PresenceIconRole: return getPresenceIcon();*/
+		default: return QVariant();
+	}
+}
+
+}
diff --git a/Swift/QtUI/ChatList/ChatListRecentItem.h b/Swift/QtUI/ChatList/ChatListRecentItem.h
new file mode 100644
index 0000000..c2646cc
--- /dev/null
+++ b/Swift/QtUI/ChatList/ChatListRecentItem.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <QList>
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/MUC/MUCBookmark.h>
+#include <Swift/Controllers/UIInterfaces/ChatListWindow.h>
+
+#include <Swift/QtUI/ChatList/ChatListItem.h>
+
+namespace Swift {
+	class ChatListRecentItem : public ChatListItem {
+		public:
+			enum RecentItemRoles {
+				DetailTextRole = Qt::UserRole/*,
+				AvatarRole = Qt::UserRole + 1,
+				PresenceIconRole = Qt::UserRole + 2,
+				StatusShowTypeRole = Qt::UserRole + 3*/
+			};
+			ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent);
+			const ChatListWindow::Chat& getChat();
+			QVariant data(int role) const;
+		private:
+			ChatListWindow::Chat chat_;
+	};
+}
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.cpp b/Swift/QtUI/ChatList/QtChatListWindow.cpp
index b532cdb..a3dbd78 100644
--- a/Swift/QtUI/ChatList/QtChatListWindow.cpp
+++ b/Swift/QtUI/ChatList/QtChatListWindow.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2011 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -10,9 +10,11 @@
 #include <QContextMenuEvent>
 
 #include "Swift/QtUI/ChatList/ChatListMUCItem.h"
+#include "Swift/QtUI/ChatList/ChatListRecentItem.h"
 #include "Swift/QtUI/QtAddBookmarkWindow.h"
 #include "Swift/QtUI/QtEditBookmarkWindow.h"
 #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
+#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
 #include "Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h"
 #include "Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h"
 #include "Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h"
@@ -77,10 +79,15 @@ void QtChatListWindow::handleItemActivated(const QModelIndex& index) {
 		boost::shared_ptr<UIEvent> event(new JoinMUCUIEvent(mucItem->getBookmark().getRoom(), mucItem->getBookmark().getNick()));
 		eventStream_->send(event);
 	}
+	ChatListRecentItem* recentItem = dynamic_cast<ChatListRecentItem*>(item);
+	if (recentItem) {
+		boost::shared_ptr<UIEvent> event(new RequestChatUIEvent(recentItem->getChat().jid));
+		eventStream_->send(event);
+	}
 }
 
-void QtChatListWindow::clear() {
-	model_->clear();
+void QtChatListWindow::clearBookmarks() {
+	model_->clearBookmarks();
 }
 
 void QtChatListWindow::addMUCBookmark(const MUCBookmark& bookmark) {
@@ -91,6 +98,10 @@ void QtChatListWindow::removeMUCBookmark(const MUCBookmark& bookmark) {
 	model_->removeMUCBookmark(bookmark);
 }
 
+void QtChatListWindow::setRecents(const std::list<ChatListWindow::Chat>& recents) {
+	model_->setRecents(recents);
+}
+
 void QtChatListWindow::handleRemoveBookmark() {
 	ChatListMUCItem* mucItem = dynamic_cast<ChatListMUCItem*>(contextMenuItem_);
 	if (!mucItem) return;
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.h b/Swift/QtUI/ChatList/QtChatListWindow.h
index 3a3e95f..f5c12f6 100644
--- a/Swift/QtUI/ChatList/QtChatListWindow.h
+++ b/Swift/QtUI/ChatList/QtChatListWindow.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2011 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -23,7 +23,8 @@ namespace Swift {
 			void addMUCBookmark(const MUCBookmark& bookmark);
 			void removeMUCBookmark(const MUCBookmark& bookmark);
 			void setBookmarksEnabled(bool enabled);
-			void clear();
+			void setRecents(const std::list<ChatListWindow::Chat>& recents);
+			void clearBookmarks();
 		private slots:
 			void handleItemActivated(const QModelIndex&);
 			void handleAddBookmark();
diff --git a/Swift/QtUI/QtTextEdit.cpp b/Swift/QtUI/QtTextEdit.cpp
index 3668220..79b3b8d 100644
--- a/Swift/QtUI/QtTextEdit.cpp
+++ b/Swift/QtUI/QtTextEdit.cpp
@@ -4,7 +4,7 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-#include "QtTextEdit.h"
+#include <Swift/QtUI/QtTextEdit.h>
 
 #include <QFontMetrics>
 #include <QKeyEvent>
@@ -22,19 +22,19 @@ void QtTextEdit::keyPressEvent(QKeyEvent* event) {
 	if ((key == Qt::Key_Enter || key == Qt::Key_Return)
 		&& (modifiers == Qt::NoModifier || modifiers == Qt::KeypadModifier)) {
 		emit returnPressed();
-	} else if (((key == Qt::Key_PageUp || key == Qt::Key_PageDown) && modifiers == Qt::ShiftModifier)
+	}
+	else if (((key == Qt::Key_PageUp || key == Qt::Key_PageDown) && modifiers == Qt::ShiftModifier)
 			   || (key == Qt::Key_C && modifiers == Qt::ControlModifier && textCursor().selectedText().isEmpty())
 			   || (key == Qt::Key_W && modifiers == Qt::ControlModifier)
 			   || (key == Qt::Key_PageUp && modifiers == Qt::ControlModifier)
 			   || (key == Qt::Key_PageDown && modifiers == Qt::ControlModifier)
-//			   || (key == Qt::Key_Left && modifiers == (Qt::ControlModifier | Qt::ShiftModifier))
-//			   || (key == Qt::Key_Right && modifiers == (Qt::ControlModifier | Qt::ShiftModifier))
 			   || (key == Qt::Key_Tab && modifiers == Qt::ControlModifier)
 			   || (key == Qt::Key_A && modifiers == Qt::AltModifier)
 			   || (key == Qt::Key_Tab)
 	) {
 		emit unhandledKeyPressEvent(event);
-	} else {
+	}
+	else {
 		QTextEdit::keyPressEvent(event);
 	}
 }
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 2f138cd..ef4f744 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -117,6 +117,7 @@ sources = [
     "ChatList/ChatListModel.cpp",
     "ChatList/ChatListDelegate.cpp",
     "ChatList/ChatListMUCItem.cpp",
+    "ChatList/ChatListRecentItem.cpp",
     "MUCSearch/QtMUCSearchWindow.cpp",
     "MUCSearch/MUCSearchModel.cpp",
     "MUCSearch/MUCSearchRoomItem.cpp",
-- 
cgit v0.10.2-6-g49f6