Kross

action.cpp
1 /***************************************************************************
2  * action.cpp
3  * This file is part of the KDE project
4  * copyright (C)2004-2006 by Sebastian Sauer ([email protected])
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Library General Public License for more details.
14  * You should have received a copy of the GNU Library General Public License
15  * along with this program; see the file COPYING. If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  ***************************************************************************/
19 
20 #include "action.h"
21 #include "actioncollection.h"
22 #include "interpreter.h"
23 #include "script.h"
24 #include "manager.h"
25 #include "wrapperinterface.h"
26 #include "kross_debug.h"
27 
28 #include <QFile>
29 #include <QFileInfo>
30 
31 #include <klocalizedstring.h>
32 #include <qmimedatabase.h>
33 
34 using namespace Kross;
35 
36 namespace Kross
37 {
38 
39 /// \internal d-pointer class.
40 class Action::Private
41 {
42 public:
43 
44  /**
45  * The \a Script instance the \a Action uses if initialized. It will
46  * be NULL as long as we didn't initialized it what will be done on
47  * demand.
48  */
49  Script *script;
50 
51  /**
52  * The version number this \a Action has. Those version number got
53  * used internally to deal with different releases of scripts.
54  */
55  int version;
56 
57  /**
58  * The optional description to provide some more details about the
59  * Action to the user.
60  */
62 
63  /**
64  * The name of the icon.
65  */
66  QString iconname;
67 
68  /**
69  * The scripting code.
70  */
72 
73  /**
74  * The name of the interpreter. This could be something
75  * like for example "python" for the python binding.
76  */
77  QString interpretername;
78 
79  /**
80  * The name of the scriptfile that should be executed. Those
81  * scriptfile will be read and the content will be used to
82  * set the scripting code and, if not defined, the used
83  * interpreter.
84  */
85  QString scriptfile;
86 
87  /**
88  * The path list where the \a Script may be located.
89  * \todo after BIC break: don't keep it all the time,
90  * as it is now passed to [to|from]DomElement
91  */
92  QStringList searchpath;
93 
94  /**
95  * Map of options that overwritte the \a InterpreterInfo::Option::Map
96  * standard options.
97  */
99 
100  Private() : script(nullptr), version(0) {}
101 };
102 
103 }
104 
105 enum InitOptions {Enable = 1};
106 void static init(Action *th, const QString &name, int options = 0)
107 {
108  th->setEnabled(options & Enable);
109  th->setObjectName(name);
110 #ifdef KROSS_ACTION_DEBUG
111  qCDebug(KROSS_LOG) << "Action::Action(QObject*,QString,QDir) Ctor name=" << th->objectName();
112 #endif
113  QObject::connect(th, SIGNAL(triggered(bool)), th, SLOT(slotTriggered()));
114 }
115 
116 Action::Action(QObject *parent, const QString &name, const QDir &packagepath)
117  : QAction(parent)
118  , QScriptable()
120  , ErrorInterface()
121  , d(new Private())
122 {
123  init(this, name);
124  d->searchpath = QStringList(packagepath.absolutePath());
125 }
126 
127 Action::Action(QObject *parent, const QUrl &url)
128  : QAction(parent)
130  , ErrorInterface()
131  , d(new Private())
132 {
133  init(this, url.path(), Enable);
134  QFileInfo fi(url.toLocalFile());
135  setText(fi.fileName());
136  QMimeDatabase db;
138  setFile(url.toLocalFile());
139 }
140 
142 {
143 #ifdef KROSS_ACTION_DEBUG
144  qCDebug(KROSS_LOG) << QStringLiteral("Action::~Action() Dtor name='%1'").arg(objectName());
145 #endif
146  finalize();
147  ActionCollection *coll = qobject_cast<ActionCollection *>(parent());
148  if (coll) {
149  coll->removeAction(this);
150  }
151  delete d;
152 }
153 
155 {
156  fromDomElement(element, d->searchpath);
157 }
158 
159 void Action::fromDomElement(const QDomElement &element, const QStringList &searchPath)
160 {
161  if (element.isNull()) {
162  return;
163  }
164 
165  QString file = element.attribute("file");
166  if (! file.isEmpty()) {
167  if (QFileInfo(file).exists()) {
168  setFile(file);
169  } else {
170  foreach (const QString &packagepath, searchPath) {
171  QFileInfo fi(QDir(packagepath), file);
172  if (fi.exists()) {
174  break;
175  }
176  }
177  }
178  }
179 
180  d->version = QVariant(element.attribute("version", QString(d->version))).toInt();
181 
182  setText(i18nd(KLocalizedString::applicationDomain().constData(), element.attribute("text").toUtf8().constData()));
183  const QString comment = element.attribute("comment");
184  if (!comment.isEmpty()) {
186  }
187  setEnabled(true);
188  setInterpreter(element.attribute("interpreter"));
189  setEnabled(QVariant(element.attribute("enabled", "true")).toBool() && isEnabled());
190 
191  QString icon = element.attribute("icon");
192  if (icon.isEmpty() && ! d->scriptfile.isNull()) {
193  QMimeDatabase db;
194  icon = db.mimeTypeForUrl(QUrl::fromLocalFile(d->scriptfile)).iconName();
195  }
196  setIconName(icon);
197 
198  const QString code = element.attribute("code");
199  if (! code.isNull()) {
200  setCode(code.toUtf8());
201  }
202 
203  for (QDomNode node = element.firstChild(); ! node.isNull(); node = node.nextSibling()) {
204  QDomElement e = node.toElement();
205  if (! e.isNull()) {
206  if (e.tagName() == "property") {
207  const QString n = e.attribute("name", QString());
208  if (! n.isNull()) {
209 #ifdef KROSS_ACTION_DEBUG
210  qCDebug(KROSS_LOG) << "Action::readDomElement: Setting property name=" <<
211  n << " value=" << e.text();
212 #endif
214  }
215  }
216  }
217  }
218 }
219 
221 {
222  return toDomElement(QStringList());
223 }
224 
226 {
227  QDomDocument doc;
228  QDomElement e = doc.createElement("script");
229  e.setAttribute("name", objectName());
230  if (d->version > 0) {
231  e.setAttribute("version", QString(d->version));
232  }
233  if (! text().isNull()) {
234  e.setAttribute("text", text());
235  }
236  if (! description().isNull()) {
237  e.setAttribute("comment", description());
238  }
239  if (! iconName().isNull()) {
240  e.setAttribute("icon", iconName());
241  }
242  if (! isEnabled()) {
243  e.setAttribute("enabled", "false");
244  }
245  if (! interpreter().isNull()) {
246  e.setAttribute("interpreter", interpreter());
247  }
248 
249  QString fileName = file();
250  if (!searchPath.isEmpty()) {
251  //fileName=QDir(searchPath.first()).relativeFilePath(fileName); //prefer absname if it is short?
252  foreach (const QString &packagepath, searchPath) {
253  QString nfn = QDir(packagepath).relativeFilePath(file());
254  if (nfn.length() < fileName.length()) {
255  fileName = nfn;
256  }
257  }
258  }
259 
260  if (! fileName.isNull()) {
261  e.setAttribute("file", fileName);
262  }
263 
265  foreach (const QByteArray &prop, props) {
266  QDomElement p = doc.createElement("property");
267  p.setAttribute("name", QString::fromLatin1(prop));
269  e.appendChild(p);
270  }
271  /*
272  else if( ! code().isNull() ) {
273  e.setAttribute("code", code());
274  }
275  */
276 
277  return e;
278 }
279 
281 {
282  return d->script;
283 }
284 
286 {
287  return objectName();
288 }
289 
290 int Action::version() const
291 {
292  return d->version;
293 }
294 
296 {
297  return d->description;
298 }
299 
300 void Action::setDescription(const QString &description)
301 {
302  d->description = description;
303  emit dataChanged(this);
304  emit updated();
305 }
306 
308 {
309  return d->iconname;
310 }
311 
312 void Action::setIconName(const QString &iconname)
313 {
314  setIcon(QIcon::fromTheme(iconname));
315  d->iconname = iconname;
316  emit dataChanged(this);
317  emit updated();
318 }
319 
320 bool Action::isEnabled() const
321 {
322  return QAction::isEnabled();
323 }
324 
325 void Action::setEnabled(bool enabled)
326 {
328  emit dataChanged(this);
329  emit updated();
330 }
331 
333 {
334  return d->code;
335 }
336 
337 void Action::setCode(const QByteArray &code)
338 {
339  if (d->code != code) {
340  finalize();
341  d->code = code;
342  emit dataChanged(this);
343  emit updated();
344  }
345 }
346 
348 {
349  return d->interpretername;
350 }
351 
352 void Action::setInterpreter(const QString &interpretername)
353 {
354  if (d->interpretername != interpretername) {
355  finalize();
356  d->interpretername = interpretername;
357  setEnabled(Manager::self().interpreters().contains(interpretername));
358  if (!isEnabled()) {
359  qCWarning(KROSS_LOG) << "Action::setInterpreter: interpreter not found: " << interpretername;
360  }
361  emit dataChanged(this);
362  emit updated();
363  }
364 }
365 
367 {
368  return d->scriptfile;
369 }
370 
371 bool Action::setFile(const QString &scriptfile)
372 {
373  if (d->scriptfile != scriptfile) {
374  finalize();
375  if (scriptfile.isNull()) {
376  if (! d->scriptfile.isNull()) {
377  d->interpretername.clear();
378  }
379  d->scriptfile.clear();
380  d->searchpath.clear();
381  } else {
382  d->scriptfile = scriptfile;
383  d->interpretername = Manager::self().interpreternameForFile(scriptfile);
384  if (d->interpretername.isNull()) {
385  return false;
386  }
387  }
388  }
389  return true;
390 }
391 
393 {
394  return file().isEmpty() ? QString() : QFileInfo(file()).absolutePath(); //obey Qt docs and don't cheat
395 }
396 
397 QVariantMap Action::options() const
398 {
399  return d->options;
400 }
401 
402 void Action::addQObject(QObject *obj, const QString &name)
403 {
404  this->addObject(obj, name);
405 }
406 
407 QObject *Action::qobject(const QString &name) const
408 {
409  return this->object(name);
410 }
411 
413 {
414  return this->objects().keys();
415 }
416 
417 QVariant Action::option(const QString &name, const QVariant &defaultvalue)
418 {
419  if (d->options.contains(name)) {
420  return d->options[name];
421  }
422  InterpreterInfo *info = Manager::self().interpreterInfo(d->interpretername);
423  return info ? info->optionValue(name, defaultvalue) : defaultvalue;
424 }
425 
426 bool Action::setOption(const QString &name, const QVariant &value)
427 {
428  InterpreterInfo *info = Manager::self().interpreterInfo(d->interpretername);
429  if (info) {
430  if (info->hasOption(name)) {
431  d->options.insert(name, value);
432  return true;
433  } else {
434  qCWarning(KROSS_LOG) << QStringLiteral("Kross::Action::setOption(%1, %2): No such option")
435  .arg(name).arg(value.toString());
436  }
437  } else {
438  qCWarning(KROSS_LOG) << QStringLiteral("Kross::Action::setOption(%1, %2): No such interpreterinfo")
439  .arg(name).arg(value.toString());
440  }
441  return false;
442 }
443 
445 {
446  if (! d->script) {
447  if (! initialize()) {
448  return QStringList();
449  }
450  }
451  return d->script->functionNames();
452 }
453 
454 QVariant Action::callFunction(const QString &name, const QVariantList &args)
455 {
456  if (! d->script) {
457  if (! initialize()) {
458  return QVariant();
459  }
460  }
461  return d->script->callFunction(name, args);
462 }
463 
465 {
466  if (! d->script) {
467  if (! initialize()) {
468  return QVariant();
469  }
470  }
471  return d->script->evaluate(code);
472 }
473 
475 {
476  finalize();
477 
478  if (! d->scriptfile.isNull()) {
479  QFile f(d->scriptfile);
480  if (! f.exists()) {
481  setError(i18n("Scriptfile \"%1\" does not exist.", d->scriptfile));
482  return false;
483  }
484  if (d->interpretername.isNull()) {
485  setError(i18n("Failed to determine interpreter for scriptfile \"%1\"", d->scriptfile));
486  return false;
487  }
488  if (! f.open(QIODevice::ReadOnly)) {
489  setError(i18n("Failed to open scriptfile \"%1\"", d->scriptfile));
490  return false;
491  }
492  d->code = f.readAll();
493  f.close();
494  }
495 
496  Interpreter *interpreter = Manager::self().interpreter(d->interpretername);
497  if (! interpreter) {
498  InterpreterInfo *info = Manager::self().interpreterInfo(d->interpretername);
499  if (info) {
500  setError(i18n("Failed to load interpreter \"%1\"", d->interpretername));
501  } else {
502  setError(i18n("No such interpreter \"%1\"", d->interpretername));
503  }
504  return false;
505  }
506 
507  d->script = interpreter->createScript(this);
508  if (! d->script) {
509  setError(i18n("Failed to create script for interpreter \"%1\"", d->interpretername));
510  return false;
511  }
512 
513  if (d->script->hadError()) {
514  setError(d->script);
515  finalize();
516  return false;
517  }
518 
519  clearError(); // clear old exception
520  return true;
521 }
522 
524 {
525  if (d->script) {
526  emit finalized(this);
527  }
528  delete d->script;
529  d->script = nullptr;
530 }
531 
533 {
534  return d->script == nullptr;
535 }
536 
537 void Action::slotTriggered()
538 {
539 #ifdef KROSS_ACTION_DEBUG
540  qCDebug(KROSS_LOG) << "Action::slotTriggered() name=" << objectName();
541 #endif
542 
543  emit started(this);
544 
545  if (! d->script) {
546  if (! initialize()) {
547  Q_ASSERT(hadError());
548  }
549  }
550 
551  if (hadError()) {
552 #ifdef KROSS_ACTION_DEBUG
553  qCDebug(KROSS_LOG) << "Action::slotTriggered() on init, errorMessage=" << errorMessage();
554 #endif
555  } else {
556  d->script->execute();
557  if (d->script->hadError()) {
558 #ifdef KROSS_ACTION_DEBUG
559  qCDebug(KROSS_LOG) << "Action::slotTriggered() after exec, errorMessage=" << errorMessage();
560 #endif
561  setError(d->script);
562  //emit finished(this);
563  finalize();
564  //return;
565  }
566  }
567 
568  emit finished(this);
569 }
570 
571 // --------
572 
573 // interface files
575 {
576 }
577 
578 #include "moc_action.cpp"
void updated()
This signal is emitted if the content of the Action was changed.
bool isNull() const const
bool isNull() const const
void addObject(QObject *object, const QString &name=QString(), Options options=NoOption)
Add a QObject to the list of children.
void fromDomElement(const QDomElement &element)
Method to read settings from the QDomElement element that contains details about e....
Definition: action.cpp:154
bool isFinalized() const
Definition: action.cpp:532
QString text() const const
QDomNode firstChild() const const
QDomElement toElement() const const
QList< QByteArray > dynamicPropertyNames() const const
void finalize()
Finalize the Script instance and frees any cached or still running executions.
Definition: action.cpp:523
Base class for interpreter implementations.
QMimeType mimeTypeForUrl(const QUrl &url) const const
QString tagName() const const
QVariant option(const QString &name, const QVariant &defaultvalue=QVariant())
Definition: action.cpp:417
void setEnabled(bool enabled)
Set the enable state of this Action to enabled .
Definition: action.cpp:325
virtual bool open(QIODevice::OpenMode mode) override
QCA_EXPORT void init()
bool isNull() const const
QList< Key > keys() const const
QDomText createTextNode(const QString &value)
QIcon fromTheme(const QString &name)
QVariant evaluate(const QByteArray &code)
Evaluate some scripting code.
Definition: action.cpp:464
QString description() const
Definition: action.cpp:295
virtual ~WrapperInterface()
Destructor.
Definition: action.cpp:574
QByteArray toLatin1() const const
QDomElement createElement(const QString &tagName)
bool isEnabled() const
Return true if this Action is enabled else false is returned.
Definition: action.cpp:320
~Action() override
Destructor.
Definition: action.cpp:141
QString currentPath() const
Definition: action.cpp:392
bool exists() const const
bool exists() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool initialize()
Initialize the Script instance.
Definition: action.cpp:474
QVariantMap options() const
Definition: action.cpp:397
void setAttribute(const QString &name, const QString &value)
QHash< QString, QObject * > objects() const
void addQObject(QObject *obj, const QString &name=QString())
Add a QObject instance to the action.
Definition: action.cpp:402
QString i18n(const char *text, const TYPE &arg...)
QString absoluteFilePath() const const
bool setFile(const QString &scriptfile)
Set the script file that should be executed.
Definition: action.cpp:371
void started(Kross::Action *)
This signal is emitted before the script got executed.
QString interpreter() const
Definition: action.cpp:347
bool isEmpty() const const
QUrl fromLocalFile(const QString &localFile)
QByteArray toUtf8() const const
int length() const const
QString name() const
Definition: action.cpp:285
void setText(const QString &text)
int toInt(bool *ok) const const
bool isEmpty() const const
QString toLocalFile() const const
void finished(Kross::Action *)
This signal is emitted after the script got executed.
QDomElement toDomElement() const
Definition: action.cpp:220
void setIconName(const QString &iconname)
Set the name of the icon to iconname .
Definition: action.cpp:312
void finalized(Kross::Action *)
This signal is emitted once a script finalized.
virtual void close() override
QStringList functionNames()
Definition: action.cpp:444
The Action class is an abstract container to deal with scripts like a single standalone script file.
Definition: action.h:112
QString absolutePath() const const
QString fileName() const const
void clearError()
Clear the error.
void setDescription(const QString &description)
Set the optional description for this Action.
Definition: action.cpp:300
Action(QObject *parent, const QString &name, const QDir &packagepath=QDir())
Constructor.
Definition: action.cpp:116
QString i18nd(const char *domain, const char *text, const TYPE &arg...)
bool toBool() const const
int version() const
Definition: action.cpp:290
QString absolutePath() const const
QObject * object(const QString &name) const
Script * script() const
Definition: action.cpp:280
bool setProperty(const char *name, const QVariant &value)
QVariant callFunction(const QString &name, const QVariantList &args=QVariantList())
Call a function in the script.
Definition: action.cpp:454
const QString errorMessage() const
QString path(QUrl::ComponentFormattingOptions options) const const
const QVariant optionValue(const QString &name, const QVariant &defaultvalue=QVariant()) const
bool isEnabled() const const
const char * constData() const const
QDomNode appendChild(const QDomNode &newChild)
QByteArray code() const
Definition: action.cpp:332
QString fromLatin1(const char *str, int size)
The InterpreterInfo class provides abstract information about a Interpreter before the interpreter-ba...
void setObjectName(const QString &name)
QStringList qobjectNames() const
Definition: action.cpp:412
QString relativeFilePath(const QString &fileName) const const
void dataChanged(Action *)
This signal is emitted when the data of the Action is changed.
void setInterpreter(const QString &interpretername)
Set the name of the interpreter (javascript, python or ruby).
Definition: action.cpp:352
Interface for error-handling.
void setCode(const QByteArray &code)
Set the scriptcode code this Action should execute.
Definition: action.cpp:337
QByteArray readAll()
bool setOption(const QString &name, const QVariant &value)
Set the Interpreter::Option value.
Definition: action.cpp:426
QString attribute(const QString &name, const QString &defValue) const const
void setError(const QString &errormessage, const QString &tracemessage=QString(), long lineno=-1)
Set the error message.
Interface for managing Object collections.
QString file() const
Definition: action.cpp:366
QString iconName() const
Return the name of the icon.
Definition: action.cpp:307
static QByteArray applicationDomain()
QObject * parent() const const
QObject * qobject(const QString &name) const
Definition: action.cpp:407
Base class for interpreter dependent functionality each script provides.
Definition: core/script.h:61
The ActionCollection class manages collections of Action instances.
QString toString() const const
QVariant property(const char *name) const const
bool hasOption(const QString &name) const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Dec 5 2023 04:09:32 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.