*************** *** 0 **** --- 1,1041 ---- + /* + * adhoc.cpp - Client & Server implementation of JEP-50 (Ad-Hoc Commands) + * Copyright (C) 2005 Remko Troncon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + #include + #include + #include + #include + + #include "adhoc.h" + #include "psiaccount.h" + + #include "xmpp_xmlcommon.h" + #include "xmpp_tasks.h" + #include "xmpp_xdata.h" + #include "xdata_widget.h" + + using namespace XMPP; + + #define AHC_NS "http://jabber.org/protocol/commands" + #define XMPPSTANZA_NS "urn:ietf:params:xml:ns:xmpp-stanzas" + + // -------------------------------------------------------------------------- + // AHCommand: The class representing an Ad-Hoc command request or reply. + // -------------------------------------------------------------------------- + + AHCommand::AHCommand(const QString& node, const QString& sessionId, Action action) : node_(node), hasData_(false), status_(NoStatus), defaultAction_(NoAction), action_(action), sessionId_(sessionId) + { + } + + AHCommand::AHCommand(const QString& node, XData data, const QString& sessionId, Action action) : node_(node), hasData_(true), data_(data), status_(NoStatus), defaultAction_(NoAction), action_(action), sessionId_(sessionId) + { + } + + AHCommand::AHCommand(const QDomElement& q) : hasData_(false), defaultAction_(NoAction) + { + // Parse attributes + QString status = q.attribute("status"); + setStatus(string2status(status)); + node_ = q.attribute("node"); + action_ = string2action(q.attribute("action")); + sessionId_ = q.attribute("sessionid"); + + // Parse the body + for (QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { + QDomElement e = n.toElement(); + if (e.isNull()) + continue; + + QString tag = e.tagName(); + + // A form + if (tag == "x" && e.attribute("xmlns") =="jabber:x:data") { + data_.fromXml(e); + hasData_ = true; + } + + // Actions + else if (tag == "actions") { + QString execute = e.attribute("execute"); + if (!execute.isEmpty()) + setDefaultAction(string2action(execute)); + + for (QDomNode m = e.firstChild(); !m.isNull(); m = m.nextSibling()) { + Action a = string2action(m.toElement().tagName()); + if (a == Prev || a == Next || a == Complete) + actions_ += a; + } + } + } + } + + QDomElement AHCommand::toXml(QDomDocument* doc, bool submit) const + { + QDomElement command = doc->createElement("command"); + command.setAttribute("xmlns", AHC_NS); + if (status_ != NoStatus) + command.setAttribute("status",status2string(status())); + if (hasData()) + command.appendChild(data().toXml(doc, submit)); + if (action_ != Execute) + command.setAttribute("action",action2string(action_)); + command.setAttribute("node", node_); + if (!sessionId_.isEmpty()) + command.setAttribute("sessionid", sessionId_); + + return command; + } + + + AHCommand AHCommand::formReply(const AHCommand& c, const XData& data) + { + AHCommand r(c.node(), data, c.sessionId()); + r.setStatus(AHCommand::Executing); + return r; + } + + AHCommand AHCommand::formReply(const AHCommand& c, const XData& data, const QString& sessionId) + { + AHCommand r(c.node(), data, sessionId); + r.setStatus(AHCommand::Executing); + return r; + } + + AHCommand AHCommand::canceledReply(const AHCommand& c) + { + AHCommand r(c.node(), c.sessionId()); + r.setStatus(Canceled); + return r; + } + + AHCommand AHCommand::completedReply(const AHCommand& c) + { + AHCommand r(c.node(), c.sessionId()); + r.setStatus(Completed); + return r; + } + + AHCommand AHCommand::completedReply(const AHCommand& c, const XData& d) + { + AHCommand r(c.node(), d, c.sessionId()); + r.setStatus(Completed); + return r; + } + + //AHCommand AHCommand::errorReply(const AHCommand& c, const AHCError& error) + //{ + // AHCommand r(c.node(), c.sessionId()); + // r.setError(error); + // return r; + //} + + void AHCommand::setStatus(Status s) + { + status_ = s; + } + + void AHCommand::setError(const AHCError& e) + { + error_ = e; + } + + void AHCommand::setDefaultAction(Action a) + { + defaultAction_ = a; + } + + QString AHCommand::status2string(Status status) + { + QString s; + switch (status) { + case Executing : s = "executing"; break; + case Completed : s = "completed"; break; + case Canceled : s = "canceled"; break; + case NoStatus : s = ""; break; + } + return s; + } + + QString AHCommand::action2string(Action action) + { + QString s; + switch (action) { + case Prev : s = "prev"; break; + case Next : s = "next"; break; + case Cancel : s = "cancel"; break; + case Complete : s = "complete"; break; + default: break; + } + return s; + } + + AHCommand::Action AHCommand::string2action(const QString& s) + { + if (s == "prev") + return Prev; + else if (s == "next") + return Next; + else if (s == "complete") + return Complete; + else if (s == "cancel") + return Cancel; + else + return Execute; + } + + AHCommand::Status AHCommand::string2status(const QString& s) + { + if (s == "canceled") + return Canceled; + else if (s == "completed") + return Completed; + else if (s == "executing") + return Executing; + else + return NoStatus; + } + + + // -------------------------------------------------------------------------- + // AHCError: The class representing an Ad-Hoc command error + // -------------------------------------------------------------------------- + + AHCError::AHCError(ErrorType t) : type_(t) + { + } + + AHCError::AHCError(const QDomElement& e) : type_(None) + { + QString errorGeneral = "", errorSpecific = ""; + + for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { + QDomElement i = n.toElement(); + if(i.isNull()) + continue; + + QString tag = i.tagName(); + + if ((tag == "bad-request" || tag == "not-allowed" || tag == "forbidden" || tag == "forbidden" || tag == "item-not-found" || tag == "feature-not-implemented") && e.attribute("xmlns") == XMPPSTANZA_NS) { + errorGeneral = tag; + } + else if ((tag == "malformed-action" || tag == "bad-action" || tag == "bad-locale" || tag == "bad-payload" || tag == "bad-sessionid" || tag == "session-expired") && e.attribute("xmlns") == AHC_NS) { + errorSpecific = tag; + } + } + + type_ = strings2error(errorGeneral, errorSpecific); + } + + QDomElement AHCError::toXml(QDomDocument* doc) const + { + QDomElement err = doc->createElement("error"); + + // Error handling + if (type_ != None) { + QString desc, specificCondition = ""; + switch (type_) { + case MalformedAction: + desc = "bad-request"; + specificCondition = "malformed-action"; + break; + case BadAction: + desc = "bad-request"; + specificCondition = "bad-action"; + break; + case BadLocale: + desc = "bad-request"; + specificCondition = "bad-locale"; + break; + case BadPayload: + desc = "bad-request"; + specificCondition = "bad-payload"; + break; + case BadSessionID: + desc = "bad-request"; + specificCondition = "bad-sessionid"; + break; + case SessionExpired: + desc = "not-allowed"; + specificCondition = "session-expired"; + break; + case Forbidden: + desc = "forbidden"; + break; + case ItemNotFound: + desc = "item-not-found"; + break; + case FeatureNotImplemented: + desc = "feature-not-implemented"; + break; + case None: + break; + } + + // General error condition + QDomElement generalElement = doc->createElement(desc); + generalElement.setAttribute("xmlns", XMPPSTANZA_NS); + err.appendChild(generalElement); + + // Specific error condition + if (!specificCondition.isEmpty()) { + QDomElement generalElement = doc->createElement(specificCondition); + generalElement.setAttribute("xmlns", AHC_NS); + err.appendChild(generalElement); + } + } + + return err; + } + + AHCError::ErrorType AHCError::strings2error(const QString& g, const QString& s) + { + if (s == "malformed-action") + return MalformedAction; + if (s == "bad-action") + return BadAction; + if (s == "bad-locale") + return BadLocale; + if (s == "bad-payload") + return BadPayload; + if (s == "bad-sessionid") + return BadSessionID; + if (s == "session-expired") + return SessionExpired; + if (g == "forbidden") + return Forbidden; + if (g == "item-not-found") + return ItemNotFound; + if (g == "feature-not-implemented") + return FeatureNotImplemented; + return None; + } + + QString AHCError::error2description(const AHCError& e) + { + QString desc; + switch (e.type()) { + case MalformedAction: + desc = QString("The responding JID does not understand the specified action"); + break; + case BadAction: + desc = QString("The responding JID cannot accept the specified action"); + break; + case BadLocale: + desc = QString("The responding JID cannot accept the specified language/locale"); + break; + case BadPayload: + desc = QString("The responding JID cannot accept the specified payload (eg the data form did not provide one or more required fields)"); + break; + case BadSessionID: + desc = QString("The responding JID cannot accept the specified sessionid"); + break; + case SessionExpired: + desc = QString("The requesting JID specified a sessionid that is no longer active (either because it was completed, canceled, or timed out)"); + break; + case Forbidden: + desc = QString("The requesting JID is not allowed to execute the command"); + break; + case ItemNotFound: + desc = QString("The responding JID cannot find the requested command node"); + break; + case FeatureNotImplemented: + desc = QString("The responding JID does not support Ad-hoc commands"); + break; + case None: + break; + } + return desc; + } + + // -------------------------------------------------------------------------- + // AHCommandServer: The server-side implementation of an Ad-hoc command. + // -------------------------------------------------------------------------- + + AHCommandServer::AHCommandServer(AHCServerManager* manager) : manager_(manager) + { + manager_->addServer(this); + } + + + // -------------------------------------------------------------------------- + // JT_AHCServer: Task to handle ad-hoc command requests + // -------------------------------------------------------------------------- + + class JT_AHCServer : public Task + { + Q_OBJECT + + public: + JT_AHCServer(Task*, AHCServerManager*); + bool take(const QDomElement& e); + void sendReply(const AHCommand&, const Jid& to, const QString& id); + + protected: + bool commandListQuery(const QDomElement& e); + bool commandExecuteQuery(const QDomElement& e); + void sendCommandList(const QString& to, const QString& from, const QString& id); + + private: + AHCServerManager* manager_; + }; + + + JT_AHCServer::JT_AHCServer(Task* t, AHCServerManager* manager) : Task(t), manager_(manager) + { + } + + bool JT_AHCServer::take(const QDomElement& e) + { + // Check if it's a query + if (e.tagName() != "iq") + return false; + + return (commandListQuery(e) || commandExecuteQuery(e)); + } + + bool JT_AHCServer::commandListQuery(const QDomElement& e) + { + if (e.attribute("type") == "get") { + bool found; + QDomElement q = findSubTag(e, "query", &found); + if (found && q.attribute("xmlns") == "http://jabber.org/protocol/disco#items" && q.attribute("node") == AHC_NS) { + sendCommandList(e.attribute("from"),e.attribute("to"),e.attribute("id")); + return true; + } + else if (found && q.attribute("xmlns") == "http://jabber.org/protocol/disco#info" && q.attribute("node") == AHC_NS) { + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info"); + iq.appendChild(query); + + send(iq); + return true; + } + else if (found && q.attribute("xmlns") == "http://jabber.org/protocol/disco#items" && manager_->hasServer(q.attribute("node"), Jid(e.attribute("from")))) { + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items"); + query.setAttribute("node",q.attribute("node")); + iq.appendChild(query); + + send(iq); + return true; + } + else if (found && q.attribute("xmlns") == "http://jabber.org/protocol/disco#info" && manager_->hasServer(q.attribute("node"), Jid(e.attribute("from")))) { + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info"); + query.setAttribute("node",q.attribute("node")); + iq.appendChild(query); + QDomElement identity; + QDomElement feature; + + identity = doc()->createElement("identity"); + identity.setAttribute("category", "automation"); + identity.setAttribute("type", "command-node"); + query.appendChild(identity); + + feature = doc()->createElement("feature"); + feature.setAttribute("var", AHC_NS); + query.appendChild(feature); + + feature = doc()->createElement("feature"); + feature.setAttribute("var", "jabber:x:data"); + query.appendChild(feature); + + send(iq); + return true; + } + + } + + return false; + } + + bool JT_AHCServer::commandExecuteQuery(const QDomElement& e) + { + if (e.attribute("type") == "set") { + bool found; + QDomElement q = findSubTag(e, "command", &found); + if (found && q.attribute("xmlns") == AHC_NS) { + if (manager_->hasServer(q.attribute("node"), Jid(e.attribute("from")))) { + AHCommand command(q); + manager_->execute(command, Jid(e.attribute("from")), e.attribute("id")); + return true; + } + return false; + } + else + return false; + } + return false; + } + + void JT_AHCServer::sendCommandList(const QString& to, const QString& from, const QString& id) + { + // Create query element + QDomElement iq = createIQ(doc(), "result", to, id); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items"); + query.setAttribute("node", AHC_NS); + iq.appendChild(query); + + // Add all commands + AHCServerManager::ServerList l = manager_->commands(Jid(to)); + for (AHCommandServer* c = l.first(); c; c = l.next()) { + QDomElement command = doc()->createElement("item"); + command.setAttribute("jid",from); + command.setAttribute("name",c->name()); + command.setAttribute("node",c->node()); + query.appendChild(command); + } + + // Send the message + send(iq); + } + + void JT_AHCServer::sendReply(const AHCommand& c, const Jid& to, const QString& id) + { + // if (c.error().type() != AHCError::None) { + QDomElement iq = createIQ(doc(), "result", to.full(), id); + QDomElement command = c.toXml(doc(), false); + iq.appendChild(command); + send(iq); + // } + // else { + // } + } + + // -------------------------------------------------------------------------- + + AHCServerManager::AHCServerManager(PsiAccount *pa) : pa_(pa) + { + server_task_ = new JT_AHCServer(pa_->client()->rootTask(), this); + } + + void AHCServerManager::addServer(AHCommandServer* server) + { + servers_.append(server); + } + + AHCServerManager::ServerList AHCServerManager::commands(const Jid& j) const + { + ServerList list; + for (ServerList::ConstIterator it = servers_.begin(); it != servers_.end(); ++it) { + if ((*it)->isAllowed(j)) + list.append(*it); + } + return list; + } + + + void AHCServerManager::execute(const AHCommand& command, const Jid& requester, QString id) + { + AHCommandServer* c = findServer(command.node()); + + // Check if the command is provided + if (!c) { + //server_task_->sendReply(AHCommand::errorReply(command,AHCError(AHCError::ItemNotFound)), requester, id); + return; + } + + // Check if the requester is allowed to execute the command + if (c->isAllowed(requester)) { + if (command.action() == AHCommand::Cancel) { + c->cancel(command); + server_task_->sendReply(AHCommand::canceledReply(command), requester, id); + } + else + // Execute the command & send back the response + server_task_->sendReply(c->execute(command, requester), requester, id); + } + else { + //server_task_->sendReply(AHCommand::errorReply(command,AHCError(AHCError::Forbidden)), requester, id); + return; + } + } + + + AHCommandServer* AHCServerManager::findServer(const QString& node) const + { + for (ServerList::ConstIterator it = servers_.begin(); it != servers_.end(); ++it) { + if ((*it)->node() == node) + return (*it); + } + return 0; + } + + bool AHCServerManager::hasServer(const QString& node, const Jid& requester) const + { + AHCommandServer* c = findServer(node); + + // Check if the command is provided + if (!c) { + return false; + } + + if (c->isAllowed(requester)) { + return true; + } + else { + return false; + } + } + + + // -------------------------------------------------------------------------- + + bool operator<(const AHCommandItem &ci1, const AHCommandItem &ci2) + { + return ci1.name < ci2.name; + } + + bool operator<=(const AHCommandItem &ci1, const AHCommandItem &ci2) + { + return ci1.name <= ci2.name; + } + + bool operator==(const AHCommandItem &ci1, const AHCommandItem &ci2) + { + return ci1.name == ci2.name; + } + + bool operator>(const AHCommandItem &ci1, const AHCommandItem &ci2) + { + return ci1.name > ci2.name; + } + + bool operator>=(const AHCommandItem &ci1, const AHCommandItem &ci2) + { + return ci1.name >= ci2.name; + } + + // -------------------------------------------------------------------------- + // JT_AHCGetList: A Task to retreive the available commands of a client + // -------------------------------------------------------------------------- + + class JT_AHCGetList : public Task + { + public: + JT_AHCGetList(Task* t, const Jid& j); + + void onGo(); + bool take(const QDomElement &x); + + const QValueList& commands() const { return commands_; } + + private: + Jid receiver_; + QValueList commands_; + }; + + + JT_AHCGetList::JT_AHCGetList(Task* t, const Jid& j) : Task(t), receiver_(j) + { + } + + void JT_AHCGetList::onGo() + { + QDomElement e = createIQ(doc(), "get", receiver_.full(), id()); + QDomElement q = doc()->createElement("query"); + q.setAttribute("xmlns", "http://jabber.org/protocol/disco#items"); + q.setAttribute("node", AHC_NS); + e.appendChild(q); + send(e); + } + + bool JT_AHCGetList::take(const QDomElement& e) + { + if(!iqVerify(e, receiver_, id())) { + return false; + } + + if (e.attribute("type") == "result") { + // Extract commands + commands_.clear(); + bool found; + QDomElement commands = findSubTag(e, "query", &found); + if(found) { + for(QDomNode n = commands.firstChild(); !n.isNull(); n = n.nextSibling()) { + QDomElement i = n.toElement(); + if(i.isNull()) + continue; + + if(i.tagName() == "item") { + AHCommandItem ci; + ci.node = i.attribute("node"); + ci.name = i.attribute("name"); + ci.jid = i.attribute("jid"); + commands_ += ci; + } + } + qHeapSort(commands_); + } + setSuccess(); + return true; + } + else { + setError(e); + return false; + } + } + + + // -------------------------------------------------------------------------- + + // -------------------------------------------------------------------------- + // JT_AHCExecute: A task to execute a command + // -------------------------------------------------------------------------- + + class JT_AHCExecute : public Task + { + public: + JT_AHCExecute(Task* t, PsiAccount* pa, Jid j, const AHCommand&); + + void onGo(); + bool take(const QDomElement &x); + + private: + Jid receiver_; + PsiAccount* account_; + AHCommand command_; + }; + + + JT_AHCExecute::JT_AHCExecute(Task* t, PsiAccount* pa, Jid j, const AHCommand& command) : Task(t), receiver_(j), account_(pa), command_(command) + { + } + + void JT_AHCExecute::onGo() + { + QDomElement e = createIQ(doc(), "set", receiver_.full(), id()); + e.appendChild(command_.toXml(doc(),true)); + send(e); + } + + bool JT_AHCExecute::take(const QDomElement& e) + { + if(!iqVerify(e, receiver_, id())) { + return false; + } + + // Result of a command + if (e.attribute("type") == "result") { + bool found; + QDomElement i = findSubTag(e, "command", &found); + if (found) { + AHCommand c(i); + if (c.status() == AHCommand::Executing) { + AHCFormDlg *w = new AHCFormDlg(c,receiver_,account_); + w->show(); + } + else if (c.status() == AHCommand::Completed && i.childNodes().count() > 0) { + AHCFormDlg *w = new AHCFormDlg(c,receiver_,account_, true); + w->show(); + } + setSuccess(); + return true; + } + } + // Error + /*else if (e.attribute("type") == "set") { + AHCError err(e); + if (err.type() != None) { + QMessageBox::critical(0, tr("Error"), AHCommand::error2description(err.type()), QMessageBox::Ok, QMessageBox::NoButton); + } + return true; + }*/ + setError(e); + return false; + } + + // -------------------------------------------------------------------------- + + // -------------------------------------------------------------------------- + // AHCommandDlg + // -------------------------------------------------------------------------- + + AHCommandDlg::AHCommandDlg(PsiAccount* pa, const Jid& receiver) : pa_(pa), receiver_(receiver) + { + QVBoxLayout *vb = new QVBoxLayout(this, 11, 6); + + // Command list + Buttons + QLabel* lb_commands = new QLabel(tr("Command:"),this); + vb->addWidget(lb_commands); + QHBoxLayout *hb1 = new QHBoxLayout(vb); + cb_commands = new QComboBox(this); + cb_commands->setSizePolicy ( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + hb1->addWidget(cb_commands); + /*pb_info = new QPushButton(tr("Info"), this); + hb1->addWidget(pb_info);*/ + pb_execute = new QPushButton(tr("Execute"), this); + hb1->addWidget(pb_execute); + connect(pb_execute, SIGNAL(clicked()), SLOT(executeCommand())); + + vb->addStretch(1); + + // Bottom row + QHBoxLayout *hb2 = new QHBoxLayout(vb); + + busy = new BusyWidget(this); + hb2->addWidget(busy); + hb2->addStretch(1); + + // Refresh button + pb_refresh = new QPushButton(tr("Refresh"), this); + hb2->addWidget(pb_refresh); + connect(pb_refresh, SIGNAL(clicked()), SLOT(refreshCommands())); + + // Close button + pb_close = new QPushButton(tr("Close"), this); + hb2->addWidget(pb_close); + connect(pb_close, SIGNAL(clicked()), SLOT(close())); + pb_close->setDefault(true); + pb_close->setFocus(); + + setCaption(QString("Execute Command (%1)").arg(receiver.full())); + + // Initialize other things + task_list_ = 0; + + // Load commands + refreshCommands(); + } + + + void AHCommandDlg::refreshCommands() + { + cb_commands->clear(); + pb_execute->setEnabled(false); + //pb_info->setEnabled(false); + busy->start(); + if (task_list_) + disconnect(task_list_,SIGNAL(finished()),this,SLOT(listReceived())); + + task_list_= new JT_AHCGetList(pa_->client()->rootTask(),receiver_); + connect(task_list_,SIGNAL(finished()),SLOT(listReceived())); + task_list_->go(true); + } + + void AHCommandDlg::listReceived() + { + QValueList l = task_list_->commands(); + QValueList::ConstIterator it; + for (it = l.begin(); it != l.end(); ++it) { + cb_commands->insertItem((*it).name); + commands_.append(*it); + } + pb_execute->setEnabled(cb_commands->count()>0); + busy->stop(); + + // FIXME delete task_list_ ?; + task_list_ = 0; + } + + void AHCommandDlg::executeCommand() + { + if (cb_commands->count() > 0) { + busy->start(); + Jid to(commands_[cb_commands->currentItem()].jid); + QString node = commands_[cb_commands->currentItem()].node; + JT_AHCExecute* t = new JT_AHCExecute(pa_->client()->rootTask(),pa_,to,AHCommand(node)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); + } + } + + void AHCommandDlg::commandExecuted() + { + busy->stop(); + close(); + } + + void AHCommandDlg::executeCommand(PsiAccount* pa, const Jid& j, const QString &node) + { + JT_AHCExecute* t = new JT_AHCExecute(pa->client()->rootTask(),pa,j,AHCommand(node)); + t->go(true); + } + + // -------------------------------------------------------------------------- + + AHCFormDlg::AHCFormDlg(const AHCommand& r, const Jid& receiver, PsiAccount* account, bool final) : receiver_(receiver), account_(account) + { + // Save node + node_ = r.node(); + sessionId_ = r.sessionId(); + + QVBoxLayout *vb = new QVBoxLayout(this, 11, 6); + + // Instructions + if (!r.data().instructions().isEmpty()) { + QLabel* lb_instructions = new QLabel(r.data().instructions(),this); + vb->addWidget(lb_instructions); + } + + // XData form + xdata_ = new XDataWidget(this); + xdata_->setFields(r.data().fields()); + vb->addWidget(xdata_); + + vb->addStretch(1); + + // Buttons + QHBoxLayout *hb = new QHBoxLayout(vb); + pb_complete = pb_cancel = pb_prev = pb_next = 0; + if (!final) { + + busy = new BusyWidget(this); + hb->addWidget(busy); + hb->addStretch(1); + + if (r.actions().empty()) { + // Single stage dialog + pb_complete = new QPushButton(tr("Finish"),this); + connect(pb_complete,SIGNAL(clicked()),SLOT(doExecute())); + hb->addWidget(pb_complete); + } + else { + // Multi-stage dialog + + // Previous + pb_prev = new QPushButton(tr("Previous"),this); + if (r.actions().contains(AHCommand::Prev)) { + if (r.defaultAction() == AHCommand::Prev) { + connect(pb_prev,SIGNAL(clicked()),SLOT(doExecute())); + pb_prev->setDefault(true); + pb_prev->setFocus(); + } + else + connect(pb_prev,SIGNAL(clicked()),SLOT(doPrev())); + pb_prev->setEnabled(true); + } + else + pb_prev->setEnabled(false); + hb->addWidget(pb_prev); + + // Next + pb_next = new QPushButton(tr("Next"),this); + if (r.actions().contains(AHCommand::Next)) { + if (r.defaultAction() == AHCommand::Next) { + connect(pb_next,SIGNAL(clicked()),SLOT(doExecute())); + pb_next->setDefault(true); + pb_next->setFocus(); + } + else + connect(pb_next,SIGNAL(clicked()),SLOT(doNext())); + pb_next->setEnabled(true); + } + else { + pb_next->setEnabled(false); + } + hb->addWidget(pb_next); + + // Complete + pb_complete = new QPushButton(tr("Finish"),this); + if (r.actions().contains(AHCommand::Complete)) { + if (r.defaultAction() == AHCommand::Complete) { + connect(pb_complete,SIGNAL(clicked()),SLOT(doExecute())); + pb_complete->setDefault(true); + pb_complete->setFocus(); + } + else + connect(pb_complete,SIGNAL(clicked()),SLOT(doComplete())); + pb_complete->setEnabled(true); + } + else { + pb_complete->setEnabled(false); + } + hb->addWidget(pb_complete); + } + pb_cancel = new QPushButton(tr("Cancel"), this); + connect(pb_cancel, SIGNAL(clicked()),SLOT(doCancel())); + hb->addWidget(pb_cancel); + } + else { + pb_complete = new QPushButton(tr("Ok"),this); + connect(pb_complete,SIGNAL(clicked()),SLOT(close())); + hb->addWidget(pb_complete); + } + + if (!r.data().title().isEmpty()) { + setCaption(QString("%1 (%2)").arg(r.data().title()).arg(receiver.full())); + } + else { + setCaption(QString("%1").arg(receiver.full())); + } + } + + void AHCFormDlg::doPrev() + { + busy->start(); + JT_AHCExecute* t = new JT_AHCExecute(account_->client()->rootTask(),account_,receiver_,AHCommand(node_,data(),sessionId_,AHCommand::Prev)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); + } + + void AHCFormDlg::doNext() + { + busy->start(); + JT_AHCExecute* t = new JT_AHCExecute(account_->client()->rootTask(),account_,receiver_,AHCommand(node_,data(),sessionId_,AHCommand::Next)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); + } + + void AHCFormDlg::doExecute() + { + busy->start(); + JT_AHCExecute* t = new JT_AHCExecute(account_->client()->rootTask(),account_,receiver_,AHCommand(node_,data(),sessionId_)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); + } + + void AHCFormDlg::doComplete() + { + busy->start(); + JT_AHCExecute* t = new JT_AHCExecute(account_->client()->rootTask(),account_,receiver_,AHCommand(node_,data(),sessionId_,AHCommand::Complete)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); + } + + void AHCFormDlg::doCancel() + { + busy->start(); + JT_AHCExecute* t = new JT_AHCExecute(account_->client()->rootTask(),account_,receiver_,AHCommand(node_,sessionId_,AHCommand::Cancel)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); + } + + void AHCFormDlg::commandExecuted() + { + busy->stop(); + close(); + } + + XData AHCFormDlg::data() const + { + XData x; + x.setFields(xdata_->fields()); + x.setType(XData::Data_Submit); + return x; + } + + // -------------------------------------------------------------------------- + + #include "adhoc.moc"