summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2010-11-18 17:01:22 (GMT)
committerKevin Smith <git@kismith.co.uk>2011-04-24 19:01:16 (GMT)
commit522a3a1c66233792741eb47f75e009a2fe9fa3ad (patch)
tree580673097cef48e58441f0adbef0462b6d0a8cbf
parente3041b220e4846fb2f0273c720d8bcbd78cafaaf (diff)
downloadswift-522a3a1c66233792741eb47f75e009a2fe9fa3ad.zip
swift-522a3a1c66233792741eb47f75e009a2fe9fa3ad.tar.bz2
Initial Ad-Hoc commands support.
This is not finished, use at your own peril.
-rw-r--r--Swift/Controllers/AdHocManager.cpp66
-rw-r--r--Swift/Controllers/AdHocManager.h37
-rw-r--r--Swift/Controllers/MainController.cpp7
-rw-r--r--Swift/Controllers/MainController.h3
-rw-r--r--Swift/Controllers/SConscript1
-rw-r--r--Swift/Controllers/UIEvents/RequestAdHocUIEvent.h21
-rw-r--r--Swift/Controllers/UIInterfaces/AdHocCommandWindow.h14
-rw-r--r--Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h22
-rw-r--r--Swift/Controllers/UIInterfaces/MainWindow.h2
-rw-r--r--Swift/Controllers/UIInterfaces/UIFactory.h4
-rw-r--r--Swift/Controllers/UnitTest/MockMainWindow.h1
-rw-r--r--Swift/QtUI/QtAdHocCommandWindow.cpp130
-rw-r--r--Swift/QtUI/QtAdHocCommandWindow.h47
-rw-r--r--Swift/QtUI/QtFormWidget.cpp248
-rw-r--r--Swift/QtUI/QtFormWidget.h31
-rw-r--r--Swift/QtUI/QtMainWindow.cpp36
-rw-r--r--Swift/QtUI/QtMainWindow.h8
-rw-r--r--Swift/QtUI/QtUIFactory.cpp4
-rw-r--r--Swift/QtUI/QtUIFactory.h1
-rw-r--r--Swift/QtUI/SConscript2
-rw-r--r--Swiften/AdHoc/OutgoingAdHocCommandSession.cpp77
-rw-r--r--Swiften/AdHoc/OutgoingAdHocCommandSession.h68
-rw-r--r--Swiften/AdHoc/SConscript6
-rw-r--r--Swiften/Disco/GetDiscoItemsRequest.h9
-rw-r--r--Swiften/Elements/DiscoInfo.cpp1
-rw-r--r--Swiften/Elements/DiscoInfo.h1
-rw-r--r--Swiften/SConscript1
-rw-r--r--Swiften/Serializer/PayloadSerializers/CommandSerializer.cpp2
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()) {