From 506d1621530a40d3e59f50f693587c3c4fd724f5 Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Thu, 23 Dec 2010 19:36:00 +0000
Subject: Turn the 'Find other users' into seperate add/chat menu items with a
 wizard.


diff --git a/Swift/Controllers/Chat/UserSearchController.cpp b/Swift/Controllers/Chat/UserSearchController.cpp
index c3e40c8..886aa47 100644
--- a/Swift/Controllers/Chat/UserSearchController.cpp
+++ b/Swift/Controllers/Chat/UserSearchController.cpp
@@ -14,12 +14,13 @@
 
 #include <Swift/Controllers/DiscoServiceWalker.h>
 #include <Swift/Controllers/UIEvents/UIEventStream.h>
-#include <Swift/Controllers/UIEvents/RequestUserSearchUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
 #include <Swift/Controllers/UIInterfaces/UserSearchWindow.h>
 #include <Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h>
 
 namespace Swift {
-UserSearchController::UserSearchController(const JID& jid, UIEventStream* uiEventStream, UserSearchWindowFactory* factory, IQRouter* iqRouter) : jid_(jid) {
+UserSearchController::UserSearchController(Type type, const JID& jid, UIEventStream* uiEventStream, UserSearchWindowFactory* factory, IQRouter* iqRouter) : type_(type), jid_(jid) {
 	iqRouter_ = iqRouter;
 	uiEventStream_ = uiEventStream;
 	uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&UserSearchController::handleUIEvent, this, _1));
@@ -34,10 +35,17 @@ UserSearchController::~UserSearchController() {
 }
 
 void UserSearchController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
-	boost::shared_ptr<RequestUserSearchUIEvent> searchEvent = boost::dynamic_pointer_cast<RequestUserSearchUIEvent>(event);
-	if (searchEvent) {
+	bool handle = false;
+	if (type_ == AddContact) {
+		boost::shared_ptr<RequestAddUserDialogUIEvent> searchEvent = boost::dynamic_pointer_cast<RequestAddUserDialogUIEvent>(event);
+		if (searchEvent) handle = true;
+	} else {
+		boost::shared_ptr<RequestChatWithUserDialogUIEvent> searchEvent = boost::dynamic_pointer_cast<RequestChatWithUserDialogUIEvent>(event);
+		if (searchEvent) handle = true;
+	}
+	if (handle) {
 		if (!window_) {
-			window_ = factory_->createUserSearchWindow(uiEventStream_);
+			window_ = factory_->createUserSearchWindow(type_ == AddContact ? UserSearchWindow::AddContact : UserSearchWindow::ChatToContact, uiEventStream_);
 			window_->onFormRequested.connect(boost::bind(&UserSearchController::handleFormRequested, this, _1));
 			window_->onSearchRequested.connect(boost::bind(&UserSearchController::handleSearch, this, _1, _2));
 			window_->setSelectedService(JID(jid_.getDomain()));
diff --git a/Swift/Controllers/Chat/UserSearchController.h b/Swift/Controllers/Chat/UserSearchController.h
index 3ba3352..c54b8d5 100644
--- a/Swift/Controllers/Chat/UserSearchController.h
+++ b/Swift/Controllers/Chat/UserSearchController.h
@@ -38,7 +38,8 @@ namespace Swift {
 
 	class UserSearchController {
 		public:
-			UserSearchController(const JID& jid, UIEventStream* uiEventStream, UserSearchWindowFactory* userSearchWindowFactory, IQRouter* iqRouter);
+			enum Type {AddContact, StartChat};
+			UserSearchController(Type type, const JID& jid, UIEventStream* uiEventStream, UserSearchWindowFactory* userSearchWindowFactory, IQRouter* iqRouter);
 			~UserSearchController();
 		private:
 			void handleUIEvent(boost::shared_ptr<UIEvent> event);
@@ -48,6 +49,7 @@ namespace Swift {
 			void handleFormResponse(boost::shared_ptr<SearchPayload> items, ErrorPayload::ref error, const JID& jid);
 			void handleSearch(boost::shared_ptr<SearchPayload> fields, const JID& jid);
 			void handleSearchResponse(boost::shared_ptr<SearchPayload> results, ErrorPayload::ref error, const JID& jid);
+			Type type_;
 			UIEventStream* uiEventStream_;
 			UserSearchWindow* window_;
 			UserSearchWindowFactory* factory_;
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 660c12f..ba8d944 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -99,7 +99,8 @@ MainController::MainController(
 	chatsManager_ = NULL;
 	eventWindowController_ = NULL;
 	mucSearchController_ = NULL;
-	userSearchController_ = NULL;
+	userSearchControllerChat_ = NULL;
+	userSearchControllerAdd_ = NULL;
 	quitRequested_ = false;
 
 	timeBeforeNextReconnect_ = -1;
@@ -190,8 +191,10 @@ void MainController::resetClient() {
 	statusTracker_ = NULL;
 	delete profileSettings_;
 	profileSettings_ = NULL;
-	delete userSearchController_;
-	userSearchController_ = NULL;
+	delete userSearchControllerChat_;
+	userSearchControllerChat_ = NULL;
+	delete userSearchControllerAdd_;
+	userSearchControllerAdd_ = NULL;
 }
 
 void MainController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
@@ -248,7 +251,8 @@ void MainController::handleConnected() {
 
 
 		mucSearchController_ = new MUCSearchController(jid_, uiEventStream_, uiFactory_, client_->getIQRouter(), settings_, client_->getNickResolver());
-		userSearchController_ = new UserSearchController(jid_, uiEventStream_, uiFactory_, client_->getIQRouter());
+		userSearchControllerChat_ = new UserSearchController(UserSearchController::StartChat, jid_, uiEventStream_, uiFactory_, client_->getIQRouter());
+		userSearchControllerAdd_ = new UserSearchController(UserSearchController::AddContact, jid_, uiEventStream_, uiFactory_, client_->getIQRouter());
 	}
 	
 	client_->requestRoster();
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 8489f71..f6a269d 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -139,7 +139,8 @@ namespace Swift {
 			boost::shared_ptr<ErrorEvent> lastDisconnectError_;
 			bool useDelayForLatency_;
 			MUCSearchController* mucSearchController_;
-			UserSearchController* userSearchController_;
+			UserSearchController* userSearchControllerChat_;
+			UserSearchController* userSearchControllerAdd_;
 			int timeBeforeNextReconnect_;
 			Timer::ref reconnectTimer_;
 			Timer::ref quitTimer_;
diff --git a/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h b/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h
new file mode 100644
index 0000000..bfa4a8b
--- /dev/null
+++ b/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swift/Controllers/UIEvents/UIEvent.h"
+
+namespace Swift {
+	class RequestAddUserDialogUIEvent : public UIEvent {
+
+	};
+}
diff --git a/Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h b/Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h
new file mode 100644
index 0000000..7b967bc
--- /dev/null
+++ b/Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "Swift/Controllers/UIEvents/UIEvent.h"
+
+namespace Swift {
+	class RequestChatWithUserDialogUIEvent : public UIEvent {
+
+	};
+}
diff --git a/Swift/Controllers/UIEvents/RequestUserSearchUIEvent.h b/Swift/Controllers/UIEvents/RequestUserSearchUIEvent.h
deleted file mode 100644
index 1312a84..0000000
--- a/Swift/Controllers/UIEvents/RequestUserSearchUIEvent.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright (c) 2010 Kevin Smith
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
- */
-
-#pragma once
-
-#include "Swift/Controllers/UIEvents/UIEvent.h"
-
-namespace Swift {
-	class RequestUserSearchUIEvent : public UIEvent {
-
-	};
-}
diff --git a/Swift/Controllers/UIInterfaces/UserSearchWindow.h b/Swift/Controllers/UIInterfaces/UserSearchWindow.h
index dda3409..e4a665b 100644
--- a/Swift/Controllers/UIInterfaces/UserSearchWindow.h
+++ b/Swift/Controllers/UIInterfaces/UserSearchWindow.h
@@ -18,6 +18,7 @@ namespace Swift {
 
 	class UserSearchWindow {
 		public:
+			enum Type {AddContact, ChatToContact};
 			virtual ~UserSearchWindow() {};
 
 			virtual void clear() = 0;
diff --git a/Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h b/Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h
index 808c1db..88e0a07 100644
--- a/Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h
+++ b/Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h
@@ -14,6 +14,6 @@ namespace Swift {
 		public:
 			virtual ~UserSearchWindowFactory() {};
 
-			virtual UserSearchWindow* createUserSearchWindow(UIEventStream* eventStream) = 0;
+			virtual UserSearchWindow* createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream) = 0;
 	};
 }
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
index 6e53258..7a84c59 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -24,7 +24,8 @@
 #include "QtSwiftUtil.h"
 #include "QtTabWidget.h"
 #include "Roster/QtTreeWidget.h"
-#include "Swift/Controllers/UIEvents/RequestUserSearchUIEvent.h"
+#include "Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h"
+#include "Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h"
 #include "Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h"
 #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
 #include "Swift/Controllers/UIEvents/ToggleShowOfflineUIEvent.h"
@@ -84,9 +85,12 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS
 	QAction* joinMUCAction = new QAction("Join Room", this);
 	connect(joinMUCAction, SIGNAL(triggered()), SLOT(handleJoinMUCAction()));
 	actionsMenu->addAction(joinMUCAction);
-	otherUserAction_ = new QAction("Find Other Contact", this);
-	connect(otherUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleOtherUserActionTriggered(bool)));
-	actionsMenu->addAction(otherUserAction_);
+	addUserAction_ = new QAction("Add Contact", this);
+	connect(addUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleAddUserActionTriggered(bool)));
+	actionsMenu->addAction(addUserAction_);
+	chatUserAction_ = new QAction("Start Chat", this);
+	connect(chatUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleChatUserActionTriggered(bool)));
+	actionsMenu->addAction(chatUserAction_);
 	QAction* signOutAction = new QAction("Sign Out", this);
 	connect(signOutAction, SIGNAL(triggered()), SLOT(handleSignOutAction()));
 	actionsMenu->addAction(signOutAction);
@@ -123,8 +127,13 @@ void QtMainWindow::handleEventCountUpdated(int count) {
 	tabs_->setTabText(eventIndex, text);
 }
 
-void QtMainWindow::handleOtherUserActionTriggered(bool /*checked*/) {
-	boost::shared_ptr<UIEvent> event(new RequestUserSearchUIEvent());
+void QtMainWindow::handleAddUserActionTriggered(bool /*checked*/) {
+	boost::shared_ptr<UIEvent> event(new RequestAddUserDialogUIEvent());
+	uiEventStream_->send(event);
+}
+
+void QtMainWindow::handleChatUserActionTriggered(bool /*checked*/) {
+	boost::shared_ptr<UIEvent> event(new RequestChatWithUserDialogUIEvent());
 	uiEventStream_->send(event);
 }
 
diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h
index 10cad66..27972cb 100644
--- a/Swift/QtUI/QtMainWindow.h
+++ b/Swift/QtUI/QtMainWindow.h
@@ -54,14 +54,16 @@ namespace Swift {
 			void handleShowOfflineToggled(bool);
 			void handleJoinMUCAction();
 			void handleSignOutAction();
-			void handleOtherUserActionTriggered(bool checked);
+			void handleAddUserActionTriggered(bool checked);
+			void handleChatUserActionTriggered(bool checked);
 			void handleEventCountUpdated(int count);
 
 		private:
 			std::vector<QMenu*> menus_;
 			QtTreeWidget* treeWidget_;
 			QtRosterHeader* meView_;
-			QAction* otherUserAction_;
+			QAction* addUserAction_;
+			QAction* chatUserAction_;
 			QAction* showOfflineAction_;
 			QtTabWidget* tabs_;
 			QWidget* contactsTabWidget_;
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index 4f1bef5..25a508e 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -78,8 +78,8 @@ ChatWindow* QtUIFactory::createChatWindow(const JID& contact, UIEventStream* eve
 	return chatWindowFactory->createChatWindow(contact, eventStream);
 }
 
-UserSearchWindow* QtUIFactory::createUserSearchWindow(UIEventStream* eventStream) {
-	return new QtUserSearchWindow(eventStream);
+UserSearchWindow* QtUIFactory::createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream) {
+	return new QtUserSearchWindow(eventStream, type);
 };
 
 }
diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h
index 5282b31..6ddc316 100644
--- a/Swift/QtUI/QtUIFactory.h
+++ b/Swift/QtUI/QtUIFactory.h
@@ -33,7 +33,7 @@ namespace Swift {
 			virtual ChatListWindow* createChatListWindow(UIEventStream*);
 			virtual MUCSearchWindow* createMUCSearchWindow(UIEventStream* eventStream);
 			virtual ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream);
-			virtual UserSearchWindow* createUserSearchWindow(UIEventStream* eventStream);
+			virtual UserSearchWindow* createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream);
 
 		private slots:
 			void handleLoginWindowGeometryChanged();
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 0f08556..c0730dc 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -141,7 +141,10 @@ else :
   swiftProgram = myenv.Program("swift", sources)
 
 myenv.Uic4("MUCSearch/QtMUCSearchWindow.ui")
-myenv.Uic4("UserSearch/QtUserSearchWindow.ui")
+myenv.Uic4("UserSearch/QtUserSearchWizard.ui")
+myenv.Uic4("UserSearch/QtUserSearchFirstPage.ui")
+myenv.Uic4("UserSearch/QtUserSearchFieldsPage.ui")
+myenv.Uic4("UserSearch/QtUserSearchResultsPage.ui")
 myenv.Uic4("QtAddContactDialog.ui")
 myenv.Uic4("QtBookmarkDetailWindow.ui")
 myenv.Qrc("DefaultTheme.qrc")
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.ui b/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.ui
new file mode 100644
index 0000000..35f9830
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.ui
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtUserSearchFieldsPage</class>
+ <widget class="QWizardPage" name="QtUserSearchFieldsPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>WizardPage</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0" colspan="2">
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Enter search terms</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="nickInputLabel_">
+     <property name="text">
+      <string>Nickname:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QLineEdit" name="nickInput_"/>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="firstInputLabel_">
+     <property name="text">
+      <string>First name:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QLineEdit" name="firstInput_"/>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="lastInputLabel_">
+     <property name="text">
+      <string>Last name:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QLineEdit" name="lastInput_"/>
+   </item>
+   <item row="4" column="0">
+    <widget class="QLabel" name="emailInputLabel_">
+     <property name="text">
+      <string>E-Mail:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <widget class="QLineEdit" name="emailInput_"/>
+   </item>
+   <item row="6" column="1">
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="5" column="0">
+    <widget class="QLabel" name="fetchingThrobber_">
+     <property name="text">
+      <string>TextLabel</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="1">
+    <widget class="QLabel" name="fetchingLabel_">
+     <property name="text">
+      <string>Fetching search fields</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/UserSearch/QtUserSearchFirstPage.ui b/Swift/QtUI/UserSearch/QtUserSearchFirstPage.ui
new file mode 100644
index 0000000..cbf3831
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtUserSearchFirstPage.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtUserSearchFirstPage</class>
+ <widget class="QWizardPage" name="QtUserSearchFirstPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>WizardPage</string>
+  </property>
+  <property name="title">
+   <string>Add a user</string>
+  </property>
+  <property name="subTitle">
+   <string>Add another user to your roster. If you know their JID you can add them directly, or you can search for them.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="errorLabel_">
+     <property name="text">
+      <string>&lt;font color='red'&gt;errorLabel_&lt;/font&gt;</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="howLabel_">
+     <property name="text">
+      <string>howLabel_</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QRadioButton" name="byJID_">
+     <property name="text">
+      <string>I know their JID</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QRadioButton" name="byLocalSearch_">
+     <property name="text">
+      <string>I'd like to search my server</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QRadioButton" name="byRemoteSearch_">
+     <property name="text">
+      <string>I'd like to search another server</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="jidLabel_">
+     <property name="text">
+      <string>jidLabel_</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLineEdit" name="jid_"/>
+   </item>
+   <item>
+    <widget class="QComboBox" name="service_">
+     <property name="editable">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/UserSearch/QtUserSearchResultsPage.ui b/Swift/QtUI/UserSearch/QtUserSearchResultsPage.ui
new file mode 100644
index 0000000..e7f7c90
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtUserSearchResultsPage.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtUserSearchResultsPage</class>
+ <widget class="QWizardPage" name="QtUserSearchResultsPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>WizardPage</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QTreeView" name="results_"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
index c3038d8..b3b64b9 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
@@ -8,6 +8,8 @@
 
 #include <qdebug.h>
 #include <QModelIndex>
+#include <QWizardPage>
+#include <QMovie>
 
 #include "Swift/Controllers/UIEvents/UIEventStream.h"
 #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
@@ -18,7 +20,7 @@
 
 namespace Swift {
 
-QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream) {
+QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type) : type_(type) {
 #ifndef Q_WS_MAC
 	setWindowIcon(QIcon(":/logo-icon-16.png"));
 #endif
@@ -26,150 +28,240 @@ QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream) {
 	setupUi(this);
 	model_ = new UserSearchModel();
 	delegate_ = new UserSearchDelegate();
-	results_->setModel(model_);
-	results_->setItemDelegate(delegate_);
-	results_->setHeaderHidden(true);
+	firstPage_ = new QtUserSearchFirstPage();
+	connect(firstPage_->byJID_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange()));
+	connect(firstPage_->byLocalSearch_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange()));
+	connect(firstPage_->byRemoteSearch_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange()));
+	fieldsPage_ = new QtUserSearchFieldsPage();
+	fieldsPage_->fetchingThrobber_->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this));
+	fieldsPage_->fetchingThrobber_->movie()->stop();
+	resultsPage_ = new QtUserSearchResultsPage();
+	resultsPage_->results_->setModel(model_);
+	resultsPage_->results_->setItemDelegate(delegate_);
+	resultsPage_->results_->setHeaderHidden(true);
+	setPage(1, firstPage_);
+	setPage(2, fieldsPage_);
+	setPage(3, resultsPage_);
+
 #ifdef SWIFT_PLATFORM_MACOSX
-	results_->setAlternatingRowColors(true);
+	resultsPage_->results_->setAlternatingRowColors(true);
 #endif
-	connect(service_, SIGNAL(activated(const QString&)), this, SLOT(handleGetForm()));
-	connect(getSearchForm_, SIGNAL(clicked()), this, SLOT(handleGetForm()));
-	//connect(user_, SIGNAL(returnPressed()), this, SLOT(handleJoin()));
-	//connect(nickName_, SIGNAL(returnPressed()), room_, SLOT(setFocus()));
-	connect(search_, SIGNAL(clicked()), this, SLOT(handleSearch()));
-
-	connect(results_, SIGNAL(clicked(const QModelIndex&)), this, SLOT(handleSelected(const QModelIndex&)));
-	connect(results_, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleActivated(const QModelIndex&)));
-	connect(buttonBox_, SIGNAL(accepted()), this, SLOT(handleOkClicked()));
-	connect(buttonBox_, SIGNAL(rejected()), this, SLOT(handleCancelClicked()));
-	/* When inputs change, check if OK is enabled */
-	connect(jid_, SIGNAL(textChanged(const QString&)), this, SLOT(enableCorrectButtons()));
-	connect(nickName_, SIGNAL(textChanged(const QString&)), this, SLOT(enableCorrectButtons()));
-	connect(startChat_, SIGNAL(stateChanged(int)), this, SLOT(enableCorrectButtons()));
-	connect(addToRoster_, SIGNAL(stateChanged(int)), this, SLOT(enableCorrectButtons()));
-	enableCorrectButtons();
+	connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(handleCurrentChanged(int)));
+	connect(this, SIGNAL(accepted()), this, SLOT(handleAccepted()));
+	clear();
 }
 
 QtUserSearchWindow::~QtUserSearchWindow() {
 
 }
 
-void QtUserSearchWindow::handleGetForm() {
-	lastServiceJID_ = JID(Q2PSTRING(service_->currentText()));
-	onFormRequested(lastServiceJID_);
+void QtUserSearchWindow::handleCurrentChanged(int page) {
+	qDebug() << "Next called, currently, was " << currentId() << ", " << lastPage_;
+	if (page == 2 && lastPage_ == 1) {
+		setError("");
+		/* next won't be called if JID is selected */
+		JID server = searchServer();
+		clearForm();
+		onFormRequested(server);
+	} else if (page == 3 && lastPage_ == 2) {
+		handleSearch();
+	}
+	lastPage_ = page;
 }
 
-void QtUserSearchWindow::handleSearch() {
-	boost::shared_ptr<SearchPayload> search(new SearchPayload());
-	if (nickInput_->isEnabled()) {
-		search->setNick(Q2PSTRING(nickInput_->text()));
-	}
-	if (firstInput_->isEnabled()) {
-		search->setFirst(Q2PSTRING(firstInput_->text()));
-	}
-	if (lastInput_->isEnabled()) {
-		search->setLast(Q2PSTRING(lastInput_->text()));
+JID QtUserSearchWindow::searchServer() {
+	return firstPage_->byRemoteSearch_->isChecked() ? JID(Q2PSTRING(firstPage_->service_->currentText())) : myServer_;
+}
+
+void QtUserSearchWindow::handleAccepted() {
+	JID jid;
+	if (!firstPage_->byJID_->isChecked()) {
+		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 {
+		jid = JID(Q2PSTRING(firstPage_->jid_->text()));
 	}
-	if (emailInput_->isEnabled()) {
-		search->setEMail(Q2PSTRING(emailInput_->text()));
+	if (type_ == AddContact) {
+		/* FIXME: allow specifying a nick */
+		boost::shared_ptr<UIEvent> event(new AddContactUIEvent(jid, jid.toString()));
+		eventStream_->send(event);
+	} else {
+		boost::shared_ptr<UIEvent> event(new RequestChatUIEvent(jid));
+		eventStream_->send(event);
 	}
-	onSearchRequested(search, lastServiceJID_);
 }
 
-void QtUserSearchWindow::show() {
-	clear();
-	enableCorrectButtons();
-	QWidget::show();
+int QtUserSearchWindow::nextId() const {
+	switch (currentId()) {
+		case 1: return firstPage_->byJID_->isChecked() ? -1 : 2;
+		case 2: return 3;
+		case 3: return -1;
+		default: return -1;
+	}
 }
 
-void QtUserSearchWindow::enableCorrectButtons() {
-	bool enable = !jid_->text().isEmpty() && (startChat_->isChecked() || (addToRoster_->isChecked() && !nickName_->text().isEmpty()));
-	buttonBox_->button(QDialogButtonBox::Ok)->setEnabled(enable);
+void QtUserSearchWindow::handleFirstPageRadioChange() {
+	if (firstPage_->byJID_->isChecked()) {
+		firstPage_->jidLabel_->setText("What is their JID? (This has the form 'alice@wonderland.lit')");
+		firstPage_->jidLabel_->show();
+		firstPage_->service_->hide();
+		firstPage_->jid_->setText("");
+		firstPage_->jid_->show();
+		restart();
+	} else if (firstPage_->byRemoteSearch_->isChecked()) {
+		firstPage_->jidLabel_->setText("Which server do you want to search? (This has the form 'wonderland.lit')");
+		firstPage_->jidLabel_->hide();
+		firstPage_->service_->show();
+		firstPage_->service_->setEditText("");
+		//firstPage_->jid_->setText("");
+		firstPage_->jid_->hide();
+		restart();
+	} else {
+		firstPage_->jid_->hide();
+		firstPage_->jidLabel_->hide();
+		firstPage_->service_->hide();
+		restart();
+	}
 }
 
-void QtUserSearchWindow::handleOkClicked() {
-	JID contact = JID(Q2PSTRING(jid_->text()));
-	String nick = Q2PSTRING(nickName_->text());
-	if (addToRoster_->isChecked()) {
-		boost::shared_ptr<UIEvent> event(new AddContactUIEvent(contact, nick));
-		eventStream_->send(event);
+void QtUserSearchWindow::handleSearch() {
+	boost::shared_ptr<SearchPayload> search(new SearchPayload());
+	if (fieldsPage_->nickInput_->isEnabled()) {
+		search->setNick(Q2PSTRING(fieldsPage_->nickInput_->text()));
 	}
-	if (startChat_->isChecked()) {
-		boost::shared_ptr<UIEvent> event(new RequestChatUIEvent(contact));
-		eventStream_->send(event);
+	if (fieldsPage_->firstInput_->isEnabled()) {
+		search->setFirst(Q2PSTRING(fieldsPage_->firstInput_->text()));
+	}
+	if (fieldsPage_->lastInput_->isEnabled()) {
+		search->setLast(Q2PSTRING(fieldsPage_->lastInput_->text()));
 	}
-	hide();
+	if (fieldsPage_->emailInput_->isEnabled()) {
+		search->setEMail(Q2PSTRING(fieldsPage_->emailInput_->text()));
+	}
+	onSearchRequested(search, searchServer());
 }
 
-void QtUserSearchWindow::handleCancelClicked() {
-	hide();
+void QtUserSearchWindow::show() {
+	clear();
+	QWidget::show();
 }
-
+//
+//void QtUserSearchWindow::enableCorrectButtons() {
+//	bool enable = !jid_->text().isEmpty() && (startChat_->isChecked() || (addToRoster_->isChecked() && !nickName_->text().isEmpty()));
+//	buttonBox_->button(QDialogButtonBox::Ok)->setEnabled(enable);
+//}
+//
+//void QtUserSearchWindow::handleOkClicked() {
+//	JID contact = JID(Q2PSTRING(jid_->text()));
+//	String nick = Q2PSTRING(nickName_->text());
+//	if (addToRoster_->isChecked()) {
+//		boost::shared_ptr<UIEvent> event(new AddContactUIEvent(contact, nick));
+//		eventStream_->send(event);
+//	}
+//	if (startChat_->isChecked()) {
+//		boost::shared_ptr<UIEvent> event(new RequestChatUIEvent(contact));
+//		eventStream_->send(event);
+//	}
+//	hide();
+//}
+//
+//void QtUserSearchWindow::handleCancelClicked() {
+//	hide();
+//}
+//
 void QtUserSearchWindow::addSavedServices(const std::vector<JID>& services) {
-	service_->clear();
+	firstPage_->service_->clear();
 	foreach (JID jid, services) {
-		service_->addItem(P2QSTRING(jid.toString()));
-	}
-	service_->clearEditText();
-}
-
-void QtUserSearchWindow::setServerSupportsSearch(bool support) {
-	if (!support) {
-		stack_->setCurrentIndex(0);
-		messageLabel_->setText("This service doesn't support searching for users.");
-		search_->setEnabled(false);
+		firstPage_->service_->addItem(P2QSTRING(jid.toString()));
 	}
+	firstPage_->service_->clearEditText();
 }
 
 void QtUserSearchWindow::setSearchFields(boost::shared_ptr<SearchPayload> fields) {
+	fieldsPage_->fetchingThrobber_->hide();
+	fieldsPage_->fetchingThrobber_->movie()->stop();
+	fieldsPage_->fetchingLabel_->hide();
 	bool enabled[8] = {fields->getNick(), fields->getNick(), fields->getFirst(), fields->getFirst(), fields->getLast(), fields->getLast(), fields->getEMail(), fields->getEMail()};
-	QWidget* widget[8] = {nickInputLabel_, nickInput_, firstInputLabel_, firstInput_, lastInputLabel_, lastInput_, emailInputLabel_, emailInput_};
+	QWidget* legacySearchWidgets[8] = {fieldsPage_->nickInputLabel_, fieldsPage_->nickInput_, fieldsPage_->firstInputLabel_, fieldsPage_->firstInput_, fieldsPage_->lastInputLabel_, fieldsPage_->lastInput_, fieldsPage_->emailInputLabel_, fieldsPage_->emailInput_};
 	for (int i = 0; i < 8; i++) {
-		widget[i]->setVisible(enabled[i]);
-		widget[i]->setEnabled(enabled[i]);
+		legacySearchWidgets[i]->setVisible(enabled[i]);
+		legacySearchWidgets[i]->setEnabled(enabled[i]);
 	}
-	stack_->setCurrentIndex(1);
-	search_->setEnabled(true);
 }
-
-void QtUserSearchWindow::handleActivated(const QModelIndex& index) {
-	if (!index.isValid()) {
-		return;
-	}
-	UserSearchResult* userItem = static_cast<UserSearchResult*>(index.internalPointer());
-	if (userItem) { /* static cast, so always will be, but if we change to be like mucsearch, remember the check.*/
-		handleSelected(index);
-		//handleJoin(); /* Don't do anything automatically on selection.*/
-	}
-}
-
-void QtUserSearchWindow::handleSelected(const QModelIndex& current) {
-	if (!current.isValid()) {
-		return;
-	}
-	UserSearchResult* userItem = static_cast<UserSearchResult*>(current.internalPointer());
-	if (userItem) { /* Remember to leave this if we change to dynamic cast */
-		jid_->setText(P2QSTRING(userItem->getJID().toString()));
-	}
-}
-
+//
+//void QtUserSearchWindow::handleActivated(const QModelIndex& index) {
+//	if (!index.isValid()) {
+//		return;
+//	}
+//	UserSearchResult* userItem = static_cast<UserSearchResult*>(index.internalPointer());
+//	if (userItem) { /* static cast, so always will be, but if we change to be like mucsearch, remember the check.*/
+//		handleSelected(index);
+//		//handleJoin(); /* Don't do anything automatically on selection.*/
+//	}
+//}
+//
+//void QtUserSearchWindow::handleSelected(const QModelIndex& current) {
+//	if (!current.isValid()) {
+//		return;
+//	}
+//	UserSearchResult* userItem = static_cast<UserSearchResult*>(current.internalPointer());
+//	if (userItem) { /* Remember to leave this if we change to dynamic cast */
+//		jid_->setText(P2QSTRING(userItem->getJID().toString()));
+//	}
+//}
+//
 void QtUserSearchWindow::setResults(const std::vector<UserSearchResult>& results) {
 	model_->setResults(results);
 }
 
 void QtUserSearchWindow::setSelectedService(const JID& jid) {
-	service_->setEditText(P2QSTRING(jid.toString()));
+	myServer_ = jid;
+}
+
+void QtUserSearchWindow::clearForm() {
+	fieldsPage_->fetchingThrobber_->show();
+	fieldsPage_->fetchingThrobber_->movie()->start();
+	fieldsPage_->fetchingLabel_->show();
+	QWidget* legacySearchWidgets[8] = {fieldsPage_->nickInputLabel_, fieldsPage_->nickInput_, fieldsPage_->firstInputLabel_, fieldsPage_->firstInput_, fieldsPage_->lastInputLabel_, fieldsPage_->lastInput_, fieldsPage_->emailInputLabel_, fieldsPage_->emailInput_};
+	for (int i = 0; i < 8; i++) {
+		legacySearchWidgets[i]->hide();
+	}
 }
 
 void QtUserSearchWindow::clear() {
-	stack_->setCurrentIndex(0);
-	messageLabel_->setText("Please choose a service to search, above");
+	firstPage_->errorLabel_->setVisible(false);
+	firstPage_->howLabel_->setText(QString("How would you like to find the user to %1?").arg(type_ == AddContact ? "add" : "chat to"));
+	firstPage_->byJID_->setChecked(true);
+	clearForm();
 	model_->clear();
-	search_->setEnabled(false);
+	handleFirstPageRadioChange();
+	restart();
+	lastPage_ = 1;
+}
+
+void QtUserSearchWindow::setError(const QString& error) {
+	if (error.isEmpty()) {
+		firstPage_->errorLabel_->hide();
+	} else {
+		firstPage_->errorLabel_->setText(QString("<font color='red'>%1</font>").arg(error));
+		firstPage_->errorLabel_->show();
+		restart();
+		lastPage_ = 1;
+	}
 }
 
 void QtUserSearchWindow::setSearchError(bool error) {
-	//FIXME
+	if (error) {
+		setError("Error while searching");
+	}
+}
+
+void QtUserSearchWindow::setServerSupportsSearch(bool support) {
+	if (!support) {
+		setError("This server doesn't support searching for users.");
+	}
 }
 
 }
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.h b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
index dca321a..b4c4dc0 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.h
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.h
@@ -6,9 +6,14 @@
 
 #pragma once
 
-#include "Swift/QtUI/UserSearch/ui_QtUserSearchWindow.h"
+#include <QWizard>
 
-#include "Swift/Controllers/UIInterfaces/UserSearchWindow.h"
+#include <Swift/QtUI/UserSearch/ui_QtUserSearchWizard.h>
+#include <Swift/QtUI/UserSearch/ui_QtUserSearchFirstPage.h>
+#include <Swift/QtUI/UserSearch/ui_QtUserSearchFieldsPage.h>
+#include <Swift/QtUI/UserSearch/ui_QtUserSearchResultsPage.h>
+
+#include <Swift/Controllers/UIInterfaces/UserSearchWindow.h>
 
 
 namespace Swift {
@@ -16,10 +21,35 @@ namespace Swift {
 	class UserSearchDelegate;
 	class UserSearchResult;
 	class UIEventStream;
-	class QtUserSearchWindow : public QWidget, public UserSearchWindow, private Ui::QtUserSearchWindow {
+
+	class QtUserSearchFirstPage : public QWizardPage, public Ui::QtUserSearchFirstPage {
+		Q_OBJECT
+		public:
+			QtUserSearchFirstPage() {
+				setupUi(this);
+			}
+	};
+
+	class QtUserSearchFieldsPage : public QWizardPage, public Ui::QtUserSearchFieldsPage {
+		Q_OBJECT
+		public:
+			QtUserSearchFieldsPage() {
+				setupUi(this);
+			}
+	};
+
+	class QtUserSearchResultsPage : public QWizardPage, public Ui::QtUserSearchResultsPage {
+		Q_OBJECT
+		public:
+			QtUserSearchResultsPage() {
+				setupUi(this);
+			}
+	};
+
+	class QtUserSearchWindow : public QWizard, public UserSearchWindow, private Ui::QtUserSearchWizard {
 		Q_OBJECT
 		public:
-			QtUserSearchWindow(UIEventStream* eventStream);
+			QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type);
 			virtual ~QtUserSearchWindow();
 
 			virtual void addSavedServices(const std::vector<JID>& services);
@@ -31,18 +61,25 @@ namespace Swift {
 			virtual void setServerSupportsSearch(bool error);
 			virtual void setSearchError(bool error);
 			virtual void setSearchFields(boost::shared_ptr<SearchPayload> fields) ;
+		protected:
+			virtual int nextId() const;
 		private slots:
-			void handleGetForm();
-			void handleSelected(const QModelIndex& current);
-			void handleSearch();
-			void handleActivated(const QModelIndex& index);
-			void handleOkClicked();
-			void handleCancelClicked();
-			void enableCorrectButtons();
+			void handleFirstPageRadioChange();
+			virtual void handleCurrentChanged(int);
+			virtual void handleAccepted();
 		private:
+			void clearForm();
+			void setError(const QString& error);
+			JID searchServer();
+			void handleSearch();
+			UserSearchWindow::Type type_;
 			UserSearchModel* model_;
 			UserSearchDelegate* delegate_;
+			QtUserSearchFirstPage* firstPage_;
+			QtUserSearchFieldsPage* fieldsPage_;
+			QtUserSearchResultsPage* resultsPage_;
 			UIEventStream* eventStream_;
-			JID lastServiceJID_;
+			JID myServer_;
+			int lastPage_;
 	};
 }
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWizard.ui b/Swift/QtUI/UserSearch/QtUserSearchWizard.ui
new file mode 100644
index 0000000..d403b0b
--- /dev/null
+++ b/Swift/QtUI/UserSearch/QtUserSearchWizard.ui
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtUserSearchWizard</class>
+ <widget class="QWizard" name="QtUserSearchWizard">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>711</width>
+    <height>524</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Find User</string>
+  </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
-- 
cgit v0.10.2-6-g49f6