From fe477c0da80c2e5799a1841ce7fcf4e023ad57bb Mon Sep 17 00:00:00 2001
From: dknn <yoann.blein@free.fr>
Date: Sat, 24 Mar 2012 16:11:14 +0100
Subject: 'Add contact' from MUC right-click menu

This patch allows to add a contact from a MUC, by right clicking a contact in
the rooster. The action is only available if the JID of the user is also
available

License: This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details.

diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index b8aa35c..fd37d27 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -22,6 +22,7 @@
 #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
 #include <Swift/Controllers/UIEvents/UIEventStream.h>
 #include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
 #include <Swift/Controllers/Roster/GroupRosterItem.h>
 #include <Swift/Controllers/Roster/ContactRosterItem.h>
 #include <Swiften/Avatars/AvatarManager.h>
@@ -121,6 +122,11 @@ void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item
 		actions.push_back(ChatWindow::MakeModerator);
 		actions.push_back(ChatWindow::MakeParticipant);
 		actions.push_back(ChatWindow::MakeVisitor);
+
+		// Add contact is available only if the real JID is also available
+		if (muc_->getOccupant(item->getJID().getResource()).getRealJID()) {
+			actions.push_back(ChatWindow::AddContact);
+		}
 	}
 	chatWindow_->setAvailableOccupantActions(actions);
 }
@@ -138,6 +144,7 @@ void MUCController::handleActionRequestedOnOccupant(ChatWindow::OccupantAction a
 		case ChatWindow::MakeModerator: muc_->changeOccupantRole(mucJID, MUCOccupant::Moderator);break;
 		case ChatWindow::MakeParticipant: muc_->changeOccupantRole(mucJID, MUCOccupant::Participant);break;
 		case ChatWindow::MakeVisitor: muc_->changeOccupantRole(mucJID, MUCOccupant::Visitor);break;
+		case ChatWindow::AddContact: if (occupant.getRealJID()) events_->send(boost::make_shared<RequestAddUserDialogUIEvent>(realJID, occupant.getNick()));break;
 	}
 }
 
diff --git a/Swift/Controllers/Chat/UserSearchController.cpp b/Swift/Controllers/Chat/UserSearchController.cpp
index af962e9..47d57d4 100644
--- a/Swift/Controllers/Chat/UserSearchController.cpp
+++ b/Swift/Controllers/Chat/UserSearchController.cpp
@@ -46,8 +46,9 @@ UserSearchController::~UserSearchController() {
 
 void UserSearchController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
 	bool handle = false;
+	boost::shared_ptr<RequestAddUserDialogUIEvent> request = boost::shared_ptr<RequestAddUserDialogUIEvent>();
 	if (type_ == AddContact) {
-		if (boost::dynamic_pointer_cast<RequestAddUserDialogUIEvent>(event)) {
+		if (request = boost::dynamic_pointer_cast<RequestAddUserDialogUIEvent>(event)) {
 			handle = true;
 		}
 	} else {
@@ -65,6 +66,13 @@ void UserSearchController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
 			window_->clear();
 		}
 		window_->show();
+		if (request) {
+			const std::string& name = request->getPredefinedName();
+			const JID& jid = request->getPredefinedJID();
+			if (!name.empty() && jid.isValid()) {
+				window_->prepopulateJIDAndName(jid, name);
+			}
+		}
 		return;
 	}
 }
diff --git a/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h b/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h
index bfa4a8b..26f48cb 100644
--- a/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h
+++ b/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h
@@ -7,9 +7,23 @@
 #pragma once
 
 #include "Swift/Controllers/UIEvents/UIEvent.h"
+#include <string>
+#include <Swiften/JID/JID.h>
+
 
 namespace Swift {
 	class RequestAddUserDialogUIEvent : public UIEvent {
 
+		public:
+			RequestAddUserDialogUIEvent(const JID& predefinedJID, const std::string& predefinedName) : preJID_(predefinedJID), preName_(predefinedName) {};
+			RequestAddUserDialogUIEvent() : preJID_(JID()), preName_(std::string()) {};
+
+			const JID& getPredefinedJID() const { return preJID_; };
+			const std::string& getPredefinedName() const { return preName_; };
+
+		private:
+			const JID& preJID_;
+			const std::string& preName_;
+
 	};
 }
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index fe7b6bf..acf2381 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -34,7 +34,7 @@ namespace Swift {
 			enum AckState {Pending, Received, Failed};
 			enum ReceiptState {ReceiptRequested, ReceiptReceived};
 			enum Tristate {Yes, No, Maybe};
-			enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor};
+			enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor, AddContact};
 			enum FileTransferState {WaitingForAccept, Negotiating, Transferring, Canceled, Finished, FTFailed};
 			ChatWindow() {}
 			virtual ~ChatWindow() {};
diff --git a/Swift/Controllers/UIInterfaces/UserSearchWindow.h b/Swift/Controllers/UIInterfaces/UserSearchWindow.h
index 7ea6849..a3d69d6 100644
--- a/Swift/Controllers/UIInterfaces/UserSearchWindow.h
+++ b/Swift/Controllers/UIInterfaces/UserSearchWindow.h
@@ -30,6 +30,7 @@ namespace Swift {
 			virtual void setSearchError(bool support) = 0;
 			virtual void setSearchFields(boost::shared_ptr<SearchPayload> fields) = 0;
 			virtual void setNameSuggestions(const std::vector<std::string>& suggestions) = 0;
+			virtual void prepopulateJIDAndName(const JID& jid, const std::string& name) = 0;
 			virtual void show() = 0;
 
 			boost::signal<void (const JID&)> onFormRequested;
diff --git a/Swift/QtUI/Roster/QtOccupantListWidget.cpp b/Swift/QtUI/Roster/QtOccupantListWidget.cpp
index 0b3722c..1469300 100644
--- a/Swift/QtUI/Roster/QtOccupantListWidget.cpp
+++ b/Swift/QtUI/Roster/QtOccupantListWidget.cpp
@@ -57,6 +57,7 @@ void QtOccupantListWidget::contextMenuEvent(QContextMenuEvent* event) {
 					case ChatWindow::MakeModerator: text = tr("Make moderator"); break;
 					case ChatWindow::MakeParticipant: text = tr("Make participant"); break;
 					case ChatWindow::MakeVisitor: text = tr("Remove voice"); break;
+					case ChatWindow::AddContact: text = tr("Add contact"); break;
 				}
 				QAction* action = contextMenu.addAction(text);
 				actions[action] = availableAction;
diff --git a/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.cpp b/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.cpp
index da89a8d..f4189e7 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.cpp
@@ -36,6 +36,10 @@ void QtUserSearchDetailsPage::setNameSuggestions(const std::vector<std::string>&
 	editWidget->setNameSuggestions(nameSuggestions);
 }
 
+void QtUserSearchDetailsPage::setName(const std::string& name) {
+	editWidget->setName(name);
+}
+
 std::set<std::string> QtUserSearchDetailsPage::getSelectedGroups() {
 	return editWidget->getSelectedGroups();
 }
diff --git a/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h b/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h
index 9409493..63fd241 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h
@@ -27,6 +27,7 @@ namespace Swift {
 
 			void setJID(const JID& jid);
 			void setNameSuggestions(const std::vector<std::string>& nameSuggestions);
+			void setName(const std::string& name);
 
 			std::set<std::string> getSelectedGroups();
 			std::string getName();
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
index 949d65c..b8a44e5 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
@@ -236,6 +236,15 @@ void QtUserSearchWindow::setNameSuggestions(const std::vector<std::string>& sugg
 	}
 }
 
+void QtUserSearchWindow::prepopulateJIDAndName(const JID& jid, const std::string& name) {
+	firstPage_->jid_->setText(P2QSTRING(jid.toBare().toString()));
+	detailsPage_->setJID(jid);
+	lastPage_ = 1;
+	restart();
+	next();
+	detailsPage_->setName(name);
+}
+
 void QtUserSearchWindow::setResults(const std::vector<UserSearchResult>& results) {
 	UserSearchModel *newModel = new UserSearchModel();
 	newModel->setResults(results);
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.h b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
index 9173ba9..32e851a 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
@@ -40,6 +40,7 @@ namespace Swift {
 			virtual void setSearchError(bool error);
 			virtual void setSearchFields(boost::shared_ptr<SearchPayload> fields);
 			virtual void setNameSuggestions(const std::vector<std::string>& suggestions);
+			virtual void prepopulateJIDAndName(const JID& jid, const std::string& name);
 
 		protected:
 			virtual int nextId() const;
-- 
cgit v0.10.2-6-g49f6