From 1f4be30a480818458fd841809585681597be831e Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Tue, 4 Oct 2011 11:02:23 +0100
Subject: Allow both instant and reserved rooms.

Resolves: #1006

diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index c0b2a7d..d3060b8 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -199,7 +199,7 @@ void ChatsManager::handleMUCBookmarkAdded(const MUCBookmark& bookmark) {
 	std::map<JID, MUCController*>::iterator it = mucControllers_.find(bookmark.getRoom());
 	if (it == mucControllers_.end() && bookmark.getAutojoin()) {
 		//FIXME: need vcard stuff here to get a nick
-		handleJoinMUCRequest(bookmark.getRoom(), bookmark.getNick(), false);
+		handleJoinMUCRequest(bookmark.getRoom(), bookmark.getNick(), false, false);
 	}
 	chatListWindow_->addMUCBookmark(bookmark);
 }
@@ -321,7 +321,7 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {
 		mucBookmarkManager_->replaceBookmark(editMUCBookmarkEvent->getOldBookmark(), editMUCBookmarkEvent->getNewBookmark());
 	}
 	else if (JoinMUCUIEvent::ref joinEvent = boost::dynamic_pointer_cast<JoinMUCUIEvent>(event)) {
-		handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getNick(), joinEvent->getShouldJoinAutomatically());
+		handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getNick(), joinEvent->getShouldJoinAutomatically(), joinEvent->getCreateAsReservedRoomIfNew());
 		mucControllers_[joinEvent->getJID()]->activateChatWindow();
 	}
 	else if (boost::shared_ptr<RequestJoinMUCUIEvent> joinEvent = boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) {
@@ -481,7 +481,7 @@ void ChatsManager::rebindControllerJID(const JID& from, const JID& to) {
 	chatControllers_[to]->setToJID(to);
 }
 
-void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<std::string>& nickMaybe, bool addAutoJoin) {
+void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional<std::string>& nickMaybe, bool addAutoJoin, bool createAsReservedIfNew) {
 	if (addAutoJoin) {
 		MUCBookmark bookmark(mucJID, mucJID.getNode());
 		bookmark.setAutojoin(true);
@@ -497,6 +497,9 @@ void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional
 	} else {
 		std::string nick = nickMaybe ? nickMaybe.get() : jid_.getNode();
 		MUC::ref muc = mucManager->createMUC(mucJID);
+		if (createAsReservedIfNew) {
+			muc->setCreateAsReservedIfNew();
+		}
 		MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_);
 		mucControllers_[mucJID] = controller;
 		controller->setAvailableServerFeatures(serverDiscoInfo_);
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index 57643eb..a82492c 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -58,7 +58,7 @@ namespace Swift {
 		private:
 			ChatListWindow::Chat createChatListChatItem(const JID& jid, const std::string& activity);
 			void handleChatRequest(const std::string& contact);
-			void handleJoinMUCRequest(const JID& muc, const boost::optional<std::string>& nick, bool addAutoJoin);
+			void handleJoinMUCRequest(const JID& muc, const boost::optional<std::string>& nick, bool addAutoJoin, bool createAsReservedIfNew);
 			void handleSearchMUCRequest();
 			void handleMUCSelectedAfterSearch(const JID&);
 			void rebindControllerJID(const JID& from, const JID& to);
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index 87d5a16..d72c0f7 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -73,6 +73,7 @@ MUCController::MUCController (
 	chatWindow_->onOccupantActionSelected.connect(boost::bind(&MUCController::handleActionRequestedOnOccupant, this, _1, _2));
 	chatWindow_->onChangeSubjectRequest.connect(boost::bind(&MUCController::handleChangeSubjectRequest, this, _1));
 	chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this, _1));
+	chatWindow_->onConfigurationFormCancelled.connect(boost::bind(&MUCController::handleConfigurationCancelled, this));
 	chatWindow_->onDestroyRequest.connect(boost::bind(&MUCController::handleDestroyRoomRequest, this));
 	chatWindow_->onInvitePersonToThisMUCRequest.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, this, _1, _2));
 	muc_->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1));
@@ -600,6 +601,10 @@ void MUCController::handleConfigurationFormReceived(Form::ref form) {
 	chatWindow_->showRoomConfigurationForm(form);
 }
 
+void MUCController::handleConfigurationCancelled() {
+	muc_->cancelConfigureRoom();
+}
+
 void MUCController::handleDestroyRoomRequest() {
 	muc_->destroyRoom();
 }
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index f83e2af..17dbba4 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -93,6 +93,7 @@ namespace Swift {
 			void handleConfigurationFormReceived(Form::ref);
 			void handleDestroyRoomRequest();
 			void handleInvitePersonToThisMUCRequest(const JID& jid, const std::string& reason);
+			void handleConfigurationCancelled();
 
 		private:
 			MUC::ref muc_;
diff --git a/Swift/Controllers/UIEvents/JoinMUCUIEvent.h b/Swift/Controllers/UIEvents/JoinMUCUIEvent.h
index dea3ead..e1d65ad 100644
--- a/Swift/Controllers/UIEvents/JoinMUCUIEvent.h
+++ b/Swift/Controllers/UIEvents/JoinMUCUIEvent.h
@@ -18,13 +18,15 @@ namespace Swift {
 	class JoinMUCUIEvent : public UIEvent {
 		public:
 			typedef boost::shared_ptr<JoinMUCUIEvent> ref;
-			JoinMUCUIEvent(const JID& jid, const boost::optional<std::string>& nick = boost::optional<std::string>(), bool joinAutomaticallyInFuture = false) : jid_(jid), nick_(nick), joinAutomatically_(joinAutomaticallyInFuture){};
+			JoinMUCUIEvent(const JID& jid, const boost::optional<std::string>& nick = boost::optional<std::string>(), bool joinAutomaticallyInFuture = false, bool createAsReservedRoomIfNew = false) : jid_(jid), nick_(nick), joinAutomatically_(joinAutomaticallyInFuture), createAsReservedRoomIfNew_(createAsReservedRoomIfNew) {};
 			boost::optional<std::string> getNick() {return nick_;};
 			JID getJID() {return jid_;};
 			bool getShouldJoinAutomatically() {return joinAutomatically_;}
+			bool getCreateAsReservedRoomIfNew() {return createAsReservedRoomIfNew_;}
 		private:
 			JID jid_;
 			boost::optional<std::string> nick_;
 			bool joinAutomatically_;
+			bool createAsReservedRoomIfNew_;
 	};
 }
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index 7977940..836a330 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -92,7 +92,12 @@ namespace Swift {
 			 */
 			virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions) = 0;
 
+			/**
+			 * A room configuration has been requested, show the form.
+			 * If the form is cancelled, must emit onConfigurationFormCancelled().
+			 */
 			virtual void showRoomConfigurationForm(Form::ref) = 0;
+
 			boost::signal<void ()> onClosed;
 			boost::signal<void ()> onAllMessagesRead;
 			boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest;
@@ -106,6 +111,7 @@ namespace Swift {
 			boost::signal<void (Form::ref)> onConfigureRequest;
 			boost::signal<void ()> onDestroyRequest;
 			boost::signal<void (const JID&, const std::string& /*reason*/)> onInvitePersonToThisMUCRequest;
+			boost::signal<void ()> onConfigurationFormCancelled;
 			
 			// File transfer related
 			boost::signal<void (std::string /* id */)> onFileTransferCancel;
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index df78767..10daa68 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -734,6 +734,7 @@ void QtChatWindow::showRoomConfigurationForm(Form::ref form) {
 	}
 	mucConfigurationWindow = new QtMUCConfigurationWindow(form);
 	mucConfigurationWindow->onFormComplete.connect(boost::bind(boost::ref(onConfigureRequest), _1));
+	mucConfigurationWindow->onFormCancelled.connect(boost::bind(boost::ref(onConfigurationFormCancelled)));
 }
 
 void QtChatWindow::addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password) {
diff --git a/Swift/QtUI/QtJoinMUCWindow.cpp b/Swift/QtUI/QtJoinMUCWindow.cpp
index a44cdaf..fec3c4d 100644
--- a/Swift/QtUI/QtJoinMUCWindow.cpp
+++ b/Swift/QtUI/QtJoinMUCWindow.cpp
@@ -24,6 +24,7 @@ QtJoinMUCWindow::QtJoinMUCWindow(UIEventStream* uiEventStream) : uiEventStream(u
 	// placeholder for the room is visible. This is just because Qt hides 
 	// the placeholder when a widget is focused for some reason.
 	ui.nickName->setFocus();
+	ui.instantRoom->setChecked(true);
 }
 
 void QtJoinMUCWindow::handleJoin() {
@@ -38,7 +39,7 @@ void QtJoinMUCWindow::handleJoin() {
 
 	lastSetNick = Q2PSTRING(ui.nickName->text());
 	JID room(Q2PSTRING(ui.room->text()));
-	uiEventStream->send(boost::make_shared<JoinMUCUIEvent>(room, lastSetNick, ui.joinAutomatically->isChecked()));
+	uiEventStream->send(boost::make_shared<JoinMUCUIEvent>(room, lastSetNick, ui.joinAutomatically->isChecked(), !ui.instantRoom->isChecked()));
 	hide();
 }
 
diff --git a/Swift/QtUI/QtJoinMUCWindow.ui b/Swift/QtUI/QtJoinMUCWindow.ui
index 493fb26..74fe513 100644
--- a/Swift/QtUI/QtJoinMUCWindow.ui
+++ b/Swift/QtUI/QtJoinMUCWindow.ui
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>410</width>
-    <height>169</height>
+    <height>212</height>
    </rect>
   </property>
   <property name="sizePolicy">
@@ -56,6 +56,13 @@
     </layout>
    </item>
    <item>
+    <widget class="QCheckBox" name="instantRoom">
+     <property name="text">
+      <string>Automatically configure newly created rooms</string>
+     </property>
+    </widget>
+   </item>
+   <item>
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
diff --git a/Swift/QtUI/QtMUCConfigurationWindow.cpp b/Swift/QtUI/QtMUCConfigurationWindow.cpp
index a8dec2a..6fdfd43 100644
--- a/Swift/QtUI/QtMUCConfigurationWindow.cpp
+++ b/Swift/QtUI/QtMUCConfigurationWindow.cpp
@@ -8,10 +8,11 @@
 
 #include <boost/bind.hpp>
 #include <QBoxLayout>
+#include <QCloseEvent>
 #include <Swift/QtUI/QtFormWidget.h>
 
 namespace Swift {
-QtMUCConfigurationWindow::QtMUCConfigurationWindow(Form::ref form) {
+QtMUCConfigurationWindow::QtMUCConfigurationWindow(Form::ref form) : closed_(false) {
 
 	setAttribute(Qt::WA_DeleteOnClose);
 
@@ -43,12 +44,19 @@ QtMUCConfigurationWindow::~QtMUCConfigurationWindow() {
 
 }
 
+void QtMUCConfigurationWindow::closeEvent(QCloseEvent* event) {
+	if (!closed_) {
+		onFormCancelled();
+	}
+}
+
 void QtMUCConfigurationWindow::handleCancelClicked() {
 	close();
 }
 
 void QtMUCConfigurationWindow::handleOKClicked() {
 	onFormComplete(formWidget_->getCompletedForm());
+	closed_ = true;
 	close();
 }
 
diff --git a/Swift/QtUI/QtMUCConfigurationWindow.h b/Swift/QtUI/QtMUCConfigurationWindow.h
index 2be126b..f6a22be 100644
--- a/Swift/QtUI/QtMUCConfigurationWindow.h
+++ b/Swift/QtUI/QtMUCConfigurationWindow.h
@@ -14,6 +14,7 @@
 #include <Swiften/Elements/Form.h>
 
 class QBoxLayout;
+class QCloseEvent;
 
 namespace Swift {
 	class QtFormWidget;
@@ -23,12 +24,16 @@ namespace Swift {
 			QtMUCConfigurationWindow(Form::ref form);
 			virtual ~QtMUCConfigurationWindow();
 			boost::signal<void (Form::ref)> onFormComplete;
+			boost::signal<void ()> onFormCancelled;
 		private slots:
 			void handleCancelClicked();
 			void handleOKClicked();
+		protected:
+			virtual void closeEvent(QCloseEvent* event);
 		private:
 			QtFormWidget* formWidget_;
 			QPushButton* okButton_;
 			QPushButton* cancelButton_;
+			bool closed_;
 	};
 }
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index 57f4175..3f65b14 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -151,7 +151,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
 			// Don't add the first tray (see note above)
 			systemTrays_.push_back(new QtSystemTray());
 		}
-		QtUIFactory* uiFactory = new QtUIFactory(settings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, startMinimized);
+		QtUIFactory* uiFactory = new QtUIFactory(settings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, startMinimized, eagleMode);
 		uiFactories_.push_back(uiFactory);
 		MainController* mainController = new MainController(
 				&clientMainThreadCaller_,
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index 9de700c..faeebdc 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -30,7 +30,7 @@
 
 namespace Swift {
 
-QtUIFactory::QtUIFactory(QtSettingsProvider* settings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, bool startMinimized) : settings(settings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), lastMainWindow(NULL), loginWindow(NULL), startMinimized(startMinimized)  {
+QtUIFactory::QtUIFactory(QtSettingsProvider* settings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, bool startMinimized, bool eagleMode) : settings(settings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), lastMainWindow(NULL), loginWindow(NULL), startMinimized(startMinimized), eagleMode(eagleMode)  {
 	chatFontSize = settings->getIntSetting(CHATWINDOW_FONT_SIZE, 0);
 }
 
diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h
index 8fc5395..319613d 100644
--- a/Swift/QtUI/QtUIFactory.h
+++ b/Swift/QtUI/QtUIFactory.h
@@ -26,7 +26,7 @@ namespace Swift {
 	class QtUIFactory : public QObject, public UIFactory {
 			Q_OBJECT
 		public:
-			QtUIFactory(QtSettingsProvider* settings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, bool startMinimized);
+			QtUIFactory(QtSettingsProvider* settings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, bool startMinimized, bool eagleMode);
 
 			virtual XMLConsoleWidget* createXMLConsoleWidget();
 			virtual MainWindow* createMainWindow(UIEventStream* eventStream);
@@ -57,5 +57,6 @@ namespace Swift {
 			std::vector<QPointer<QtChatWindow> > chatWindows;
 			bool startMinimized;
 			int chatFontSize;
+			bool eagleMode;
 	};
 }
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
index 4c9e537..08391b4 100644
--- a/Swiften/MUC/MUC.cpp
+++ b/Swiften/MUC/MUC.cpp
@@ -29,7 +29,7 @@ namespace Swift {
 
 typedef std::pair<std::string, MUCOccupant> StringMUCOccupantPair;
 
-MUC::MUC(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, const JID &muc, MUCRegistry* mucRegistry) : ownMUCJID(muc), stanzaChannel(stanzaChannel), iqRouter_(iqRouter), presenceSender(presenceSender), mucRegistry(mucRegistry) {
+MUC::MUC(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, const JID &muc, MUCRegistry* mucRegistry) : ownMUCJID(muc), stanzaChannel(stanzaChannel), iqRouter_(iqRouter), presenceSender(presenceSender), mucRegistry(mucRegistry), createAsReservedIfNew(false), unlocking(false) {
 	scopedConnection_ = stanzaChannel->onPresenceReceived.connect(boost::bind(&MUC::handleIncomingPresence, this, _1));
 }
 
@@ -192,18 +192,25 @@ void MUC::handleIncomingPresence(Presence::ref presence) {
 					ownMUCJID = presence->getFrom();
 					presenceSender->addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::AndSendPresence);
 				}
-				MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload());
-				presenceSender->addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::DontSendPresence);
-				mucPayload->setPayload(boost::make_shared<Form>(Form::SubmitType));
-				GenericRequest<MUCOwnerPayload>* request = new GenericRequest<MUCOwnerPayload>(IQ::Set, getJID(), mucPayload, iqRouter_);
-				request->onResponse.connect(boost::bind(&MUC::handleCreationConfigResponse, this, _1, _2));
-				request->send();
+				if (createAsReservedIfNew) {
+					unlocking = true;
+					requestConfigurationForm();
+				}
+				else {
+					MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload());
+					presenceSender->addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::DontSendPresence);
+					mucPayload->setPayload(boost::make_shared<Form>(Form::SubmitType));
+					GenericRequest<MUCOwnerPayload>* request = new GenericRequest<MUCOwnerPayload>(IQ::Set, getJID(), mucPayload, iqRouter_);
+					request->onResponse.connect(boost::bind(&MUC::handleCreationConfigResponse, this, _1, _2));
+					request->send();
+				}
 			}
 		}
 	}
 }
 
 void MUC::handleCreationConfigResponse(MUCOwnerPayload::ref /*unused*/, ErrorPayload::ref error) {
+	unlocking = false;
 	if (error) {
 		presenceSender->removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::AndSendPresence);
 		onJoinFailed(error);
@@ -252,6 +259,13 @@ void MUC::requestConfigurationForm() {
 	request->send();
 }
 
+void MUC::cancelConfigureRoom() {
+	MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload());
+	mucPayload->setPayload(boost::make_shared<Form>(Form::CancelType));
+	GenericRequest<MUCOwnerPayload>* request = new GenericRequest<MUCOwnerPayload>(IQ::Set, getJID(), mucPayload, iqRouter_);
+	request->send();
+}
+
 void MUC::handleConfigurationFormReceived(MUCOwnerPayload::ref payload, ErrorPayload::ref error) {
 	Form::ref form;
 	if (payload) {
@@ -274,7 +288,12 @@ void MUC::configureRoom(Form::ref form) {
 	MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload());
 	mucPayload->setPayload(form);
 	GenericRequest<MUCOwnerPayload>* request = new GenericRequest<MUCOwnerPayload>(IQ::Set, getJID(), mucPayload, iqRouter_);
-	request->onResponse.connect(boost::bind(&MUC::handleConfigurationResultReceived, this, _1, _2));
+	if (unlocking) {
+		request->onResponse.connect(boost::bind(&MUC::handleCreationConfigResponse, this, _1, _2));
+	}
+	else {
+		request->onResponse.connect(boost::bind(&MUC::handleConfigurationResultReceived, this, _1, _2));
+	}
 	request->send();
 }
 
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index 45b8004..adc5707 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -60,9 +60,11 @@ namespace Swift {
 			void changeSubject(const std::string& subject);
 			void requestConfigurationForm();
 			void configureRoom(Form::ref);
+			void cancelConfigureRoom();
 			void destroyRoom();
 			/** Send an invite for the person to join the MUC */
 			void invitePerson(const JID& person, const std::string& reason = "");
+			void setCreateAsReservedIfNew() {createAsReservedIfNew = true;}
 		public:
 			boost::signal<void (const std::string& /*nick*/)> onJoinComplete;
 			boost::signal<void (ErrorPayload::ref)> onJoinFailed;
@@ -106,5 +108,7 @@ namespace Swift {
 			bool joinComplete_;
 			boost::bsignals::scoped_connection scopedConnection_;
 			boost::posix_time::ptime joinSince_;
+			bool createAsReservedIfNew;
+			bool unlocking;
 	};
 }
-- 
cgit v0.10.2-6-g49f6