From 3d27d98ccc232ae7bfacfd5a3f85f44b6c2e9cc9 Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Wed, 14 Mar 2012 22:36:10 +0100
Subject: Naming suggestion for 'Add Contact'-dialog and "Edit Contact"-dialog
 based on vCards.

License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php

diff --git a/Swift/Controllers/Chat/UserSearchController.cpp b/Swift/Controllers/Chat/UserSearchController.cpp
index d90a3f8..d6a920d 100644
--- a/Swift/Controllers/Chat/UserSearchController.cpp
+++ b/Swift/Controllers/Chat/UserSearchController.cpp
@@ -13,6 +13,8 @@
 #include <Swiften/Disco/GetDiscoInfoRequest.h>
 #include <Swiften/Disco/GetDiscoItemsRequest.h>
 #include <Swiften/Disco/DiscoServiceWalker.h>
+#include <Swiften/VCards/VCardManager.h>
+#include <Swift/Controllers/ContactEditController.h>
 #include <Swift/Controllers/UIEvents/UIEventStream.h>
 #include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h>
 #include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
@@ -21,8 +23,9 @@
 #include <Swift/Controllers/Roster/RosterController.h>
 
 namespace Swift {
-UserSearchController::UserSearchController(Type type, const JID& jid, UIEventStream* uiEventStream, UserSearchWindowFactory* factory, IQRouter* iqRouter, RosterController* rosterController) : type_(type), jid_(jid), uiEventStream_(uiEventStream), factory_(factory), iqRouter_(iqRouter), rosterController_(rosterController) {
+UserSearchController::UserSearchController(Type type, const JID& jid, UIEventStream* uiEventStream, VCardManager* vcardManager, UserSearchWindowFactory* factory, IQRouter* iqRouter, RosterController* rosterController) : type_(type), jid_(jid), uiEventStream_(uiEventStream), vcardManager_(vcardManager), factory_(factory), iqRouter_(iqRouter), rosterController_(rosterController) {
 	uiEventStream_->onUIEvent.connect(boost::bind(&UserSearchController::handleUIEvent, this, _1));
+	vcardManager_->onVCardChanged.connect(boost::bind(&UserSearchController::handleVCardChanged, this, _1, _2));
 	window_ = NULL;
 	discoWalker_ = NULL;
 }
@@ -31,10 +34,12 @@ UserSearchController::~UserSearchController() {
 	endDiscoWalker();
 	delete discoWalker_;
 	if (window_) {
+		window_->onNameSuggestionRequested.disconnect(boost::bind(&UserSearchController::handleNameSuggestionRequest, this, _1));
 		window_->onFormRequested.disconnect(boost::bind(&UserSearchController::handleFormRequested, this, _1));
 		window_->onSearchRequested.disconnect(boost::bind(&UserSearchController::handleSearch, this, _1, _2));
 		delete window_;
 	}
+	vcardManager_->onVCardChanged.disconnect(boost::bind(&UserSearchController::handleVCardChanged, this, _1, _2));
 	uiEventStream_->onUIEvent.disconnect(boost::bind(&UserSearchController::handleUIEvent, this, _1));
 }
 
@@ -52,6 +57,7 @@ void UserSearchController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
 	if (handle) {
 		if (!window_) {
 			window_ = factory_->createUserSearchWindow(type_ == AddContact ? UserSearchWindow::AddContact : UserSearchWindow::ChatToContact, uiEventStream_, rosterController_->getGroups());
+			window_->onNameSuggestionRequested.connect(boost::bind(&UserSearchController::handleNameSuggestionRequest, this, _1));
 			window_->onFormRequested.connect(boost::bind(&UserSearchController::handleFormRequested, this, _1));
 			window_->onSearchRequested.connect(boost::bind(&UserSearchController::handleSearch, this, _1, _2));
 			window_->setSelectedService(JID(jid_.getDomain()));
@@ -143,6 +149,21 @@ void UserSearchController::handleSearchResponse(boost::shared_ptr<SearchPayload>
 	}
 }
 
+void UserSearchController::handleNameSuggestionRequest(const JID &jid) {
+	suggestionsJID_= jid;
+	VCard::ref vcard = vcardManager_->getVCardAndRequestWhenNeeded(jid);
+	if (vcard) {
+		handleVCardChanged(jid, vcard);
+	}
+}
+
+void UserSearchController::handleVCardChanged(const JID& jid, VCard::ref vcard) {
+	if (jid == suggestionsJID_) {
+		window_->setNameSuggestions(ContactEditController::nameSuggestionsFromVCard(vcard));
+		suggestionsJID_ = JID();
+	}
+}
+
 void UserSearchController::handleDiscoWalkFinished() {
 	window_->setServerSupportsSearch(false);
 	endDiscoWalker();
diff --git a/Swift/Controllers/Chat/UserSearchController.h b/Swift/Controllers/Chat/UserSearchController.h
index 071ec33..ce0754c 100644
--- a/Swift/Controllers/Chat/UserSearchController.h
+++ b/Swift/Controllers/Chat/UserSearchController.h
@@ -17,6 +17,7 @@
 #include <Swiften/Elements/DiscoInfo.h>
 #include <Swiften/Elements/DiscoItems.h>
 #include <Swiften/Elements/ErrorPayload.h>
+#include <Swiften/Elements/VCard.h>
 
 namespace Swift {
 	class UIEventStream;
@@ -26,6 +27,7 @@ namespace Swift {
 	class IQRouter;
 	class DiscoServiceWalker;
 	class RosterController;
+	class VCardManager;
 
 	class UserSearchResult {
 		public:
@@ -40,7 +42,7 @@ namespace Swift {
 	class UserSearchController {
 		public:
 			enum Type {AddContact, StartChat};
-			UserSearchController(Type type, const JID& jid, UIEventStream* uiEventStream, UserSearchWindowFactory* userSearchWindowFactory, IQRouter* iqRouter, RosterController* rosterController);
+			UserSearchController(Type type, const JID& jid, UIEventStream* uiEventStream, VCardManager* vcardManager, UserSearchWindowFactory* userSearchWindowFactory, IQRouter* iqRouter, RosterController* rosterController);
 			~UserSearchController();
 
 		private:
@@ -51,12 +53,16 @@ namespace Swift {
 			void handleFormResponse(boost::shared_ptr<SearchPayload> items, ErrorPayload::ref error);
 			void handleSearch(boost::shared_ptr<SearchPayload> fields, const JID& jid);
 			void handleSearchResponse(boost::shared_ptr<SearchPayload> results, ErrorPayload::ref error);
+			void handleNameSuggestionRequest(const JID& jid);
+			void handleVCardChanged(const JID& jid, VCard::ref vcard);
 			void endDiscoWalker();
 
 		private:
 			Type type_;
 			JID jid_;
+			JID suggestionsJID_;
 			UIEventStream* uiEventStream_;
+			VCardManager* vcardManager_;
 			UserSearchWindowFactory* factory_;
 			IQRouter* iqRouter_;
 			RosterController* rosterController_;
diff --git a/Swift/Controllers/ContactEditController.cpp b/Swift/Controllers/ContactEditController.cpp
index b4729a8..a8b171c 100644
--- a/Swift/Controllers/ContactEditController.cpp
+++ b/Swift/Controllers/ContactEditController.cpp
@@ -6,6 +6,7 @@
 
 #include <Swift/Controllers/ContactEditController.h>
 
+#include <boost/algorithm/string.hpp>
 #include <boost/bind.hpp>
 #include <boost/smart_ptr/make_shared.hpp>
 
@@ -14,11 +15,12 @@
 #include <Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h>
 #include <Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h>
 #include <Swift/Controllers/Roster/RosterController.h>
+#include <Swiften/VCards/VCardManager.h>
 
 
 namespace Swift {
 
-ContactEditController::ContactEditController(RosterController* rosterController, ContactEditWindowFactory* contactEditWindowFactory, UIEventStream* uiEventStream) : rosterController(rosterController), contactEditWindowFactory(contactEditWindowFactory), uiEventStream(uiEventStream), contactEditWindow(NULL) {
+ContactEditController::ContactEditController(RosterController* rosterController, VCardManager* vcardManager, ContactEditWindowFactory* contactEditWindowFactory, UIEventStream* uiEventStream) : rosterController(rosterController), vcardManager(vcardManager), contactEditWindowFactory(contactEditWindowFactory), uiEventStream(uiEventStream), contactEditWindow(NULL) {
 	uiEventStream->onUIEvent.connect(boost::bind(&ContactEditController::handleUIEvent, this, _1));
 }
 
@@ -44,8 +46,22 @@ void ContactEditController::handleUIEvent(UIEvent::ref event) {
 	}
 	currentContact = rosterController->getItem(editEvent->getJID());
 	assert(currentContact);
-	contactEditWindow->setContact(currentContact->getJID(), currentContact->getName(), currentContact->getGroups(), rosterController->getGroups());
+	jid = rosterController->getItem(editEvent->getJID())->getJID();
+	contactEditWindow->setContact(jid, currentContact->getName(), currentContact->getGroups(), rosterController->getGroups());
 	contactEditWindow->show();
+
+	if (vcardManager) {
+		VCard::ref vcard = vcardManager->getVCardAndRequestWhenNeeded(jid);
+		if (vcard) {
+			handleVCardChanged(jid, vcard);
+		}
+	}
+}
+
+void ContactEditController::handleVCardChanged(const JID &jid, VCard::ref vcard) {
+	if (jid == this->jid) {
+		contactEditWindow->setNameSuggestions(nameSuggestionsFromVCard(vcard));
+	}
 }
 
 void ContactEditController::setAvailable(bool b) {
@@ -54,6 +70,23 @@ void ContactEditController::setAvailable(bool b) {
 	}
 }
 
+std::vector<std::string> ContactEditController::nameSuggestionsFromVCard(VCard::ref vcard) {
+	std::vector<std::string> suggestions;
+	if (!vcard->getNickname().empty()) {
+		suggestions.push_back(vcard->getNickname());
+	}
+	if (!vcard->getFullName().empty()) {
+		suggestions.push_back(vcard->getFullName());
+	}
+	if (!vcard->getGivenName().empty()) {
+		std::string suggestedName;
+		suggestedName = vcard->getGivenName();
+		boost::algorithm::trim(suggestedName);
+		suggestions.push_back(suggestedName);
+	}
+	return suggestions;
+}
+
 void ContactEditController::handleRemoveContactRequest() {
 	assert(currentContact);
 	uiEventStream->send(boost::make_shared<RemoveRosterItemUIEvent>(currentContact->getJID()));
diff --git a/Swift/Controllers/ContactEditController.h b/Swift/Controllers/ContactEditController.h
index 1947944..5f64a6a 100644
--- a/Swift/Controllers/ContactEditController.h
+++ b/Swift/Controllers/ContactEditController.h
@@ -12,6 +12,7 @@
 
 #include <Swiften/JID/JID.h>
 #include <string>
+#include <Swiften/Elements/VCard.h>
 #include <Swift/Controllers/UIEvents/UIEvent.h>
 #include <Swiften/Roster/XMPPRosterItem.h>
 
@@ -20,26 +21,33 @@ namespace Swift {
 	class ContactEditWindowFactory;
 	class ContactEditWindow;
 	class RosterController;
+	class VCardManager;
 
 	class ContactEditController {
 		public:
-			ContactEditController(RosterController* rosterController, ContactEditWindowFactory* contactEditWindowFactory, UIEventStream* uiEventStream);
+			ContactEditController(RosterController* rosterController, VCardManager* vcardManager, ContactEditWindowFactory* contactEditWindowFactory, UIEventStream* uiEventStream);
 			~ContactEditController();
 
 			void setAvailable(bool b);
 
+		public:
+			static std::vector<std::string> nameSuggestionsFromVCard(VCard::ref vcard);
+
 		private:
 			void handleRemoveContactRequest();
 			void handleChangeContactRequest(const std::string& name, const std::set<std::string>& groups);
 
 		private:
 			void handleUIEvent(UIEvent::ref event);
+			void handleVCardChanged(const JID& jid, VCard::ref vcard);
 
 		private:
 			boost::optional<XMPPRosterItem> currentContact;
 			RosterController* rosterController;
+			VCardManager* vcardManager;
 			ContactEditWindowFactory* contactEditWindowFactory;
 			UIEventStream* uiEventStream;
+			JID jid;
 			ContactEditWindow* contactEditWindow;
 	};
 }
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index b154e93..cd7e673 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -286,7 +286,7 @@ void MainController::handleConnected() {
 		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
 		rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
 
-		contactEditController_ = new ContactEditController(rosterController_, uiFactory_, uiEventStream_);
+		contactEditController_ = new ContactEditController(rosterController_, client_->getVCardManager(), uiFactory_, uiEventStream_);
 
 		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_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_);
 		
@@ -312,8 +312,8 @@ void MainController::handleConnected() {
 		client_->getDiscoManager()->setCapsNode(CLIENT_NODE);
 		client_->getDiscoManager()->setDiscoInfo(discoInfo);
 
-		userSearchControllerChat_ = new UserSearchController(UserSearchController::StartChat, jid_, uiEventStream_, uiFactory_, client_->getIQRouter(), rosterController_);
-		userSearchControllerAdd_ = new UserSearchController(UserSearchController::AddContact, jid_, uiEventStream_, uiFactory_, client_->getIQRouter(), rosterController_);
+		userSearchControllerChat_ = new UserSearchController(UserSearchController::StartChat, jid_, uiEventStream_, client_->getVCardManager(), uiFactory_, client_->getIQRouter(), rosterController_);
+		userSearchControllerAdd_ = new UserSearchController(UserSearchController::AddContact, jid_, uiEventStream_, client_->getVCardManager(), uiFactory_, client_->getIQRouter(), rosterController_);
 		adHocManager_ = new AdHocManager(JID(boundJID_.getDomain()), uiFactory_, client_->getIQRouter(), uiEventStream_, rosterController_->getWindow());
 	}
 	loginWindow_->setIsLoggingIn(false);
diff --git a/Swift/Controllers/UIInterfaces/ContactEditWindow.h b/Swift/Controllers/UIInterfaces/ContactEditWindow.h
index 2445456..90f774c 100644
--- a/Swift/Controllers/UIInterfaces/ContactEditWindow.h
+++ b/Swift/Controllers/UIInterfaces/ContactEditWindow.h
@@ -15,13 +15,15 @@
 
 namespace Swift {
 	class JID;
+	class VCardManager;
 
 	class ContactEditWindow {
 		public:
-			virtual ~ContactEditWindow() {};
+			virtual ~ContactEditWindow() {}
 
 			virtual void setEnabled(bool b) = 0;
 
+			virtual void setNameSuggestions(const std::vector<std::string>& suggestions) = 0;
 			virtual void setContact(const JID& jid, const std::string& name, const std::vector<std::string>& groups, const std::set<std::string>& allGroups) = 0;
 
 			virtual void show() = 0;
diff --git a/Swift/Controllers/UIInterfaces/UserSearchWindow.h b/Swift/Controllers/UIInterfaces/UserSearchWindow.h
index 0bfc509..7ea6849 100644
--- a/Swift/Controllers/UIInterfaces/UserSearchWindow.h
+++ b/Swift/Controllers/UIInterfaces/UserSearchWindow.h
@@ -9,8 +9,8 @@
 #include "Swiften/Base/boost_bsignals.h"
 
 #include <vector>
-
 #include <string>
+
 #include "Swiften/JID/JID.h"
 #include "Swift/Controllers/Chat/UserSearchController.h"
 
@@ -19,7 +19,7 @@ namespace Swift {
 	class UserSearchWindow {
 		public:
 			enum Type {AddContact, ChatToContact};
-			virtual ~UserSearchWindow() {};
+			virtual ~UserSearchWindow() {}
 
 			virtual void clear() = 0;
 			virtual void setResults(const std::vector<UserSearchResult>& results) = 0;
@@ -29,9 +29,11 @@ namespace Swift {
 			virtual void setServerSupportsSearch(bool support) = 0;
 			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 show() = 0;
 
 			boost::signal<void (const JID&)> onFormRequested;
 			boost::signal<void (boost::shared_ptr<SearchPayload>, const JID&)> onSearchRequested;
+			boost::signal<void (const JID&)> onNameSuggestionRequested;
 	};
 }
diff --git a/Swift/QtUI/QtContactEditWidget.cpp b/Swift/QtUI/QtContactEditWidget.cpp
index a5134e4..a80eb38 100644
--- a/Swift/QtUI/QtContactEditWidget.cpp
+++ b/Swift/QtUI/QtContactEditWidget.cpp
@@ -8,28 +8,36 @@
 
 #include <algorithm>
 
-#include <QScrollArea>
 #include <QBoxLayout>
-#include <QLabel>
 #include <QCheckBox>
+#include <QLabel>
 #include <QLineEdit>
+#include <QMovie>
+#include <QScrollArea>
+#include <QRadioButton>
 
 #include "Swift/QtUI/QtSwiftUtil.h"
 
 namespace Swift {
 
-QtContactEditWidget::QtContactEditWidget(const std::set<std::string>& allGroups, QWidget* parent) : QWidget(parent), groups_(NULL) {
+QtContactEditWidget::QtContactEditWidget(const std::set<std::string>& allGroups, QWidget* parent) : QWidget(parent), nameRadioButton_(NULL), groups_(NULL) {
 	QBoxLayout* layout = new QVBoxLayout(this);
 	setContentsMargins(0,0,0,0);
 	layout->setContentsMargins(0,0,0,0);
 
-	QHBoxLayout* nameLayout = new QHBoxLayout();
-	
-	QLabel* label = new QLabel(tr("Name:"), this);
-	nameLayout->addWidget(label);
+	nameLayout_ = new QHBoxLayout();
+	suggestionsLayout_ = new QHBoxLayout();
+	nameLayout_->addLayout(suggestionsLayout_);
+
 	name_ = new QLineEdit(this);
-	nameLayout->addWidget(name_);
-	layout->addLayout(nameLayout);
+	nameLayout_->addWidget(name_);
+
+	throbberLabel_ = new QLabel(this);
+	throbberLabel_->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this));
+	throbberLabel_->movie()->start();
+	nameLayout_->addWidget(throbberLabel_);
+
+	layout->addLayout(nameLayout_);
 
 	layout->addWidget(new QLabel(tr("Groups:"), this));
 
@@ -68,7 +76,19 @@ void QtContactEditWidget::setName(const std::string& name) {
 }
 
 std::string QtContactEditWidget::getName() const {
-	return Q2PSTRING(name_->text());
+	std::string name;
+	QList<QRadioButton*> buttons = findChildren<QRadioButton*>();
+	foreach(const QRadioButton* button, buttons) {
+		if (button->isChecked()) {
+			if (button == nameRadioButton_) {
+				name = Q2PSTRING(name_->text());
+			} else {
+				name = Q2PSTRING(button->text());
+			}
+			break;
+		}
+	}
+	return name;
 }
 
 void QtContactEditWidget::setSelectedGroups(const std::vector<std::string>& groups) {
@@ -90,11 +110,42 @@ std::set<std::string> QtContactEditWidget::getSelectedGroups() const {
 	return groups;
 }
 
+void QtContactEditWidget::setNameSuggestions(const std::vector<std::string>& suggestions) {
+	throbberLabel_->movie()->stop();
+	throbberLabel_->hide();
+
+	foreach(const std::string& name, suggestions) {
+		suggestionsLayout_->insertWidget(nameLayout_->count() - 2, new QRadioButton(P2QSTRING(name), this));
+	}
+
+	nameRadioButton_ = new QRadioButton(tr("Name:"), this);
+	suggestionsLayout_->insertWidget(nameLayout_->count(), nameRadioButton_);
+
+	if (name_->text().isEmpty()) {
+		QRadioButton* suggestedRadioButton = dynamic_cast<QRadioButton*>(suggestionsLayout_->itemAt(0)->widget());
+		if (suggestedRadioButton) {
+			suggestedRadioButton->setChecked(true);
+		}
+	} else {
+		nameRadioButton_->setChecked(true);
+	}
+}
+
 void QtContactEditWidget::clear() {
 	name_->clear();
 	setSelectedGroups(std::vector<std::string>());
 	newGroup_->setChecked(false);
 	newGroupName_->clear();
+	throbberLabel_->movie()->start();
+	throbberLabel_->show();
+
+	// clear suggestions
+	while(suggestionsLayout_->count() != 0) {
+		QLayoutItem *layoutItem = suggestionsLayout_->takeAt(0);
+		delete layoutItem->widget();
+		delete layoutItem;
+	}
+	nameRadioButton_ = NULL;
 }
 
 }
diff --git a/Swift/QtUI/QtContactEditWidget.h b/Swift/QtUI/QtContactEditWidget.h
index 8ff267d..5350762 100644
--- a/Swift/QtUI/QtContactEditWidget.h
+++ b/Swift/QtUI/QtContactEditWidget.h
@@ -12,10 +12,14 @@
 
 #include <QWidget>
 
+#include <vector>
 #include <string>
 
+class QLabel;
 class QLineEdit;
 class QCheckBox;
+class QHBoxLayout;
+class QRadioButton;
 
 namespace Swift {
 	class QtContactEditWidget : public QWidget {
@@ -30,15 +34,21 @@ namespace Swift {
 			void setSelectedGroups(const std::vector<std::string>& groups);
 			std::set<std::string> getSelectedGroups() const;
 
+			void setNameSuggestions(const std::vector<std::string>& suggestions);
+
 			void clear();
 
 		private:
 			typedef std::map<std::string, QCheckBox*> CheckBoxMap;
 			CheckBoxMap checkBoxes_;
+			QHBoxLayout* nameLayout_;
+			QHBoxLayout* suggestionsLayout_;
+			QRadioButton* nameRadioButton_;
 			QLineEdit* name_;
 			QWidget* groups_;
 			QCheckBox* newGroup_;
 			QLineEdit* newGroupName_;
+			QLabel* throbberLabel_;
 	};
 }
 
diff --git a/Swift/QtUI/QtContactEditWindow.cpp b/Swift/QtUI/QtContactEditWindow.cpp
index 9b24b23..6520c0a 100644
--- a/Swift/QtUI/QtContactEditWindow.cpp
+++ b/Swift/QtUI/QtContactEditWindow.cpp
@@ -8,6 +8,8 @@
 
 #include <algorithm>
 
+#include <boost/bind.hpp>
+
 #include <QScrollArea>
 #include <QBoxLayout>
 #include <QLabel>
@@ -22,7 +24,7 @@
 namespace Swift {
 
 QtContactEditWindow::QtContactEditWindow() : contactEditWidget_(NULL) {
-	resize(300,300);
+	resize(400,300);
 	setWindowTitle(tr("Edit contact"));
 	setContentsMargins(0,0,0,0);
 
@@ -48,6 +50,15 @@ QtContactEditWindow::QtContactEditWindow() : contactEditWidget_(NULL) {
 	buttonLayout->addWidget(okButton);
 }
 
+QtContactEditWindow::~QtContactEditWindow() {
+}
+
+void QtContactEditWindow::setNameSuggestions(const std::vector<std::string>& nameSuggestions) {
+	if (contactEditWidget_) {
+		contactEditWidget_->setNameSuggestions(nameSuggestions);
+	}
+}
+
 void QtContactEditWindow::setContact(const JID& jid, const std::string& name, const std::vector<std::string>& groups, const std::set<std::string>& allGroups) {
 	delete contactEditWidget_;
 	jid_ = jid;
diff --git a/Swift/QtUI/QtContactEditWindow.h b/Swift/QtUI/QtContactEditWindow.h
index b25e8b9..5392b10 100644
--- a/Swift/QtUI/QtContactEditWindow.h
+++ b/Swift/QtUI/QtContactEditWindow.h
@@ -11,6 +11,7 @@
 #include <Swift/Controllers/UIInterfaces/ContactEditWindow.h>
 #include <string>
 #include <Swiften/JID/JID.h>
+#include <Swiften/Elements/VCard.h>
 
 class QLabel;
 class QVBoxLayout;
@@ -23,7 +24,9 @@ namespace Swift {
 
 		public:
 			QtContactEditWindow();
+			virtual ~QtContactEditWindow();
 
+			virtual void setNameSuggestions(const std::vector<std::string>& nameSuggestions);
 			virtual void setContact(const JID& jid, const std::string& name, const std::vector<std::string>& groups, const std::set<std::string>& allGroups);
 
 			void setEnabled(bool);
diff --git a/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.cpp b/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.cpp
index 779e547..da89a8d 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.cpp
@@ -8,6 +8,11 @@
 
 #include <QVBoxLayout>
 
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+
 #include <Swift/QtUI/QtContactEditWidget.h>
 
 namespace Swift {
@@ -19,6 +24,18 @@ QtUserSearchDetailsPage::QtUserSearchDetailsPage(const std::set<std::string>& gr
 	layout->addWidget(editWidget);
 }
 
+QtUserSearchDetailsPage::~QtUserSearchDetailsPage() {
+
+}
+
+void QtUserSearchDetailsPage::setJID(const JID& jid) {
+	contactJID = jid;
+}
+
+void QtUserSearchDetailsPage::setNameSuggestions(const std::vector<std::string>& nameSuggestions) {
+	editWidget->setNameSuggestions(nameSuggestions);
+}
+
 std::set<std::string> QtUserSearchDetailsPage::getSelectedGroups() {
 	return editWidget->getSelectedGroups();
 }
@@ -31,5 +48,4 @@ void QtUserSearchDetailsPage::clear() {
 	editWidget->clear();
 }
 
-
 }
diff --git a/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h b/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h
index 70afcba..9409493 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h
@@ -7,8 +7,13 @@
 #pragma once
 
 #include <QWizardPage>
+
 #include <set>
+#include <string>
+#include <vector>
 
+#include <Swiften/Elements/VCard.h>
+#include <Swiften/JID/JID.h>
 #include <Swift/QtUI/UserSearch/ui_QtUserSearchFieldsPage.h>
 
 namespace Swift {
@@ -18,6 +23,10 @@ namespace Swift {
 			Q_OBJECT
 		public:
 			QtUserSearchDetailsPage(const std::set<std::string>& availableGroups);
+			virtual ~QtUserSearchDetailsPage();
+
+			void setJID(const JID& jid);
+			void setNameSuggestions(const std::vector<std::string>& nameSuggestions);
 
 			std::set<std::string> getSelectedGroups();
 			std::string getName();
@@ -25,9 +34,10 @@ namespace Swift {
 			void clear();
 
 		signals:
-		  void onUserTriggersFinish();
+			void onUserTriggersFinish();
 
 		private:
-		  QtContactEditWidget* editWidget;
+			QtContactEditWidget* editWidget;
+			JID contactJID;
 	};
 }
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
index 933612c..53eac07 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
@@ -90,6 +90,8 @@ void QtUserSearchWindow::handleCurrentChanged(int page) {
 	}
 	else if (page == 4) {
 		detailsPage_->clear();
+		detailsPage_->setJID(getContactJID());
+		onNameSuggestionRequested(getContactJID());
 	}
 	lastPage_ = page;
 }
@@ -99,35 +101,7 @@ JID QtUserSearchWindow::getServerToSearch() {
 }
 
 void QtUserSearchWindow::handleAccepted() {
-	JID jid;
-	if (!firstPage_->byJID_->isChecked()) {
-		if (dynamic_cast<UserSearchModel*>(model_)) {
-			UserSearchResult* userItem = static_cast<UserSearchResult*>(resultsPage_->results_->currentIndex().internalPointer());
-			if (userItem) { /* Remember to leave this if we change to dynamic cast */
-				jid = userItem->getJID();
-			}
-		} else {
-			int row = resultsPage_->results_->currentIndex().row();
-
-			Form::FormItem item = dynamic_cast<QtFormResultItemModel*>(model_)->getForm()->getItems().at(row);
-			JID fallbackJid;
-			foreach(FormField::ref field, item) {
-				if (boost::dynamic_pointer_cast<JIDSingleFormField>(field)) {
-					jid = JID(field->getRawValues().at(0));
-					break;
-				}
-				if (field->getName() == "jid") {
-					fallbackJid = field->getRawValues().at(0);
-				}
-			}
-			if (!jid.isValid()) {
-				jid = fallbackJid;
-			}
-		}
-	}
-	else {
-		jid = JID(Q2PSTRING(firstPage_->jid_->text()));
-	}
+	JID jid = getContactJID();
 
 	if (type_ == AddContact) {
 		eventStream_->send(boost::make_shared<AddContactUIEvent>(jid, detailsPage_->getName(), detailsPage_->getSelectedGroups()));
@@ -190,6 +164,39 @@ void QtUserSearchWindow::handleSearch() {
 	onSearchRequested(search, getServerToSearch());
 }
 
+JID QtUserSearchWindow::getContactJID() const {
+	JID jid;
+	if (!firstPage_->byJID_->isChecked()) {
+		if (dynamic_cast<UserSearchModel*>(model_)) {
+			UserSearchResult* userItem = static_cast<UserSearchResult*>(resultsPage_->results_->currentIndex().internalPointer());
+			if (userItem) { /* Remember to leave this if we change to dynamic cast */
+				jid = userItem->getJID();
+			}
+		} else {
+			int row = resultsPage_->results_->currentIndex().row();
+
+			Form::FormItem item = dynamic_cast<QtFormResultItemModel*>(model_)->getForm()->getItems().at(row);
+			JID fallbackJid;
+			foreach(FormField::ref field, item) {
+				if (boost::dynamic_pointer_cast<JIDSingleFormField>(field)) {
+					jid = JID(field->getRawValues().at(0));
+					break;
+				}
+				if (field->getName() == "jid") {
+					fallbackJid = field->getRawValues().at(0);
+				}
+			}
+			if (!jid.isValid()) {
+				jid = fallbackJid;
+			}
+		}
+	}
+	else {
+		jid = JID(Q2PSTRING(firstPage_->jid_->text()));
+	}
+	return jid;
+}
+
 void QtUserSearchWindow::show() {
 	clear();
 	QWidget::show();
@@ -223,6 +230,12 @@ void QtUserSearchWindow::setSearchFields(boost::shared_ptr<SearchPayload> fields
 	fieldsPage_->emitCompletenessCheck();
 }
 
+void QtUserSearchWindow::setNameSuggestions(const std::vector<std::string>& suggestions) {
+	if (detailsPage_) {
+		detailsPage_->setNameSuggestions(suggestions);
+	}
+}
+
 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 4cfe93f..9173ba9 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
@@ -38,7 +38,9 @@ namespace Swift {
 			virtual void setSelectedService(const JID& jid);
 			virtual void setServerSupportsSearch(bool error);
 			virtual void setSearchError(bool error);
-			virtual void setSearchFields(boost::shared_ptr<SearchPayload> fields) ;
+			virtual void setSearchFields(boost::shared_ptr<SearchPayload> fields);
+			virtual void setNameSuggestions(const std::vector<std::string>& suggestions);
+
 		protected:
 			virtual int nextId() const;
 		private slots:
@@ -50,6 +52,7 @@ namespace Swift {
 			void setError(const QString& error);
 			JID getServerToSearch();
 			void handleSearch();
+			JID getContactJID() const;
 
 		private:
 			UIEventStream* eventStream_;
-- 
cgit v0.10.2-6-g49f6