00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "action.h"
00021 #include "actioncollection.h"
00022 #include "interpreter.h"
00023 #include "script.h"
00024 #include "manager.h"
00025 #include "wrapperinterface.h"
00026
00027 #include <QtCore/QFile>
00028 #include <QtCore/QFileInfo>
00029
00030 #include <klocale.h>
00031 #include <kicon.h>
00032 #include <kmimetype.h>
00033
00034 using namespace Kross;
00035
00036 namespace Kross {
00037
00039 class Action::Private
00040 {
00041 public:
00042
00048 Script* script;
00049
00054 int version;
00055
00060 QString description;
00061
00065 QString iconname;
00066
00070 QByteArray code;
00071
00076 QString interpretername;
00077
00084 QString scriptfile;
00085
00091 QStringList searchpath;
00092
00097 QMap< QString, QVariant > options;
00098
00099 Private() : script(0), version(0) {}
00100 };
00101
00102 }
00103
00104 enum InitOptions{Enable=1};
00105 void static init(Action* th, const QString& name, int options=0)
00106 {
00107 th->setEnabled(options&Enable);
00108 th->setObjectName(name);
00109 #ifdef KROSS_ACTION_DEBUG
00110 krossdebug( QString("Action::Action(QObject*,QString,QDir) Ctor name='%1'").arg(th->objectName()) );
00111 #endif
00112 QObject::connect(th, SIGNAL(triggered(bool)), th, SLOT(slotTriggered()));
00113 }
00114
00115 Action::Action(QObject* parent, const QString& name, const QDir& packagepath)
00116 : QAction(parent)
00117 , QScriptable()
00118 , ChildrenInterface()
00119 , ErrorInterface()
00120 , d( new Private() )
00121 {
00122 init(this,name);
00123 d->searchpath=QStringList(packagepath.absolutePath());
00124 }
00125
00126 Action::Action(QObject* parent, const QUrl& url)
00127 : QAction(parent)
00128 , ChildrenInterface()
00129 , ErrorInterface()
00130 , d( new Private() )
00131 {
00132 init(this,url.path(),Enable);
00133 QFileInfo fi( url.toLocalFile() );
00134 setText( fi.fileName() );
00135 setIconName( KMimeType::iconNameForUrl(url) );
00136 setFile( url.toLocalFile() );
00137 }
00138
00139 Action::~Action()
00140 {
00141 #ifdef KROSS_ACTION_DEBUG
00142 krossdebug( QString("Action::~Action() Dtor name='%1'").arg(objectName()) );
00143 #endif
00144 finalize();
00145 ActionCollection *coll = qobject_cast<ActionCollection*>(parent());
00146 if ( coll ) {
00147 coll->removeAction(this);
00148 }
00149 delete d;
00150 }
00151
00152
00153 void Action::fromDomElement(const QDomElement& element)
00154 {
00155 fromDomElement(element, d->searchpath);
00156 }
00157
00158 void Action::fromDomElement(const QDomElement& element, const QStringList& searchPath)
00159 {
00160 if( element.isNull() )
00161 return;
00162
00163 QString file = element.attribute("file");
00164 if( ! file.isEmpty() ) {
00165 if( QFileInfo(file).exists() ) {
00166 setFile(file);
00167 }
00168 else {
00169 foreach (const QString& packagepath, searchPath) {
00170 QFileInfo fi(QDir(packagepath), file);
00171 if( fi.exists() ) {
00172 setFile( fi.absoluteFilePath() );
00173 break;
00174 }
00175 }
00176 }
00177 }
00178
00179 d->version = QVariant( element.attribute("version",QString(d->version)) ).toInt();
00180
00181 setText( i18n( element.attribute("text").toUtf8() ) );
00182 setDescription( i18n( element.attribute("comment").toUtf8() ) );
00183 setEnabled( true );
00184 setInterpreter( element.attribute("interpreter") );
00185 setEnabled( QVariant(element.attribute("enabled","true")).toBool() && isEnabled() );
00186
00187 QString icon = element.attribute("icon");
00188 if( icon.isEmpty() && ! d->scriptfile.isNull() )
00189 icon = KMimeType::iconNameForUrl( KUrl(d->scriptfile) );
00190 setIconName( icon );
00191
00192 const QString code = element.attribute("code");
00193 if( ! code.isNull() )
00194 setCode(code.toUtf8());
00195
00196 for(QDomNode node = element.firstChild(); ! node.isNull(); node = node.nextSibling()) {
00197 QDomElement e = node.toElement();
00198 if( ! e.isNull() ) {
00199 if( e.tagName() == "property" ) {
00200 const QString n = e.attribute("name", QString());
00201 if( ! n.isNull() ) {
00202 #ifdef KROSS_ACTION_DEBUG
00203 krossdebug(QString("Action::readDomElement: Setting property name=%1 value=%2").arg(n).arg(e.text()));
00204 #endif
00205 setProperty(n.toLatin1().constData(), QVariant(e.text()));
00206 }
00207 }
00208 }
00209 }
00210 }
00211
00212 QDomElement Action::toDomElement() const
00213 {
00214 return toDomElement(QStringList());
00215 }
00216
00217 QDomElement Action::toDomElement(const QStringList& searchPath) const
00218 {
00219 QDomDocument doc;
00220 QDomElement e = doc.createElement("script");
00221 e.setAttribute("name", objectName());
00222 if( d->version > 0 )
00223 e.setAttribute("version", QString(d->version));
00224 if( ! text().isNull() )
00225 e.setAttribute("text", text());
00226 if( ! description().isNull() )
00227 e.setAttribute("comment", description());
00228 if( ! iconName().isNull() )
00229 e.setAttribute("icon", iconName());
00230 if( ! isEnabled() )
00231 e.setAttribute("enabled", "false");
00232 if( ! interpreter().isNull() )
00233 e.setAttribute("interpreter", interpreter());
00234
00235
00236 QString fileName=file();
00237 if (!searchPath.isEmpty()) {
00238
00239 foreach(const QString& packagepath, searchPath) {
00240 QString nfn=QDir(packagepath).relativeFilePath(file());
00241 if (nfn.length()<fileName.length())
00242 fileName=nfn;
00243 }
00244 }
00245
00246 if( ! fileName.isNull() ) {
00247 e.setAttribute("file", fileName);
00248 }
00249
00250 QList<QByteArray> props=dynamicPropertyNames();
00251 foreach(const QByteArray& prop, props) {
00252 QDomElement p = doc.createElement("property");
00253 p.setAttribute("name", QString::fromLatin1(prop));
00254 p.appendChild(doc.createTextNode(property(prop.constData()).toString()));
00255 e.appendChild(p);
00256 }
00257
00258
00259
00260
00261
00262
00263 return e;
00264 }
00265
00266 Kross::Script* Action::script() const
00267 {
00268 return d->script;
00269 }
00270
00271 QString Action::name() const
00272 {
00273 return objectName();
00274 }
00275
00276 int Action::version() const
00277 {
00278 return d->version;
00279 }
00280
00281 QString Action::description() const
00282 {
00283 return d->description;
00284 }
00285
00286 void Action::setDescription(const QString& description)
00287 {
00288 d->description = description;
00289 emit dataChanged(this);
00290 emit updated();
00291 }
00292
00293 QString Action::iconName() const
00294 {
00295 return d->iconname;
00296 }
00297
00298 void Action::setIconName(const QString& iconname)
00299 {
00300 setIcon( KIcon(iconname) );
00301 d->iconname = iconname;
00302 emit dataChanged(this);
00303 emit updated();
00304 }
00305
00306 bool Action::isEnabled() const
00307 {
00308 return QAction::isEnabled();
00309 }
00310
00311 void Action::setEnabled(bool enabled)
00312 {
00313 QAction::setEnabled(enabled);
00314 emit dataChanged(this);
00315 emit updated();
00316 }
00317
00318 QByteArray Action::code() const
00319 {
00320 return d->code;
00321 }
00322
00323 void Action::setCode(const QByteArray& code)
00324 {
00325 if( d->code != code ) {
00326 finalize();
00327 d->code = code;
00328 emit dataChanged(this);
00329 emit updated();
00330 }
00331 }
00332
00333 QString Action::interpreter() const
00334 {
00335 return d->interpretername;
00336 }
00337
00338 void Action::setInterpreter(const QString& interpretername)
00339 {
00340 if( d->interpretername != interpretername ) {
00341 finalize();
00342 d->interpretername = interpretername;
00343 setEnabled( Manager::self().interpreters().contains(interpretername) );
00344 if (!isEnabled())
00345 krosswarning("Action::setInterpreter: interpreter not found: "+interpretername);
00346 emit dataChanged(this);
00347 emit updated();
00348 }
00349 }
00350
00351 QString Action::file() const
00352 {
00353 return d->scriptfile;
00354 }
00355
00356 bool Action::setFile(const QString& scriptfile)
00357 {
00358 if( d->scriptfile != scriptfile ) {
00359 finalize();
00360 if ( scriptfile.isNull() ) {
00361 if( ! d->scriptfile.isNull() )
00362 d->interpretername.clear();
00363 d->scriptfile.clear();
00364 d->searchpath.clear();
00365 }
00366 else {
00367 d->scriptfile = scriptfile;
00368 d->interpretername = Manager::self().interpreternameForFile(scriptfile);
00369 if( d->interpretername.isNull() )
00370 return false;
00371 }
00372 }
00373 return true;
00374 }
00375
00376 QString Action::currentPath() const
00377 {
00378 return file().isEmpty()?QString():QFileInfo(file()).absolutePath();
00379 }
00380
00381 QVariantMap Action::options() const
00382 {
00383 return d->options;
00384 }
00385
00386 void Action::addQObject(QObject* obj, const QString &name)
00387 {
00388 this->addObject(obj, name);
00389 }
00390
00391 QObject* Action::qobject(const QString &name) const
00392 {
00393 return this->object(name);
00394 }
00395
00396 QStringList Action::qobjectNames() const
00397 {
00398 return this->objects().keys();
00399 }
00400
00401 QVariant Action::option(const QString& name, const QVariant& defaultvalue)
00402 {
00403 if(d->options.contains(name))
00404 return d->options[name];
00405 InterpreterInfo* info = Manager::self().interpreterInfo( d->interpretername );
00406 return info ? info->optionValue(name, defaultvalue) : defaultvalue;
00407 }
00408
00409 bool Action::setOption(const QString& name, const QVariant& value)
00410 {
00411 InterpreterInfo* info = Manager::self().interpreterInfo( d->interpretername );
00412 if(info) {
00413 if(info->hasOption(name)) {
00414 d->options.insert(name, value);
00415 return true;
00416 } else krosswarning( QString("Kross::Action::setOption(%1, %2): No such option").arg(name).arg(value.toString()) );
00417 } else krosswarning( QString("Kross::Action::setOption(%1, %2): No such interpreterinfo").arg(name).arg(value.toString()) );
00418 return false;
00419 }
00420
00421 QStringList Action::functionNames()
00422 {
00423 if(! d->script) {
00424 if(! initialize())
00425 return QStringList();
00426 }
00427 return d->script->functionNames();
00428 }
00429
00430 QVariant Action::callFunction(const QString& name, const QVariantList& args)
00431 {
00432 if(! d->script) {
00433 if(! initialize())
00434 return QVariant();
00435 }
00436 return d->script->callFunction(name, args);
00437 }
00438
00439 QVariant Action::evaluate(const QByteArray& code)
00440 {
00441 if(! d->script) {
00442 if(! initialize())
00443 return QVariant();
00444 }
00445 return d->script->evaluate(code);
00446 }
00447
00448 bool Action::initialize()
00449 {
00450 finalize();
00451
00452 if( ! d->scriptfile.isNull() ) {
00453 QFile f( d->scriptfile );
00454 if( ! f.exists() ) {
00455 setError(i18n("Scriptfile \"%1\" does not exist.", d->scriptfile));
00456 return false;
00457 }
00458 if( d->interpretername.isNull() ) {
00459 setError(i18n("Failed to determine interpreter for scriptfile \"%1\"", d->scriptfile));
00460 return false;
00461 }
00462 if( ! f.open(QIODevice::ReadOnly) ) {
00463 setError(i18n("Failed to open scriptfile \"%1\"", d->scriptfile));
00464 return false;
00465 }
00466 d->code = f.readAll();
00467 f.close();
00468 }
00469
00470 Interpreter* interpreter = Manager::self().interpreter(d->interpretername);
00471 if( ! interpreter ) {
00472 InterpreterInfo* info = Manager::self().interpreterInfo(d->interpretername);
00473 if( info )
00474 setError(i18n("Failed to load interpreter \"%1\"", d->interpretername));
00475 else
00476 setError(i18n("No such interpreter \"%1\"", d->interpretername));
00477 return false;
00478 }
00479
00480 d->script = interpreter->createScript(this);
00481 if( ! d->script ) {
00482 setError(i18n("Failed to create script for interpreter \"%1\"", d->interpretername));
00483 return false;
00484 }
00485
00486 if( d->script->hadError() ) {
00487 setError(d->script);
00488 finalize();
00489 return false;
00490 }
00491
00492 clearError();
00493 return true;
00494 }
00495
00496 void Action::finalize()
00497 {
00498 if( d->script )
00499 emit finalized(this);
00500 delete d->script;
00501 d->script = 0;
00502 }
00503
00504 bool Action::isFinalized() const
00505 {
00506 return d->script == 0;
00507 }
00508
00509 void Action::slotTriggered()
00510 {
00511 #ifdef KROSS_ACTION_DEBUG
00512 krossdebug( QString("Action::slotTriggered() name=%1").arg(objectName()) );
00513 #endif
00514
00515 emit started(this);
00516
00517 if( ! d->script ) {
00518 if( ! initialize() )
00519 Q_ASSERT( hadError() );
00520 }
00521
00522 if( hadError() ) {
00523 #ifdef KROSS_ACTION_DEBUG
00524 krossdebug( QString("Action::slotTriggered() on init, errorMessage=%2").arg(errorMessage()) );
00525 #endif
00526 }
00527 else {
00528 d->script->execute();
00529 if( d->script->hadError() ) {
00530 #ifdef KROSS_ACTION_DEBUG
00531 krossdebug( QString("Action::slotTriggered() after exec, errorMessage=%2").arg(errorMessage()) );
00532 #endif
00533 setError(d->script);
00534
00535 finalize();
00536
00537 }
00538 }
00539
00540 emit finished(this);
00541 }
00542
00543
00544
00545
00546 WrapperInterface::~WrapperInterface()
00547 {
00548 }
00549
00550 #include "action.moc"