diff options
28 files changed, 844 insertions, 6 deletions
diff --git a/Swift/Controllers/AdHocManager.cpp b/Swift/Controllers/AdHocManager.cpp new file mode 100644 index 0000000..368771f --- /dev/null +++ b/Swift/Controllers/AdHocManager.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/AdHocManager.h> + +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Disco/GetDiscoItemsRequest.h> +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> +#include <Swift/Controllers/UIInterfaces/MainWindow.h> +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h> + +namespace Swift { + +AdHocManager::AdHocManager(const JID& jid, AdHocCommandWindowFactory* factory, IQRouter* iqRouter, UIEventStream* uiEventStream, MainWindow* mainWindow) : jid_(jid) { +	iqRouter_ = iqRouter; +	uiEventStream_ = uiEventStream; +	mainWindow_ = mainWindow; +	factory_ = factory; + +	uiEventStream_->onUIEvent.connect(boost::bind(&AdHocManager::handleUIEvent, this, _1)); +} + +AdHocManager::~AdHocManager() { + +} + +void AdHocManager::setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info) { +	if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::CommandsFeature)) { +		GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(JID(jid_.getDomain()), DiscoInfo::CommandsFeature, iqRouter_); +		discoItemsRequest->onResponse.connect(boost::bind(&AdHocManager::handleServerDiscoItemsResponse, this, _1, _2)); +		discoItemsRequest->send(); +	} else { +		mainWindow_->setAvailableAdHocCommands(std::vector<DiscoItems::Item>()); +	} + +} + +void AdHocManager::handleServerDiscoItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error) { +	std::vector<DiscoItems::Item> commands; +	if (!error) { +		foreach (DiscoItems::Item item, items->getItems()) { +			if (item.getNode() != "http://isode.com/xmpp/commands#test") { +				commands.push_back(item); +			} +		} +	} +	mainWindow_->setAvailableAdHocCommands(commands); +} + +void AdHocManager::handleUIEvent(boost::shared_ptr<UIEvent> event) { +	boost::shared_ptr<RequestAdHocUIEvent> adHocEvent = boost::dynamic_pointer_cast<RequestAdHocUIEvent>(event); +	if (adHocEvent) { +		factory_->createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession>(new OutgoingAdHocCommandSession(adHocEvent->getCommand(), factory_, iqRouter_))); +	} +} + +} diff --git a/Swift/Controllers/AdHocManager.h b/Swift/Controllers/AdHocManager.h new file mode 100644 index 0000000..9e21bf7 --- /dev/null +++ b/Swift/Controllers/AdHocManager.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010-2011 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/Base/boost_bsignals.h> +#include <Swiften/JID/JID.h> +#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Elements/DiscoItems.h> +#include <Swiften/Elements/ErrorPayload.h> +#include <Swift/Controllers/UIEvents/UIEvent.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_; +	}; +} diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 3f86b43..efe4de4 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -21,7 +21,6 @@  #include "Swift/Controllers/StoragesFactory.h"  #include "Swiften/Client/Storages.h"  #include "Swiften/VCards/VCardManager.h" -#include "Swift/Controllers/Chat/MUCSearchController.h"  #include "Swift/Controllers/Chat/UserSearchController.h"  #include "Swift/Controllers/Chat/ChatsManager.h"  #include "Swift/Controllers/XMPPEvents/EventController.h" @@ -66,6 +65,7 @@  #include <Swift/Controllers/ProfileController.h>  #include <Swift/Controllers/ContactEditController.h>  #include <Swift/Controllers/XMPPURIController.h> +#include "Swift/Controllers/AdHocManager.h"  namespace Swift { @@ -268,14 +268,14 @@ 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_); +		adHocManager_ = new AdHocManager(boundJID_, uiFactory_, client_->getIQRouter(), uiEventStream_, rosterController_->getWindow());  	}  	client_->requestRoster(); -	GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(JID(), client_->getIQRouter()); +	GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(boundJID_.toBare(), client_->getIQRouter());  	discoInfoRequest->onResponse.connect(boost::bind(&MainController::handleServerDiscoInfoResponse, this, _1, _2));  	discoInfoRequest->send(); @@ -560,6 +560,7 @@ void MainController::setManagersOffline() {  void MainController::handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, ErrorPayload::ref error) {  	if (!error) {  		chatsManager_->setServerDiscoInfo(info); +		adHocManager_->setServerDiscoInfo(info);  	}  } diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index f9722de..ae69e70 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -64,6 +64,8 @@ namespace Swift {  	class NetworkFactories;  	class URIHandler;  	class XMPPURIController; +	class AdHocManager; +	class AdHocCommandWindowFactory;  	class MainController {  		public: @@ -133,6 +135,7 @@ namespace Swift {  			RosterController* rosterController_;  			EventController* eventController_;  			EventWindowController* eventWindowController_; +			AdHocManager* adHocManager_;  			LoginWindow* loginWindow_;  			UIEventStream* uiEventStream_;  			XMLConsoleController* xmlConsoleController_; diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index eb5d21f..bd18a3e 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -44,6 +44,7 @@ if env["SCONS_STAGE"] == "build" :  			"StatusTracker.cpp",  			"PresenceNotifier.cpp",  			"EventNotifier.cpp", +			"AdHocManager.cpp",  			"XMPPEvents/EventController.cpp",  			"UIEvents/UIEvent.cpp",  			"UIInterfaces/XMLConsoleWidget.cpp", diff --git a/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h b/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h new file mode 100644 index 0000000..c3b4b49 --- /dev/null +++ b/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h @@ -0,0 +1,21 @@ +/* + * 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/UIInterfaces/MainWindow.h> + +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { +	class RequestAdHocUIEvent : public UIEvent { +		public: +			RequestAdHocUIEvent(const DiscoItems::Item& command) : command_(command) {}; +			const DiscoItems::Item& getCommand() const {return command_;} +		private: +			DiscoItems::Item command_; +	}; +} diff --git a/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h b/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h new file mode 100644 index 0000000..f7a5d39 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +namespace Swift { +	class AdHocCommandWindow { +		public: +			virtual ~AdHocCommandWindow() {}; +	}; +} diff --git a/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h b/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h new file mode 100644 index 0000000..ae77180 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindow.h> +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> + +namespace Swift { +	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; +	}; +} diff --git a/Swift/Controllers/UIInterfaces/MainWindow.h b/Swift/Controllers/UIInterfaces/MainWindow.h index 2fd463b..93584e7 100644 --- a/Swift/Controllers/UIInterfaces/MainWindow.h +++ b/Swift/Controllers/UIInterfaces/MainWindow.h @@ -9,6 +9,7 @@  #include <string>  #include "Swiften/JID/JID.h"  #include "Swiften/Elements/StatusShow.h" +#include "Swiften/Elements/DiscoItems.h"  #include "Swiften/Base/boost_bsignals.h"  #include <boost/shared_ptr.hpp> @@ -33,6 +34,7 @@ namespace Swift {  			/** Must be able to cope with NULL to clear the roster */  			virtual void setRosterModel(Roster* roster) = 0;  			virtual void setConnecting() = 0; +			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) = 0;  			boost::signal<void (StatusShow::Type, const std::string&)> onChangeStatusRequest;  			boost::signal<void ()> onSignOutRequest; diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h index 9b36ac5..57f55d0 100644 --- a/Swift/Controllers/UIInterfaces/UIFactory.h +++ b/Swift/Controllers/UIInterfaces/UIFactory.h @@ -17,6 +17,7 @@  #include <Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h>  #include <Swift/Controllers/UIInterfaces/ProfileWindowFactory.h>  #include <Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h>  namespace Swift {  	class UIFactory :  @@ -30,7 +31,8 @@ namespace Swift {  			public UserSearchWindowFactory,   			public JoinMUCWindowFactory,  			public ProfileWindowFactory, -			public ContactEditWindowFactory { +			public ContactEditWindowFactory, +			public AdHocCommandWindowFactory {  		public:  			virtual ~UIFactory() {}  	}; diff --git a/Swift/Controllers/UnitTest/MockMainWindow.h b/Swift/Controllers/UnitTest/MockMainWindow.h index afa5c2a..f773062 100644 --- a/Swift/Controllers/UnitTest/MockMainWindow.h +++ b/Swift/Controllers/UnitTest/MockMainWindow.h @@ -20,6 +20,7 @@ namespace Swift {  			virtual void setMyAvatarPath(const std::string& /*path*/) {};  			virtual void setMyStatusText(const std::string& /*status*/) {};  			virtual void setMyStatusType(StatusShow::Type /*type*/) {}; +			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& /*commands*/) {};  			virtual void setConnecting() {};  			Roster* roster; diff --git a/Swift/QtUI/QtAdHocCommandWindow.cpp b/Swift/QtUI/QtAdHocCommandWindow.cpp new file mode 100644 index 0000000..f6b1b5c --- /dev/null +++ b/Swift/QtUI/QtAdHocCommandWindow.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2010-2011 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/QtFormWidget.h> + +namespace Swift { +QtAdHocCommandWindow::QtAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) : command_(command) { + +	formWidget_ = NULL; + +	setAttribute(Qt::WA_DeleteOnClose); +	command->onNextStageReceived.connect(boost::bind(&QtAdHocCommandWindow::handleNextStageReceived, this, _1)); +	command->onError.connect(boost::bind(&QtAdHocCommandWindow::handleError, this, _1)); +	command->start(); + +	QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, this); +	layout->setContentsMargins(0,0,0,0); +	layout->setSpacing(2); +	label_ = new QLabel(this); +	layout->addWidget(label_); +	QWidget* formContainer = new QWidget(this); +	layout->addWidget(formContainer); +	formLayout_ = new QBoxLayout(QBoxLayout::TopToBottom, formContainer); +	QWidget* buttonsWidget = new QWidget(this); +	layout->addWidget(buttonsWidget); + +	QBoxLayout* buttonsLayout = new QBoxLayout(QBoxLayout::LeftToRight, buttonsWidget); +	cancelButton_ = new QPushButton(tr("Cancel"), buttonsWidget); +	buttonsLayout->addWidget(cancelButton_); +	connect(cancelButton_, SIGNAL(clicked()), this, SLOT(handleCancelClicked())); +	backButton_ = new QPushButton(tr("Back"), buttonsWidget); +	buttonsLayout->addWidget(backButton_); +	connect(backButton_, SIGNAL(clicked()), this, SLOT(handlePrevClicked())); +	nextButton_ = new QPushButton(tr("Next"), buttonsWidget); +	buttonsLayout->addWidget(nextButton_); +	connect(nextButton_, SIGNAL(clicked()), this, SLOT(handleNextClicked())); +	completeButton_ = new QPushButton(tr("Complete"), buttonsWidget); +	buttonsLayout->addWidget(completeButton_); +	connect(completeButton_, SIGNAL(clicked()), this, SLOT(handleCompleteClicked())); +	nextButton_->setEnabled(false); +	backButton_->setEnabled(false); +	completeButton_->setEnabled(false); +	show(); +} + +QtAdHocCommandWindow::~QtAdHocCommandWindow() { + +} + +void QtAdHocCommandWindow::handleCancelClicked() { +	command_->cancel(); +} + +void QtAdHocCommandWindow::handlePrevClicked() { +	command_->goBack(); +} + +void QtAdHocCommandWindow::handleNextClicked() { +	command_->goNext(formWidget_ ? formWidget_->getCompletedForm() : Form::ref()); +} + +void QtAdHocCommandWindow::handleCompleteClicked() { +	command_->complete(formWidget_ ? formWidget_->getCompletedForm() : Form::ref()); +} + +void QtAdHocCommandWindow::handleNextStageReceived(Command::ref command) { +	if (command->getForm()) { +		setForm(command->getForm()); +	} else { +		setNoForm(); +	} +	QString notes; +	foreach (Command::Note note, command->getNotes()) { +		if (!notes.isEmpty()) { +			notes += "\n"; +			QString qNote(note.note.c_str()); +			switch (note.type) { +				case Command::Note::Error: notes += tr("Error: %1").arg(qNote); break; +				case Command::Note::Warn: notes += tr("Warning: %1").arg(qNote); break; +				case Command::Note::Info: notes += qNote; break; +			} +		} +	} +	label_->setText(notes); +	setAvailableActions(command); +} + +void QtAdHocCommandWindow::handleError(ErrorPayload::ref /*error*/) { +	nextButton_->setEnabled(false); +	backButton_->setEnabled(false); +	completeButton_->setEnabled(false); +	label_->setText(tr("Error executing command")); +} + +void QtAdHocCommandWindow::setForm(Form::ref form) { +	delete formWidget_; +	formWidget_ = new QtFormWidget(form, this); +	formLayout_->addWidget(formWidget_); +} + +void QtAdHocCommandWindow::setNoForm() { +	delete formWidget_; +} + +void QtAdHocCommandWindow::setAvailableActions(Command::ref commandResult) { +	const std::vector<Command::Action> actions = commandResult->getAvailableActions(); +	nextButton_->setEnabled(std::find(actions.begin(), actions.end(), Command::Next) != actions.end()); +	backButton_->setEnabled(std::find(actions.begin(), actions.end(), Command::Prev) != actions.end()); +	completeButton_->setEnabled(std::find(actions.begin(), actions.end(), Command::Complete) != actions.end()); + + +	if (command_->getIsMultiStage()) { +		backButton_->show(); +		nextButton_->show(); +	} +	else { +		backButton_->hide(); +		nextButton_->hide(); + +	} +} + +} diff --git a/Swift/QtUI/QtAdHocCommandWindow.h b/Swift/QtUI/QtAdHocCommandWindow.h new file mode 100644 index 0000000..f1073bc --- /dev/null +++ b/Swift/QtUI/QtAdHocCommandWindow.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QWidget> +#include <QPushButton> +#include <QLabel> + +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindow.h> +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> + +class QBoxLayout; + +namespace Swift { +	class QtFormWidget; +	class QtAdHocCommandWindow : public QWidget, public AdHocCommandWindow { +		Q_OBJECT +		public: +			QtAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command); +			virtual ~QtAdHocCommandWindow(); +		private: +			void handleNextStageReceived(Command::ref command); +			void handleError(ErrorPayload::ref error); +			void setForm(Form::ref); +			void setNoForm(); +			void setAvailableActions(Command::ref commandResult); +		private slots: +			void handleCancelClicked(); +			void handlePrevClicked(); +			void handleNextClicked(); +			void handleCompleteClicked(); +		private: +			boost::shared_ptr<OutgoingAdHocCommandSession> command_; +			QtFormWidget* formWidget_; +			QBoxLayout* formLayout_; +			Form::ref form_; +			QLabel* label_; +			QPushButton* backButton_; +			QPushButton* nextButton_; +			QPushButton* completeButton_; +			QPushButton* cancelButton_; +	}; +} diff --git a/Swift/QtUI/QtFormWidget.cpp b/Swift/QtUI/QtFormWidget.cpp new file mode 100644 index 0000000..659fb25 --- /dev/null +++ b/Swift/QtUI/QtFormWidget.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/QtUI/QtFormWidget.h> + +#include <QGridLayout> +#include <QLabel> +#include <QListWidget> +#include <QLineEdit> +#include <QTextEdit> +#include <QCheckBox> +#include <QScrollArea> + +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swiften/Base/foreach.h> + +namespace Swift { + +QtFormWidget::QtFormWidget(Form::ref form, QWidget* parent) : QWidget(parent), form_(form) { +	QGridLayout* thisLayout = new QGridLayout(this); +	int row = 0; +	if (!form->getTitle().empty()) { +		QLabel* instructions = new QLabel(("<b>" + form->getTitle() + "</b>").c_str(), this); +		thisLayout->addWidget(instructions, row++, 0, 1, 2); +	} +	if (!form->getInstructions().empty()) { +		QLabel* instructions = new QLabel(form->getInstructions().c_str(), this); +		thisLayout->addWidget(instructions, row++, 0, 1, 2); +	} +	QScrollArea* scrollArea = new QScrollArea(this); +	thisLayout->addWidget(scrollArea); +	QWidget* scroll = new QWidget(this); +	QGridLayout* layout = new QGridLayout(scroll); +	foreach (boost::shared_ptr<FormField> field, form->getFields()) { +		QWidget* widget = createWidget(field); +		if (widget) { +			layout->addWidget(new QLabel(field->getLabel().c_str(), this), row, 0); +			layout->addWidget(widget, row++, 1); +		} +	} +	scrollArea->setWidget(scroll); +	scrollArea->setWidgetResizable(true); +} + +QtFormWidget::~QtFormWidget() { + +} + +QListWidget* QtFormWidget::createList(FormField::ref field) { +	QListWidget* listWidget = new QListWidget(this); +	listWidget->setSortingEnabled(false); +	listWidget->setSelectionMode(boost::dynamic_pointer_cast<ListMultiFormField>(field) ? QAbstractItemView::MultiSelection : QAbstractItemView::SingleSelection); +	foreach (FormField::Option option, field->getOptions()) { +		listWidget->addItem(option.label.c_str()); +	} +	boost::shared_ptr<ListMultiFormField> listMultiField = boost::dynamic_pointer_cast<ListMultiFormField>(field); +	boost::shared_ptr<ListSingleFormField> listSingleField = boost::dynamic_pointer_cast<ListSingleFormField>(field); +	for (int i = 0; i < listWidget->count(); i++) { +		QListWidgetItem* item = listWidget->item(i); +		bool selected = false; +		if (listSingleField) { +			selected = (item->text() == QString(listSingleField->getValue().c_str())); +		} +		else if (listMultiField) { +			std::string text = Q2PSTRING(item->text()); +			selected = (std::find(listMultiField->getValue().begin(), listMultiField->getValue().end(), text) != listMultiField->getValue().end()); +		} +		item->setSelected(selected); +	} +	return listWidget; +} + +QWidget* QtFormWidget::createWidget(FormField::ref field) { +	QWidget* widget = NULL; +	boost::shared_ptr<BooleanFormField> booleanField = boost::dynamic_pointer_cast<BooleanFormField>(field); +	if (booleanField) { +		QCheckBox* checkWidget = new QCheckBox(this); +		checkWidget->setCheckState(booleanField->getValue() ? Qt::Checked : Qt::Unchecked); +		widget = checkWidget; +	} +	boost::shared_ptr<FixedFormField> fixedField = boost::dynamic_pointer_cast<FixedFormField>(field); +	if (fixedField) { +		QString value = fixedField->getValue().c_str(); +		widget = new QLabel(value, this); +	} +	boost::shared_ptr<ListSingleFormField> listSingleField = boost::dynamic_pointer_cast<ListSingleFormField>(field); +	if (listSingleField) { +		widget = createList(field); +	} +	boost::shared_ptr<TextMultiFormField> textMultiField = boost::dynamic_pointer_cast<TextMultiFormField>(field); +	if (textMultiField) { +		QString value = textMultiField->getValue().c_str(); +		widget = new QTextEdit(value, this); +	} +	boost::shared_ptr<TextPrivateFormField> textPrivateField = boost::dynamic_pointer_cast<TextPrivateFormField>(field); +	if (textPrivateField) { +		QString value = textPrivateField->getValue().c_str(); +		QLineEdit* lineWidget = new QLineEdit(value, this); +		lineWidget->setEchoMode(QLineEdit::Password); +		widget = lineWidget; +	} +	boost::shared_ptr<TextSingleFormField> textSingleField = boost::dynamic_pointer_cast<TextSingleFormField>(field); +	if (textSingleField) { +		QString value = textSingleField->getValue().c_str(); +		widget = new QLineEdit(value, this); +	} +	boost::shared_ptr<JIDSingleFormField> jidSingleField = boost::dynamic_pointer_cast<JIDSingleFormField>(field); +	if (jidSingleField) { +		QString value = jidSingleField->getValue().toString().c_str(); +		widget = new QLineEdit(value, this); +	} +	boost::shared_ptr<JIDMultiFormField> jidMultiField = boost::dynamic_pointer_cast<JIDMultiFormField>(field); +	if (jidMultiField) { +		QString text; +		bool prev = false; +		foreach (JID line, jidMultiField->getValue()) { +			if (prev) { +				text += "\n"; +			} +			prev = true; +			text += line.toString().c_str(); +		} +		widget = new QTextEdit(text, this); +	} +	boost::shared_ptr<ListMultiFormField> listMultiField = boost::dynamic_pointer_cast<ListMultiFormField>(field); +	if (listMultiField) { +		widget = createList(field); +	} +	boost::shared_ptr<UntypedFormField> untypedField = boost::dynamic_pointer_cast<UntypedFormField>(field); +	if (untypedField) { +	} +	boost::shared_ptr<HiddenFormField> hiddenField = boost::dynamic_pointer_cast<HiddenFormField>(field); +	if (hiddenField) { +	} +	fields_[field->getName()] = widget; +	return widget; +} + +Form::ref QtFormWidget::getCompletedForm() { +	Form::ref result(new Form(Form::SubmitType)); +	foreach (boost::shared_ptr<FormField> field, form_->getFields()) { +		boost::shared_ptr<FormField> resultField; +		boost::shared_ptr<BooleanFormField> booleanField = boost::dynamic_pointer_cast<BooleanFormField>(field); +		if (booleanField) { +			resultField = FormField::ref(BooleanFormField::create(qobject_cast<QCheckBox*>(fields_[field->getName()])->checkState() == Qt::Checked)); +		} +		boost::shared_ptr<FixedFormField> fixedField = boost::dynamic_pointer_cast<FixedFormField>(field); +		if (fixedField) { +			resultField = FormField::ref(FixedFormField::create(fixedField->getValue())); +		} +		boost::shared_ptr<ListSingleFormField> listSingleField = boost::dynamic_pointer_cast<ListSingleFormField>(field); +		if (listSingleField) { +			QListWidget* listWidget = qobject_cast<QListWidget*>(fields_[field->getName()]); +			if (listWidget->selectedItems().size() > 0) { +				int i = listWidget->row(listWidget->selectedItems()[0]); +				resultField = FormField::ref(ListSingleFormField::create(field->getOptions()[i].value)); +			} +			else { +				resultField = FormField::ref(ListSingleFormField::create()); +			} +		} +		boost::shared_ptr<TextMultiFormField> textMultiField = boost::dynamic_pointer_cast<TextMultiFormField>(field); +		if (textMultiField) { +			QTextEdit* widget = qobject_cast<QTextEdit*>(fields_[field->getName()]); +			QString string = widget->toPlainText(); +			if (string.isEmpty()) { +				resultField = FormField::ref(TextMultiFormField::create()); +			} +			else { +				resultField = FormField::ref(TextMultiFormField::create(Q2PSTRING(string))); +			} +		} +		boost::shared_ptr<TextPrivateFormField> textPrivateField = boost::dynamic_pointer_cast<TextPrivateFormField>(field); +		if (textPrivateField) { +			QLineEdit* widget = qobject_cast<QLineEdit*>(fields_[field->getName()]); +			QString string = widget->text(); +			if (string.isEmpty()) { +				resultField = FormField::ref(TextPrivateFormField::create()); +			} +			else { +				resultField = FormField::ref(TextPrivateFormField::create(Q2PSTRING(string))); +			} +		} +		boost::shared_ptr<TextSingleFormField> textSingleField = boost::dynamic_pointer_cast<TextSingleFormField>(field); +		if (textSingleField) { +			QLineEdit* widget = qobject_cast<QLineEdit*>(fields_[field->getName()]); +			QString string = widget->text(); +			if (string.isEmpty()) { +				resultField = FormField::ref(TextSingleFormField::create()); +			} +			else { +				resultField = FormField::ref(TextSingleFormField::create(Q2PSTRING(string))); +			} +		} +		boost::shared_ptr<JIDSingleFormField> jidSingleField = boost::dynamic_pointer_cast<JIDSingleFormField>(field); +		if (jidSingleField) { +			QLineEdit* widget = qobject_cast<QLineEdit*>(fields_[field->getName()]); +			QString string = widget->text(); +			JID jid(Q2PSTRING(string)); +			if (string.isEmpty()) { +				resultField = FormField::ref(JIDSingleFormField::create()); +			} +			else { +				resultField = FormField::ref(JIDSingleFormField::create(jid)); +			} +		} +		boost::shared_ptr<JIDMultiFormField> jidMultiField = boost::dynamic_pointer_cast<JIDMultiFormField>(field); +		if (jidMultiField) { +			QTextEdit* widget = qobject_cast<QTextEdit*>(fields_[field->getName()]); +			QString string = widget->toPlainText(); +			if (string.isEmpty()) { +				resultField = FormField::ref(JIDMultiFormField::create()); +			} +			else { +				QStringList lines = string.split("\n"); +				std::vector<JID> value; +				foreach (QString line, lines) { +					value.push_back(JID(Q2PSTRING(line))); +				} +				resultField = FormField::ref(JIDMultiFormField::create(value)); +			} +		} +		boost::shared_ptr<ListMultiFormField> listMultiField = boost::dynamic_pointer_cast<ListMultiFormField>(field); +		if (listMultiField) { +			QListWidget* listWidget = qobject_cast<QListWidget*>(fields_[field->getName()]); +			std::vector<std::string> values; +			foreach (QListWidgetItem* item, listWidget->selectedItems()) { +				values.push_back(field->getOptions()[listWidget->row(item)].value); +			} +			resultField = FormField::ref(ListMultiFormField::create(values)); +		} +		boost::shared_ptr<UntypedFormField> untypedField = boost::dynamic_pointer_cast<UntypedFormField>(field); +		if (untypedField) { +		} +		boost::shared_ptr<HiddenFormField> hiddenField = boost::dynamic_pointer_cast<HiddenFormField>(field); +		if (hiddenField) { +			resultField = FormField::ref(HiddenFormField::create(hiddenField->getValue())); +		} +		resultField->setName(field->getName()); +		result->addField(resultField); +	} +	return result; +} + +} diff --git a/Swift/QtUI/QtFormWidget.h b/Swift/QtUI/QtFormWidget.h new file mode 100644 index 0000000..2fb7b98 --- /dev/null +++ b/Swift/QtUI/QtFormWidget.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QWidget> + +#include <map> +#include <Swiften/Elements/Form.h> + +class QListWidget; + +namespace Swift { + +class QtFormWidget : public QWidget { +	Q_OBJECT +	public: +		QtFormWidget(Form::ref form, QWidget* parent = NULL); +		virtual ~QtFormWidget(); +		Form::ref getCompletedForm(); +	private: +		QWidget* createWidget(FormField::ref field); +		QListWidget* createList(FormField::ref field); +		std::map<std::string, QWidget*> fields_; +		Form::ref form_; +}; + +} diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 6391961..f7dba58 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -30,6 +30,7 @@  #include "Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h"  #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"  #include "Swift/Controllers/UIEvents/ToggleShowOfflineUIEvent.h" +#include "Swift/Controllers/UIEvents/RequestAdHocUIEvent.h"  namespace Swift { @@ -99,6 +100,8 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS  	chatUserAction_ = new QAction(tr("Start &Chat"), this);  	connect(chatUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleChatUserActionTriggered(bool)));  	actionsMenu->addAction(chatUserAction_); +	serverAdHocMenu_ = new QMenu("Server Commands", this); +	actionsMenu->addMenu(serverAdHocMenu_);  	actionsMenu->addSeparator();  	QAction* signOutAction = new QAction(tr("&Sign Out"), this);  	connect(signOutAction, SIGNAL(triggered()), SLOT(handleSignOutAction())); @@ -106,6 +109,12 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS  	connect(treeWidget_, SIGNAL(onSomethingSelectedChanged(bool)), editUserAction_, SLOT(setEnabled(bool))); +	setAvailableAdHocCommands(std::vector<DiscoItems::Item>()); +	QAction* adHocAction = new QAction("Populating commands..", this); +	adHocAction->setEnabled(false); +	serverAdHocMenu_->addAction(adHocAction); +	serverAdHocCommandActions_.append(adHocAction); +  	lastOfflineState_ = false;  	uiEventStream_->onUIEvent.connect(boost::bind(&QtMainWindow::handleUIEvent, this, _1));  } @@ -206,6 +215,33 @@ void QtMainWindow::setConnecting() {  	meView_->setConnecting();  } +void QtMainWindow::handleAdHocActionTriggered(bool /*checked*/) { +	QAction* action = qobject_cast<QAction*>(sender()); +	assert(action); +	DiscoItems::Item command = serverAdHocCommands_[serverAdHocCommandActions_.indexOf(action)]; +	uiEventStream_->send(boost::shared_ptr<UIEvent>(new RequestAdHocUIEvent(command))); +} + +void QtMainWindow::setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) { +	serverAdHocCommands_ = commands; +	foreach (QAction* action, serverAdHocCommandActions_) { +		delete action; +	} +	serverAdHocMenu_->clear(); +	serverAdHocCommandActions_.clear(); +	foreach (DiscoItems::Item command, commands) { +		QAction* action = new QAction(P2QSTRING(command.getName()), this); +		connect(action, SIGNAL(triggered(bool)), this, SLOT(handleAdHocActionTriggered(bool))); +		serverAdHocMenu_->addAction(action); +		serverAdHocCommandActions_.append(action); +	} +	if (serverAdHocCommandActions_.isEmpty()) { +		QAction* action = new QAction("No available commands", this); +		action->setEnabled(false); +		serverAdHocMenu_->addAction(action); +		serverAdHocCommandActions_.append(action); +	} +}  } diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h index 3462bb0..e20d773 100644 --- a/Swift/QtUI/QtMainWindow.h +++ b/Swift/QtUI/QtMainWindow.h @@ -8,6 +8,7 @@  #include <QWidget>  #include <QMenu> +#include <QList>  #include "Swift/Controllers/UIInterfaces/MainWindow.h"  #include "Swift/QtUI/QtRosterHeader.h"  #include "Swift/QtUI/EventViewer/QtEventWindow.h" @@ -20,7 +21,7 @@ class QLineEdit;  class QPushButton;  class QToolBar;  class QAction; - +class QMenu;  class QTabWidget;  namespace Swift { @@ -46,6 +47,7 @@ namespace Swift {  			QtEventWindow* getEventWindow();  			QtChatListWindow* getChatListWindow();  			void setRosterModel(Roster* roster); +			void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands);  		private slots:  			void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage);  			void handleUIEvent(boost::shared_ptr<UIEvent> event); @@ -55,6 +57,7 @@ namespace Swift {  			void handleEditProfileAction();  			void handleAddUserActionTriggered(bool checked);  			void handleChatUserActionTriggered(bool checked); +			void handleAdHocActionTriggered(bool checked);  			void handleEventCountUpdated(int count);  			void handleEditProfileRequest(); @@ -66,6 +69,7 @@ namespace Swift {  			QAction* editUserAction_;  			QAction* chatUserAction_;  			QAction* showOfflineAction_; +			QMenu* serverAdHocMenu_;  			QtTabWidget* tabs_;  			QWidget* contactsTabWidget_;  			QWidget* eventsTabWidget_; @@ -73,5 +77,7 @@ namespace Swift {  			QtChatListWindow* chatListWindow_;  			UIEventStream* uiEventStream_;  			bool lastOfflineState_; +			std::vector<DiscoItems::Item> serverAdHocCommands_; +			QList<QAction*> serverAdHocCommandActions_;  	};  } diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index 35fbfcd..5c1f78d 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -23,6 +23,7 @@  #include "UserSearch/QtUserSearchWindow.h"  #include "QtProfileWindow.h"  #include "QtContactEditWindow.h" +#include "QtAdHocCommandWindow.h"  namespace Swift { @@ -99,5 +100,8 @@ ContactEditWindow* QtUIFactory::createContactEditWindow() {  	return new QtContactEditWindow();  } +void QtUIFactory::createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) { +	new QtAdHocCommandWindow(command); +}  } diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h index ddaaf6e..9ef228a 100644 --- a/Swift/QtUI/QtUIFactory.h +++ b/Swift/QtUI/QtUIFactory.h @@ -37,6 +37,7 @@ namespace Swift {  			virtual JoinMUCWindow* createJoinMUCWindow();  			virtual ProfileWindow* createProfileWindow();  			virtual ContactEditWindow* createContactEditWindow(); +			virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);  		private slots:  			void handleLoginWindowGeometryChanged(); diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 58f2b99..2f138cd 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -88,6 +88,7 @@ sources = [      "QtTabWidget.cpp",      "QtTextEdit.cpp",      "QtXMLConsoleWidget.cpp", +    "QtAdHocCommandWindow.cpp",      "QtUtilities.cpp",      "QtBookmarkDetailWindow.cpp",      "QtAddBookmarkWindow.cpp", @@ -98,6 +99,7 @@ sources = [      "MessageSnippet.cpp",      "SystemMessageSnippet.cpp",      "QtElidingLabel.cpp", +    "QtFormWidget.cpp",      "QtLineEdit.cpp",      "QtJoinMUCWindow.cpp",      "Roster/RosterModel.cpp", diff --git a/Swiften/AdHoc/OutgoingAdHocCommandSession.cpp b/Swiften/AdHoc/OutgoingAdHocCommandSession.cpp new file mode 100644 index 0000000..40b17e7 --- /dev/null +++ b/Swiften/AdHoc/OutgoingAdHocCommandSession.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> + +#include <boost/bind.hpp> + +#include <Swiften/Queries/GenericRequest.h> + +namespace Swift { +OutgoingAdHocCommandSession::OutgoingAdHocCommandSession(const DiscoItems::Item& command, AdHocCommandWindowFactory* /*factory*/, IQRouter* iqRouter) : command_(command), iqRouter_(iqRouter), isMultiStage_(false) { + +} + +void OutgoingAdHocCommandSession::handleResponse(boost::shared_ptr<Command> payload, ErrorPayload::ref error) { +	if (error) { +		onError(error); +	} else { +		sessionID_ = payload->getSessionID(); +		const std::vector<Command::Action> actions = payload->getAvailableActions(); +		if (std::find(actions.begin(), actions.end(), Command::Next) != actions.end() +				|| std::find(actions.begin(), actions.end(), Command::Prev) != actions.end()) { +			isMultiStage_ = true; +		} +		onNextStageReceived(payload); +	} +} + +bool OutgoingAdHocCommandSession::getIsMultiStage() { +	return isMultiStage_; +} + +void OutgoingAdHocCommandSession::start() { +	boost::shared_ptr<Payload> commandPayload(new Command(command_.getNode())); +	boost::shared_ptr<GenericRequest<Command> > commandRequest(new GenericRequest<Command>(IQ::Set, command_.getJID(), commandPayload, iqRouter_)); +	commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); +	commandRequest->send(); +} + +void OutgoingAdHocCommandSession::cancel() { +	if (!sessionID_.empty()) { +		boost::shared_ptr<Payload> commandPayload(new Command(command_.getNode(), sessionID_, Command::Cancel)); +		boost::shared_ptr<GenericRequest<Command> > commandRequest(new GenericRequest<Command>(IQ::Set, command_.getJID(), commandPayload, iqRouter_)); +		commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); +		commandRequest->send(); +	} +} + +void OutgoingAdHocCommandSession::goBack() { +	boost::shared_ptr<Payload> commandPayload(new Command(command_.getNode(), sessionID_, Command::Prev)); +	boost::shared_ptr<GenericRequest<Command> > commandRequest(new GenericRequest<Command>(IQ::Set, command_.getJID(), commandPayload, iqRouter_)); +	commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); +	commandRequest->send(); +} + +void OutgoingAdHocCommandSession::complete(Form::ref form) { +	Command* command = new Command(command_.getNode(), sessionID_, Command::Complete); +	boost::shared_ptr<Payload> commandPayload(command); +	command->setForm(form); +	boost::shared_ptr<GenericRequest<Command> > commandRequest(new GenericRequest<Command>(IQ::Set, command_.getJID(), commandPayload, iqRouter_)); +	commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); +	commandRequest->send(); +} + +void OutgoingAdHocCommandSession::goNext(Form::ref form) { +	Command* command = new Command(command_.getNode(), sessionID_, Command::Next); +	boost::shared_ptr<Payload> commandPayload(command); +	command->setForm(form); +	boost::shared_ptr<GenericRequest<Command> > commandRequest(new GenericRequest<Command>(IQ::Set, command_.getJID(), commandPayload, iqRouter_)); +	commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); +	commandRequest->send(); +} + +} diff --git a/Swiften/AdHoc/OutgoingAdHocCommandSession.h b/Swiften/AdHoc/OutgoingAdHocCommandSession.h new file mode 100644 index 0000000..820dc62 --- /dev/null +++ b/Swiften/AdHoc/OutgoingAdHocCommandSession.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010-2011 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> +#include <boost/shared_ptr.hpp> +#include <Swiften/Elements/DiscoItems.h> +#include <Swiften/Elements/Command.h> +#include <Swiften/Elements/ErrorPayload.h> + +namespace Swift { +	class IQRouter; +	class MainWindow; +	class UIEventStream; +	class AdHocCommandWindowFactory; +	class OutgoingAdHocCommandSession { +		public: +			OutgoingAdHocCommandSession(const DiscoItems::Item& command, AdHocCommandWindowFactory* factory, IQRouter* iqRouter); +			/** +			 * Send initial request to the target. +			 */ +			void start(); +			/** +			 * Cancel command session with the target. +			 */ +			void cancel(); +			/** +			 * Return to the previous stage. +			 */ +			void goBack(); +			/** +			 * Send the form to complete the command. +			 * \param form Form for submission - if missing the command will be submitted with no form. +			 */ +			void complete(Form::ref form); +			/** +			 * Send the form to advance to the next stage of the command. +			 * \param form Form for submission - if missing the command will be submitted with no form. +			 */ +			void goNext(Form::ref form); + +			/** +			 * Is the form multi-stage? +			 */ +			bool getIsMultiStage(); + +			/** +			 * Emitted when the form for the next stage is available. +			 */ +			boost::signal<void (Command::ref)> onNextStageReceived; + +			/** +			 * Emitted on error. +			 */ +			boost::signal<void (ErrorPayload::ref)> onError; +		private: +			void handleResponse(boost::shared_ptr<Command> payload, ErrorPayload::ref error); +		private: +			DiscoItems::Item command_; +			IQRouter* iqRouter_; +			bool isMultiStage_; +			std::string sessionID_; +	}; +} diff --git a/Swiften/AdHoc/SConscript b/Swiften/AdHoc/SConscript new file mode 100644 index 0000000..69c9083 --- /dev/null +++ b/Swiften/AdHoc/SConscript @@ -0,0 +1,6 @@ +Import("swiften_env") + +objects = swiften_env.SwiftenObject([ +			"OutgoingAdHocCommandSession.cpp", +		]) +swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/Disco/GetDiscoItemsRequest.h b/Swiften/Disco/GetDiscoItemsRequest.h index 0a94402..46735ef 100644 --- a/Swiften/Disco/GetDiscoItemsRequest.h +++ b/Swiften/Disco/GetDiscoItemsRequest.h @@ -18,9 +18,18 @@ namespace Swift {  				return ref(new GetDiscoItemsRequest(jid, router));  			} +			static ref create(const JID& jid, const std::string& node, IQRouter* router) { +				return ref(new GetDiscoItemsRequest(jid, node, router)); +			} +  		private:  			GetDiscoItemsRequest(const JID& jid, IQRouter* router) :  					GenericRequest<DiscoItems>(IQ::Get, jid, boost::shared_ptr<DiscoItems>(new DiscoItems()), router) {  			} + +			GetDiscoItemsRequest(const JID& jid, const std::string& node, IQRouter* router) : +				GenericRequest<DiscoItems>(IQ::Get, jid, boost::shared_ptr<DiscoItems>(new DiscoItems()), router) { +				getPayloadGeneric()->setNode(node); +			}  	};  } diff --git a/Swiften/Elements/DiscoInfo.cpp b/Swiften/Elements/DiscoInfo.cpp index 158ea66..5b2bb04 100644 --- a/Swiften/Elements/DiscoInfo.cpp +++ b/Swiften/Elements/DiscoInfo.cpp @@ -14,6 +14,7 @@ const std::string DiscoInfo::ChatStatesFeature = std::string("http://jabber.org/  const std::string DiscoInfo::SecurityLabelsFeature = std::string("urn:xmpp:sec-label:0");  const std::string DiscoInfo::SecurityLabelsCatalogFeature = std::string("urn:xmpp:sec-label:catalog:2");  const std::string DiscoInfo::JabberSearchFeature = std::string("jabber:iq:search"); +const std::string DiscoInfo::CommandsFeature = std::string("http://jabber.org/protocol/commands");  bool DiscoInfo::Identity::operator<(const Identity& other) const { diff --git a/Swiften/Elements/DiscoInfo.h b/Swiften/Elements/DiscoInfo.h index 6144b16..cd650b9 100644 --- a/Swiften/Elements/DiscoInfo.h +++ b/Swiften/Elements/DiscoInfo.h @@ -21,6 +21,7 @@ namespace Swift {  			static const std::string SecurityLabelsFeature;  			static const std::string SecurityLabelsCatalogFeature;  			static const std::string JabberSearchFeature; +			static const std::string CommandsFeature;  			class Identity {  				public: diff --git a/Swiften/SConscript b/Swiften/SConscript index 7c1f70f..5536b3f 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -190,6 +190,7 @@ if env["SCONS_STAGE"] == "build" :  			"StreamManagement",  			"Component",  			"Config", +			"AdHoc"  		])  	SConscript(test_only = True, dirs = [  			"QA", diff --git a/Swiften/Serializer/PayloadSerializers/CommandSerializer.cpp b/Swiften/Serializer/PayloadSerializers/CommandSerializer.cpp index 0fa45ce..12a38a8 100644 --- a/Swiften/Serializer/PayloadSerializers/CommandSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/CommandSerializer.cpp @@ -21,7 +21,7 @@ CommandSerializer::CommandSerializer() {  }  std::string CommandSerializer::serializePayload(boost::shared_ptr<Command> command)	const { -	XMLElement commandElement("command", "http://jabber.org/protocol/comands"); +	XMLElement commandElement("command", "http://jabber.org/protocol/commands");  	commandElement.setAttribute("node", command->getNode());  	if (!command->getSessionID().empty()) {  | 
 Swift