From acac7962ba04c083377f62f4265ecc754176f74e Mon Sep 17 00:00:00 2001
From: Richard Maudsley <richard.maudsley@isode.com>
Date: Fri, 2 May 2014 10:19:23 +0100
Subject: Refactor AdHoc forms.

Test-Information:

Check that forms still open properly and can be submitted and canceled. Check that error message is displayed if disconnected when a form is open.

Change-Id: I23e35730b0decdfb5cf0592fc7234bf4643b6127

diff --git a/Swift/Controllers/AdHocController.cpp b/Swift/Controllers/AdHocController.cpp
new file mode 100644
index 0000000..c017120
--- /dev/null
+++ b/Swift/Controllers/AdHocController.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010-2014 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <boost/bind.hpp>
+#include <Swift/Controllers/AdHocController.h>
+#include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h>
+
+namespace Swift {
+
+AdHocController::AdHocController(AdHocCommandWindowFactory* factory, boost::shared_ptr<OutgoingAdHocCommandSession> command) {
+	window_ = factory->createAdHocCommandWindow(command);
+	window_->onClosing.connect(boost::bind(&AdHocController::handleWindowClosed, this));
+}
+
+AdHocController::~AdHocController() {
+	window_->onClosing.disconnect(boost::bind(&AdHocController::handleWindowClosed, this));
+	delete window_;
+}
+
+void AdHocController::setOnline(bool online) {
+	window_->setOnline(online);
+}
+
+void AdHocController::handleWindowClosed() {
+	onDeleting();
+}
+
+}
diff --git a/Swift/Controllers/AdHocController.h b/Swift/Controllers/AdHocController.h
new file mode 100644
index 0000000..910756f
--- /dev/null
+++ b/Swift/Controllers/AdHocController.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2010-2014 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h>
+
+namespace Swift {
+
+class AdHocCommandWindowFactory;
+class AdHocCommandWindow;
+
+class AdHocController {
+public:
+	AdHocController(AdHocCommandWindowFactory* factory, boost::shared_ptr<OutgoingAdHocCommandSession> command);
+	~AdHocController();
+	boost::signal<void ()> onDeleting;
+	void setOnline(bool online);
+private:
+	void handleWindowClosed();
+	AdHocCommandWindow* window_;
+};
+
+}
diff --git a/Swift/Controllers/AdHocManager.cpp b/Swift/Controllers/AdHocManager.cpp
index e926138..59e139b 100644
--- a/Swift/Controllers/AdHocManager.cpp
+++ b/Swift/Controllers/AdHocManager.cpp
@@ -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.
  */
@@ -31,6 +31,14 @@ AdHocManager::AdHocManager(const JID& jid, AdHocCommandWindowFactory* factory, I
 
 AdHocManager::~AdHocManager() {
 	uiEventStream_->onUIEvent.disconnect(boost::bind(&AdHocManager::handleUIEvent, this, _1));
+	for (size_t i = 0; i < controllers_.size(); ++i) {
+		controllers_[i]->onDeleting.disconnect(boost::bind(&AdHocManager::removeController, this, controllers_[i]));
+	}
+}
+
+void AdHocManager::removeController(boost::shared_ptr<AdHocController> controller) {
+	controller->onDeleting.disconnect(boost::bind(&AdHocManager::removeController, this, controller));
+	controllers_.erase(std::find(controllers_.begin(), controllers_.end(), controller));
 }
 
 void AdHocManager::setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info) {
@@ -45,7 +53,12 @@ void AdHocManager::setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info) {
 	} else {
 		mainWindow_->setAvailableAdHocCommands(std::vector<DiscoItems::Item>());
 	}
+}
 
+void AdHocManager::setOnline(bool online) {
+	foreach (boost::shared_ptr<AdHocController> controller, controllers_) {
+		controller->setOnline(online);
+	}
 }
 
 void AdHocManager::handleServerDiscoItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error) {
@@ -63,7 +76,10 @@ void AdHocManager::handleServerDiscoItemsResponse(boost::shared_ptr<DiscoItems>
 void AdHocManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {
 	boost::shared_ptr<RequestAdHocUIEvent> adHocEvent = boost::dynamic_pointer_cast<RequestAdHocUIEvent>(event);
 	if (adHocEvent) {
-		factory_->createAdHocCommandWindow(boost::make_shared<OutgoingAdHocCommandSession>(adHocEvent->getCommand().getJID(), adHocEvent->getCommand().getNode(), iqRouter_));
+		boost::shared_ptr<OutgoingAdHocCommandSession> command = boost::make_shared<OutgoingAdHocCommandSession>(adHocEvent->getCommand().getJID(), adHocEvent->getCommand().getNode(), iqRouter_);
+		boost::shared_ptr<AdHocController> controller = boost::make_shared<AdHocController>(factory_, command);
+		controller->onDeleting.connect(boost::bind(&AdHocManager::removeController, this, controller));
+		controllers_.push_back(controller);
 	}
 }
 
diff --git a/Swift/Controllers/AdHocManager.h b/Swift/Controllers/AdHocManager.h
index 47b03cd..b2c34c5 100644
--- a/Swift/Controllers/AdHocManager.h
+++ b/Swift/Controllers/AdHocManager.h
@@ -1,12 +1,9 @@
 /*
- * 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.
  */
 
-#pragma once
-
-#include <boost/shared_ptr.hpp>
 #include <vector>
 
 #include <Swiften/Base/boost_bsignals.h>
@@ -15,26 +12,33 @@
 #include <Swiften/Elements/DiscoItems.h>
 #include <Swiften/Elements/ErrorPayload.h>
 #include <Swiften/Disco/GetDiscoItemsRequest.h>
+#include <Swiften/Client/Client.h>
 #include <Swift/Controllers/UIEvents/UIEvent.h>
+#include <Swift/Controllers/AdHocController.h>
 
 namespace Swift {
-	class IQRouter;
-	class MainWindow;
-	class UIEventStream;
-	class AdHocCommandWindowFactory;
-	class AdHocManager {
-		public:
-			AdHocManager(const JID& jid, AdHocCommandWindowFactory* factory, IQRouter* iqRouter, UIEventStream* uiEventStream, MainWindow* mainWindow);
-			~AdHocManager();
-			void setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info);
-		private:
-			void handleUIEvent(boost::shared_ptr<UIEvent> event);
-			void handleServerDiscoItemsResponse(boost::shared_ptr<DiscoItems>, ErrorPayload::ref error);
-			JID jid_;
-			IQRouter* iqRouter_;
-			UIEventStream* uiEventStream_;
-			MainWindow* mainWindow_;
-			AdHocCommandWindowFactory* factory_;
-			GetDiscoItemsRequest::ref discoItemsRequest_;
-	};
+class IQRouter;
+class MainWindow;
+class UIEventStream;
+class AdHocCommandWindowFactory;
+class AdHocManager {
+public:
+	AdHocManager(const JID& jid, AdHocCommandWindowFactory* factory, IQRouter* iqRouter, UIEventStream* uiEventStream, MainWindow* mainWindow);
+	~AdHocManager();
+	void removeController(boost::shared_ptr<AdHocController> contoller);
+	void setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info);
+	void setOnline(bool online);
+private:
+	void handleServerDiscoItemsResponse(boost::shared_ptr<DiscoItems>, ErrorPayload::ref error);
+	void handleUIEvent(boost::shared_ptr<UIEvent> event);
+	boost::signal<void (const AdHocController&)> onControllerComplete;
+	JID jid_;
+	IQRouter* iqRouter_;
+	UIEventStream* uiEventStream_;
+	MainWindow* mainWindow_;
+	AdHocCommandWindowFactory* factory_;
+	GetDiscoItemsRequest::ref discoItemsRequest_;
+	std::vector<boost::shared_ptr<AdHocController> > controllers_;
+};
+
 }
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index f16f8ad..79b7502 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -422,7 +422,7 @@ void MainController::handleConnected() {
 	/* Enable chats last of all, so rejoining MUCs has the right sent presence */
 	assert(chatsManager_);
 	chatsManager_->setOnline(true);
-
+	adHocManager_->setOnline(true);
 }
 
 void MainController::handleEventQueueLengthChange(int count) {
@@ -602,6 +602,9 @@ void MainController::handleDisconnected(const boost::optional<ClientError>& erro
 	if (rosterController_) {
 		rosterController_->getWindow()->setStreamEncryptionStatus(false);
 	}
+	if (adHocManager_) {
+		adHocManager_->setOnline(false);
+	}
 	if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) {
 		purgeCachedCredentials();
 	}
diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript
index e911a3a..4c71268 100644
--- a/Swift/Controllers/SConscript
+++ b/Swift/Controllers/SConscript
@@ -57,6 +57,7 @@ if env["SCONS_STAGE"] == "build" :
 			"PresenceNotifier.cpp",
 			"EventNotifier.cpp",
 			"AdHocManager.cpp",
+			"AdHocController.cpp",
 			"XMPPEvents/EventController.cpp",
 			"UIEvents/UIEvent.cpp",
 			"UIInterfaces/XMLConsoleWidget.cpp",
diff --git a/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h b/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h
index 835defe..07319c2 100644
--- a/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h
+++ b/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h
@@ -1,14 +1,18 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
 #pragma once
 
+#include <Swiften/Base/boost_bsignals.h>
+
 namespace Swift {
 	class AdHocCommandWindow {
 		public:
 			virtual ~AdHocCommandWindow() {}
+			virtual void setOnline(bool /*online*/) {}
+			boost::signal<void ()> onClosing;
 	};
 }
diff --git a/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h b/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h
index ae77180..eeefa2d 100644
--- a/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h
+++ b/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.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.
  */
@@ -10,13 +10,10 @@
 #include <Swiften/AdHoc/OutgoingAdHocCommandSession.h>
 
 namespace Swift {
+class AdHocCommandWindow;
 	class AdHocCommandWindowFactory {
 		public:
 			virtual ~AdHocCommandWindowFactory() {}
-			/**
-			 * The UI should deal with the lifetime of this window (i.e. DeleteOnClose),
-			 * so the result isn't returned.
-			 */
-			virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) = 0;
+			virtual AdHocCommandWindow* createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) = 0;
 	};
 }
diff --git a/Swift/QtUI/QtAdHocCommandWindow.cpp b/Swift/QtUI/QtAdHocCommandWindow.cpp
index 5d87031..5f99dba 100644
--- a/Swift/QtUI/QtAdHocCommandWindow.cpp
+++ b/Swift/QtUI/QtAdHocCommandWindow.cpp
@@ -1,16 +1,16 @@
 /*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-#include <Swift/QtUI/QtAdHocCommandWindow.h>
-
 #include <boost/bind.hpp>
 #include <QBoxLayout>
+#include <Swift/QtUI/QtAdHocCommandWindow.h>
 #include <Swift/QtUI/QtFormWidget.h>
-#include <Swiften/Elements/Command.h>
 #include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swiften/Base/format.h>
+#include <Swiften/Elements/Command.h>
 
 const int FormLayoutIndex = 1;
 
@@ -29,6 +29,13 @@ QtAdHocCommandWindow::QtAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocComman
 	label_ = new QLabel(this);
 	label_->setTextFormat(Qt::PlainText);
 	layout_->addWidget(label_);
+
+	errorLabel_ = new QLabel(this);
+	errorLabel_->setText(QString("<b>%1</b>").arg(tr("Unable to complete the command because you have been disconnected")));
+	errorLabel_->setVisible(false);
+	errorLabel_->setFrameStyle(QFrame::Box|QFrame::Sunken);
+	layout_->addWidget(errorLabel_);
+
 	QWidget* buttonsWidget = new QWidget(this);
 	layout_->addWidget(buttonsWidget);
 
@@ -48,6 +55,7 @@ QtAdHocCommandWindow::QtAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocComman
 	nextButton_->setEnabled(false);
 	backButton_->setEnabled(false);
 	completeButton_->setEnabled(false);
+
 	actions_[Command::Next] = nextButton_;
 	actions_[Command::Prev] = backButton_;
 	actions_[Command::Complete] = completeButton_;
@@ -55,7 +63,19 @@ QtAdHocCommandWindow::QtAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocComman
 }
 
 QtAdHocCommandWindow::~QtAdHocCommandWindow() {
+}
+
+void QtAdHocCommandWindow::setOnline(bool online) {
+	if (!online) {
+		nextButton_->setEnabled(false);
+		backButton_->setEnabled(false);
+		completeButton_->setEnabled(false);
+		errorLabel_->setVisible(true);
+	}
+}
 
+void QtAdHocCommandWindow::closeEvent(QCloseEvent*) {
+	onClosing();
 }
 
 void QtAdHocCommandWindow::handleCancelClicked() {
diff --git a/Swift/QtUI/QtAdHocCommandWindow.h b/Swift/QtUI/QtAdHocCommandWindow.h
index d42a77d..0e398af 100644
--- a/Swift/QtUI/QtAdHocCommandWindow.h
+++ b/Swift/QtUI/QtAdHocCommandWindow.h
@@ -22,7 +22,9 @@ namespace Swift {
 		public:
 			QtAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);
 			virtual ~QtAdHocCommandWindow();
+			virtual void setOnline(bool online);
 		private:
+			void closeEvent(QCloseEvent* event);
 			void handleNextStageReceived(Command::ref command);
 			void handleError(ErrorPayload::ref error);
 			void setForm(Form::ref);
@@ -38,6 +40,7 @@ namespace Swift {
 			QtFormWidget* formWidget_;
 			Form::ref form_;
 			QLabel* label_;
+			QLabel* errorLabel_;
 			QPushButton* backButton_;
 			QPushButton* nextButton_;
 			QPushButton* completeButton_;
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index e5db22d..afd2a9e 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2012 Remko Tronçon
+ * Copyright (c) 2010-2014 Remko Tronçon
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -172,8 +172,8 @@ BlockListEditorWidget *QtUIFactory::createBlockListEditorWidget() {
 	return new QtBlockListEditorWindow();
 }
 
-void QtUIFactory::createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) {
-	new QtAdHocCommandWindow(command);
+AdHocCommandWindow* QtUIFactory::createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) {
+	return new QtAdHocCommandWindow(command);
 }
 
 }
diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h
index 662c78e..721aa76 100644
--- a/Swift/QtUI/QtUIFactory.h
+++ b/Swift/QtUI/QtUIFactory.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2012 Remko Tronçon
+ * Copyright (c) 2010-2014 Remko Tronçon
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -50,7 +50,7 @@ namespace Swift {
 			virtual WhiteboardWindow* createWhiteboardWindow(boost::shared_ptr<WhiteboardSession> whiteboardSession);
 			virtual HighlightEditorWidget* createHighlightEditorWidget();
 			virtual BlockListEditorWidget* createBlockListEditorWidget();
-			virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);
+			virtual AdHocCommandWindow* createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);
 
 		private slots:
 			void handleLoginWindowGeometryChanged();
-- 
cgit v0.10.2-6-g49f6