From b70a50f11da1ea57a0e89ff14882eed03944eb2a Mon Sep 17 00:00:00 2001
From: Richard Maudsley <richard.maudsley@isode.com>
Date: Tue, 6 May 2014 14:08:57 +0100
Subject: Right-click MUC in Recents to bookmark.

Change-Id: Idfb5907adf9bf53f0ac1f417dd57d49ecc897bb0

diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 654f735..1698b4a 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -11,6 +11,7 @@
 #include <boost/smart_ptr/make_shared.hpp>
 #include <boost/archive/text_oarchive.hpp>
 #include <boost/archive/text_iarchive.hpp>
+#include <boost/serialization/optional.hpp>
 #include <boost/serialization/vector.hpp>
 #include <boost/serialization/map.hpp>
 #include <boost/serialization/string.hpp>
@@ -62,6 +63,8 @@
 #include <Swiften/StringCodecs/Base64.h>
 #include <Swiften/Base/Log.h>
 
+BOOST_CLASS_VERSION(Swift::ChatListWindow::Chat, 1)
+
 namespace boost {
 namespace serialization {
 	template<class Archive> void save(Archive& ar, const Swift::JID& jid, const unsigned int /*version*/) {
@@ -79,13 +82,16 @@ namespace serialization {
 		split_free(ar, t, file_version);
 	}
 
-	template<class Archive> void serialize(Archive& ar, Swift::ChatListWindow::Chat& chat, const unsigned int /*version*/) {
+	template<class Archive> void serialize(Archive& ar, Swift::ChatListWindow::Chat& chat, const unsigned int version) {
 		ar & chat.jid;
 		ar & chat.chatName;
 		ar & chat.activity;
 		ar & chat.isMUC;
 		ar & chat.nick;
 		ar & chat.impromptuJIDs;
+		if (version > 0) {
+			ar & chat.password;
+		}
 	}
 }
 }
@@ -369,6 +375,7 @@ ChatListWindow::Chat ChatsManager::createChatListChatItem(const JID& jid, const
 		MUCController* controller = mucControllers_[jid.toBare()];
 		StatusShow::Type type = StatusShow::None;
 		std::string nick = "";
+		std::string password = "";
 		if (controller) {
 			unreadCount = controller->getUnreadCount();
 			if (controller->isJoined()) {
@@ -376,15 +383,19 @@ ChatListWindow::Chat ChatsManager::createChatListChatItem(const JID& jid, const
 			}
 			nick = controller->getNick();
 
+			if (controller->getPassword()) {
+				password = *controller->getPassword();
+			}
+
 			if (controller->isImpromptu()) {
-				ChatListWindow::Chat chat = ChatListWindow::Chat(jid, jid.toString(), activity, unreadCount, type, boost::filesystem::path(), true, nick);
+				ChatListWindow::Chat chat = ChatListWindow::Chat(jid, jid.toString(), activity, unreadCount, type, boost::filesystem::path(), true, nick, password);
 				typedef std::pair<std::string, JID> StringJIDPair;
 				std::map<std::string, JID> participants = controller->getParticipantJIDs();
 				chat.impromptuJIDs = participants;
 				return chat;
 			}
 		}
-		return ChatListWindow::Chat(jid, jid.toString(), activity, unreadCount, type, boost::filesystem::path(), true, nick);
+		return ChatListWindow::Chat(jid, jid.toString(), activity, unreadCount, type, boost::filesystem::path(), true, nick, password);
 	} else {
 		ChatController* controller = getChatControllerIfExists(jid, false);
 		if (controller) {
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index 4860fc8..6bc7067 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -243,6 +243,10 @@ const std::string& MUCController::getNick() {
 	return nick_;
 }
 
+const boost::optional<std::string> MUCController::getPassword() const {
+	return password_;
+}
+
 bool MUCController::isImpromptu() const {
 	return isImpromptu_;
 }
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index e78ff77..feffaba 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -62,6 +62,7 @@ namespace Swift {
 			static std::string concatenateListOfNames(const std::vector<NickJoinPart>& joinParts);
 			bool isJoined();
 			const std::string& getNick();
+			const boost::optional<std::string> getPassword() const;
 			bool isImpromptu() const;
 			std::map<std::string, JID> getParticipantJIDs() const;
 			void sendInvites(const std::vector<JID>& jids, const std::string& reason) const;
diff --git a/Swift/Controllers/UIInterfaces/ChatListWindow.h b/Swift/Controllers/UIInterfaces/ChatListWindow.h
index 67cd0ff..38d8c3e 100644
--- a/Swift/Controllers/UIInterfaces/ChatListWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatListWindow.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -22,8 +22,8 @@ namespace Swift {
 			class Chat {
 				public:
 					Chat() : statusType(StatusShow::None), isMUC(false), unreadCount(0) {}
-					Chat(const JID& jid, const std::string& chatName, const std::string& activity, int unreadCount, StatusShow::Type statusType, const boost::filesystem::path& avatarPath, bool isMUC, const std::string& nick = "")
-					: jid(jid), chatName(chatName), activity(activity), statusType(statusType), isMUC(isMUC), nick(nick), unreadCount(unreadCount), avatarPath(avatarPath) {}
+					Chat(const JID& jid, const std::string& chatName, const std::string& activity, int unreadCount, StatusShow::Type statusType, const boost::filesystem::path& avatarPath, bool isMUC, const std::string& nick = "", const boost::optional<std::string> password = boost::optional<std::string>())
+					: jid(jid), chatName(chatName), activity(activity), statusType(statusType), isMUC(isMUC), nick(nick), password(password), unreadCount(unreadCount), avatarPath(avatarPath) {}
 					/** Assume that nicks and other transient features aren't important for equality */
 					bool operator==(const Chat& other) const {
 						if (impromptuJIDs.empty()) {
@@ -73,6 +73,7 @@ namespace Swift {
 					StatusShow::Type statusType;
 					bool isMUC;
 					std::string nick;
+					boost::optional<std::string> password;
 					int unreadCount;
 					boost::filesystem::path avatarPath;
 					std::map<std::string, JID> impromptuJIDs;
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.cpp b/Swift/QtUI/ChatList/QtChatListWindow.cpp
index 7455fb5..210124b 100644
--- a/Swift/QtUI/ChatList/QtChatListWindow.cpp
+++ b/Swift/QtUI/ChatList/QtChatListWindow.cpp
@@ -68,6 +68,10 @@ void QtChatListWindow::handleSettingChanged(const std::string& setting) {
 	}
 }
 
+void QtChatListWindow::handleClearRecentsRequested() {
+	onClearRecentsRequested();
+}
+
 void QtChatListWindow::setBookmarksEnabled(bool enabled) {
 	bookmarksEnabled_ = enabled;
 }
@@ -84,9 +88,11 @@ void QtChatListWindow::setupContextMenus() {
 	mucMenu_->addAction(tr("Add New Bookmark"), this, SLOT(handleAddBookmark()));
 	mucMenu_->addAction(tr("Edit Bookmark"), this, SLOT(handleEditBookmark()));
 	mucMenu_->addAction(tr("Remove Bookmark"), this, SLOT(handleRemoveBookmark()));
+	mucRecentsMenu_ = new QMenu();
+	mucRecentsMenu_->addAction(tr("Add to Bookmarks"), this, SLOT(handleAddBookmarkFromRecents()));
+	mucRecentsMenu_->addAction(tr("Clear recents"), this, SLOT(handleClearRecentsRequested()));
 	emptyMenu_ = new QMenu();
 	emptyMenu_->addAction(tr("Add New Bookmark"), this, SLOT(handleAddBookmark()));
-	
 }
 
 void QtChatListWindow::handleItemActivated(const QModelIndex& index) {
@@ -142,6 +148,17 @@ void QtChatListWindow::handleRemoveBookmark() {
 	eventStream_->send(boost::shared_ptr<UIEvent>(new RemoveMUCBookmarkUIEvent(mucItem->getBookmark())));
 }
 
+void QtChatListWindow::handleAddBookmarkFromRecents() {
+	ChatListRecentItem* item = dynamic_cast<ChatListRecentItem*>(contextMenuItem_);
+	if (item) {
+		const ChatListWindow::Chat& chat = item->getChat();
+		MUCBookmark bookmark(chat.jid, chat.jid.toBare().toString());
+		bookmark.setNick(chat.nick);
+		bookmark.setPassword(chat.password);
+		eventStream_->send(boost::shared_ptr<UIEvent>(new AddMUCBookmarkUIEvent(bookmark)));
+	}
+}
+
 void QtChatListWindow::handleAddBookmark() {
 	(new QtAddBookmarkWindow(eventStream_))->show();
 }
@@ -168,22 +185,28 @@ void QtChatListWindow::contextMenuEvent(QContextMenuEvent* event) {
 		emptyMenu_->exec(QCursor::pos());
 		return;
 	}
+
 	ChatListMUCItem* mucItem = dynamic_cast<ChatListMUCItem*>(baseItem);
 	if (mucItem) {
 		if (!bookmarksEnabled_) {
 			return;
 		}
 		mucMenu_->exec(QCursor::pos());
+		return;
 	}
-	else {
-		QMenu menu;
-		QAction* clearRecents = menu.addAction(tr("Clear recents"));
-		menu.addAction(clearRecents);
-		QAction* result = menu.exec(event->globalPos());
-		if (result == clearRecents) {
-			onClearRecentsRequested();
+
+	ChatListRecentItem* recentItem = dynamic_cast<ChatListRecentItem*>(baseItem);
+	if (recentItem) {
+		const ChatListWindow::Chat& chat = recentItem->getChat();
+		if (chat.isMUC) {
+			mucRecentsMenu_->exec(QCursor::pos());
+			return;
 		}
 	}
+
+	QMenu menu;
+	menu.addAction(tr("Clear recents"), this, SLOT(handleClearRecentsRequested()));
+	menu.exec(event->globalPos());
 }
 
 }
diff --git a/Swift/QtUI/ChatList/QtChatListWindow.h b/Swift/QtUI/ChatList/QtChatListWindow.h
index e218266..1cba3a4 100644
--- a/Swift/QtUI/ChatList/QtChatListWindow.h
+++ b/Swift/QtUI/ChatList/QtChatListWindow.h
@@ -36,8 +36,10 @@ namespace Swift {
 			void handleAddBookmark();
 			void handleEditBookmark();
 			void handleRemoveBookmark();
+			void handleAddBookmarkFromRecents();
 			void handleClicked(const QModelIndex& index);
 			void handleSettingChanged(const std::string& setting);
+			void handleClearRecentsRequested();
 
 		protected:
 			void dragEnterEvent(QDragEnterEvent* event);
@@ -51,6 +53,7 @@ namespace Swift {
 			ChatListDelegate* delegate_;
 			QMenu* mucMenu_;
 			QMenu* emptyMenu_;
+			QMenu* mucRecentsMenu_;
 			ChatListItem* contextMenuItem_;
 			SettingsProvider* settings_;
 	};
-- 
cgit v0.10.2-6-g49f6