00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "simplejavascriptapplet.h"
00020
00021 #include <QScriptEngine>
00022 #include <QFile>
00023 #include <QUiLoader>
00024 #include <QGraphicsLayout>
00025 #include <QWidget>
00026
00027 #include <KDebug>
00028 #include <KLocale>
00029 #include <KStandardDirs>
00030 #include <KConfigGroup>
00031
00032 #include <Plasma/Applet>
00033 #include <Plasma/Svg>
00034 #include <Plasma/FrameSvg>
00035 #include <Plasma/Package>
00036
00037 #include "appletinterface.h"
00038
00039 using namespace Plasma;
00040
00041 #include "bind_dataengine.h"
00042
00043 Q_DECLARE_METATYPE(QPainter*)
00044 Q_DECLARE_METATYPE(QStyleOptionGraphicsItem*)
00045 Q_DECLARE_METATYPE(SimpleJavaScriptApplet*)
00046 Q_DECLARE_METATYPE(AppletInterface*)
00047 Q_DECLARE_METATYPE(Applet*)
00048 Q_DECLARE_METATYPE(QGraphicsWidget*)
00049 Q_DECLARE_METATYPE(QGraphicsLayout*)
00050 Q_DECLARE_METATYPE(KConfigGroup)
00051
00052 Q_SCRIPT_DECLARE_QMETAOBJECT(AppletInterface, SimpleJavaScriptApplet*)
00053
00054 QScriptValue constructPainterClass(QScriptEngine *engine);
00055 QScriptValue constructGraphicsItemClass(QScriptEngine *engine);
00056 QScriptValue constructLinearLayoutClass(QScriptEngine *engine);
00057 QScriptValue constructTimerClass(QScriptEngine *engine);
00058 QScriptValue constructFontClass(QScriptEngine *engine);
00059 QScriptValue constructQRectFClass(QScriptEngine *engine);
00060 QScriptValue constructQPointClass(QScriptEngine *engine);
00061 QScriptValue constructQSizeFClass(QScriptEngine *engine);
00062
00063 class DummyService : public Service
00064 {
00065 public:
00066 ServiceJob *createJob(const QString &operation, QMap<QString, QVariant> ¶meters)
00067 {
00068 Q_UNUSED(operation)
00069 Q_UNUSED(parameters)
00070 return 0;
00071 }
00072 };
00073
00074
00075
00076
00077 QScriptValue variant2ScriptValue(QScriptEngine *engine, QVariant var)
00078 {
00079 if (var.isNull()) {
00080 return engine->nullValue();
00081 }
00082
00083 switch(var.type())
00084 {
00085 case QVariant::Invalid:
00086 return engine->nullValue();
00087 case QVariant::Bool:
00088 return QScriptValue(engine, var.toBool());
00089 case QVariant::Date:
00090 return engine->newDate(var.toDateTime());
00091 case QVariant::DateTime:
00092 return engine->newDate(var.toDateTime());
00093 case QVariant::Double:
00094 return QScriptValue(engine, var.toDouble());
00095 case QVariant::Int:
00096 case QVariant::LongLong:
00097 return QScriptValue(engine, var.toInt());
00098 case QVariant::String:
00099 return QScriptValue(engine, var.toString());
00100 case QVariant::Time: {
00101 QDateTime t(QDate::currentDate(), var.toTime());
00102 return engine->newDate(t);
00103 }
00104 case QVariant::UInt:
00105 return QScriptValue(engine, var.toUInt());
00106 default:
00107 break;
00108 }
00109
00110 return qScriptValueFromValue(engine, var);
00111 }
00112
00113 QScriptValue qScriptValueFromData(QScriptEngine *engine, const DataEngine::Data &data)
00114 {
00115 DataEngine::Data::const_iterator begin = data.begin();
00116 DataEngine::Data::const_iterator end = data.end();
00117 DataEngine::Data::const_iterator it;
00118
00119 QScriptValue obj = engine->newObject();
00120
00121 for (it = begin; it != end; ++it) {
00122
00123 QString prop = it.key();
00124 prop.replace(' ', '_');
00125 obj.setProperty(prop, variant2ScriptValue(engine, it.value()));
00126 }
00127
00128 return obj;
00129 }
00130
00131 QScriptValue qScriptValueFromKConfigGroup(QScriptEngine *engine, const KConfigGroup &config)
00132 {
00133 QScriptValue obj = engine->newObject();
00134
00135 if (!config.isValid()) {
00136 return obj;
00137 }
00138
00139 QMap<QString, QString> entryMap = config.entryMap();
00140 QMap<QString, QString>::const_iterator it = entryMap.constBegin();
00141 QMap<QString, QString>::const_iterator begin = it;
00142 QMap<QString, QString>::const_iterator end = entryMap.constEnd();
00143
00144
00145 obj.setProperty("__name", QScriptValue(engine, config.name()));
00146
00147
00148 for (it = begin; it != end; ++it) {
00149
00150 QString prop = it.key();
00151 prop.replace(' ', '_');
00152 obj.setProperty(prop, variant2ScriptValue(engine, it.value()));
00153 }
00154
00155 return obj;
00156 }
00157
00158 void kConfigGroupFromScriptValue(const QScriptValue& obj, KConfigGroup &config)
00159 {
00160 KConfigSkeleton *skel = new KConfigSkeleton();
00161 config = KConfigGroup(skel->config(), obj.property("__name").toString());
00162
00163 QScriptValueIterator it(obj);
00164
00165 while (it.hasNext()) {
00166 it.next();
00167
00168 if (it.name() != "__name") {
00169 config.writeEntry(it.name(), it.value().toString());
00170 }
00171 }
00172 }
00173
00174 KSharedPtr<UiLoader> SimpleJavaScriptApplet::s_widgetLoader;
00175
00176 SimpleJavaScriptApplet::SimpleJavaScriptApplet(QObject *parent, const QVariantList &args)
00177 : Plasma::AppletScript(parent)
00178 {
00179
00180
00181 m_engine = new QScriptEngine(this);
00182 importExtensions();
00183 }
00184
00185 SimpleJavaScriptApplet::~SimpleJavaScriptApplet()
00186 {
00187 if (s_widgetLoader.count() == 1) {
00188 s_widgetLoader.clear();
00189 }
00190 }
00191
00192 void SimpleJavaScriptApplet::reportError()
00193 {
00194 kDebug() << "Error: " << m_engine->uncaughtException().toString()
00195 << " at line " << m_engine->uncaughtExceptionLineNumber() << endl;
00196 kDebug() << m_engine->uncaughtExceptionBacktrace();
00197 }
00198
00199 void SimpleJavaScriptApplet::showConfigurationInterface()
00200 {
00201 kDebug() << "Script: showConfigurationInterface";
00202
00203
00204 QScriptValue global = m_engine->globalObject();
00205
00206 QScriptValue fun = m_self.property("showConfigurationInterface");
00207 if (!fun.isFunction()) {
00208 kDebug() << "Script: ShowConfiguratioInterface is not a function, " << fun.toString();
00209 return;
00210 }
00211
00212 QScriptContext *ctx = m_engine->pushContext();
00213 ctx->setActivationObject(m_self);
00214 fun.call(m_self);
00215 m_engine->popContext();
00216
00217 if (m_engine->hasUncaughtException()) {
00218 reportError();
00219 }
00220 }
00221
00222 void SimpleJavaScriptApplet::configAccepted()
00223 {
00224 QScriptValue fun = m_self.property("configAccepted");
00225 if (!fun.isFunction()) {
00226 kDebug() << "Script: configAccepted is not a function, " << fun.toString();
00227 return;
00228 }
00229
00230 QScriptContext *ctx = m_engine->pushContext();
00231 ctx->setActivationObject(m_self);
00232 fun.call(m_self);
00233 m_engine->popContext();
00234
00235 if (m_engine->hasUncaughtException()) {
00236 reportError();
00237 }
00238 }
00239
00240 void SimpleJavaScriptApplet::dataUpdated(const QString &name, const DataEngine::Data &data)
00241 {
00242 QScriptValue fun = m_self.property("dataUpdate");
00243 if (!fun.isFunction()) {
00244 kDebug() << "Script: dataUpdate is not a function, " << fun.toString();
00245 return;
00246 }
00247
00248 QScriptValueList args;
00249 args << m_engine->toScriptValue(name) << m_engine->toScriptValue(data);
00250
00251 QScriptContext *ctx = m_engine->pushContext();
00252 ctx->setActivationObject(m_self);
00253 fun.call(m_self, args);
00254 m_engine->popContext();
00255
00256 if (m_engine->hasUncaughtException()) {
00257 reportError();
00258 }
00259 }
00260
00261 void SimpleJavaScriptApplet::executeAction(const QString &name)
00262 {
00263 callFunction("action_" + name);
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276 }
00277
00278 void SimpleJavaScriptApplet::paintInterface(QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
00279 {
00280 Q_UNUSED(option)
00281 Q_UNUSED(contentsRect)
00282
00283
00284 QScriptValue fun = m_self.property("paintInterface");
00285 if (!fun.isFunction()) {
00286
00287 AppletScript::paintInterface(p, option, contentsRect);
00288 return;
00289 }
00290
00291 QScriptValueList args;
00292 args << m_engine->toScriptValue(p);
00293 args << m_engine->toScriptValue(const_cast<QStyleOptionGraphicsItem*>(option));
00294 args << m_engine->toScriptValue(contentsRect);
00295
00296 QScriptContext *ctx = m_engine->pushContext();
00297 ctx->setActivationObject(m_self);
00298 fun.call(m_self, args);
00299 m_engine->popContext();
00300
00301 if (m_engine->hasUncaughtException()) {
00302 reportError();
00303 }
00304 }
00305
00306 QList<QAction*> SimpleJavaScriptApplet::contextualActions()
00307 {
00308 return m_interface->contextualActions();
00309 }
00310
00311 void SimpleJavaScriptApplet::callFunction(const QString &functionName, const QScriptValueList &args)
00312 {
00313 QScriptValue fun = m_self.property(functionName);
00314 if (fun.isFunction()) {
00315 QScriptContext *ctx = m_engine->pushContext();
00316 ctx->setActivationObject(m_self);
00317 fun.call(m_self, args);
00318 m_engine->popContext();
00319
00320 if (m_engine->hasUncaughtException()) {
00321 reportError();
00322 }
00323 }
00324 }
00325
00326 void SimpleJavaScriptApplet::constraintsEvent(Plasma::Constraints constraints)
00327 {
00328 QString functionName;
00329
00330 if (constraints & Plasma::FormFactorConstraint) {
00331 callFunction("formFactorChanged");
00332 }
00333
00334 if (constraints & Plasma::LocationConstraint) {
00335 callFunction("locationChanged");
00336 }
00337
00338 if (constraints & Plasma::ContextConstraint) {
00339 callFunction("contextChanged");
00340 }
00341 }
00342
00343 bool SimpleJavaScriptApplet::init()
00344 {
00345 setupObjects();
00346
00347 kDebug() << "ScriptName:" << applet()->name();
00348 kDebug() << "ScriptCategory:" << applet()->category();
00349
00350 QFile file(mainScript());
00351 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
00352 kWarning() << "Unable to load script file";
00353 return false;
00354 }
00355
00356 QString script = file.readAll();
00357
00358
00359 m_engine->evaluate(script);
00360 if (m_engine->hasUncaughtException()) {
00361 reportError();
00362 return false;
00363 }
00364
00365 return true;
00366 }
00367
00368 void SimpleJavaScriptApplet::importExtensions()
00369 {
00370 return;
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385 }
00386
00387 void SimpleJavaScriptApplet::setupObjects()
00388 {
00389 QScriptValue global = m_engine->globalObject();
00390
00391
00392 m_engine->setDefaultPrototype(qMetaTypeId<DataEngine*>(), m_engine->newQObject(new DataEngine()));
00393 m_engine->setDefaultPrototype(qMetaTypeId<Service*>(), m_engine->newQObject(new DummyService()));
00394 m_engine->setDefaultPrototype(qMetaTypeId<ServiceJob*>(), m_engine->newQObject(new ServiceJob(QString(), QString(), QMap<QString, QVariant>())));
00395
00396 global.setProperty("dataEngine", m_engine->newFunction(SimpleJavaScriptApplet::dataEngine));
00397 global.setProperty("service", m_engine->newFunction(SimpleJavaScriptApplet::service));
00398 qScriptRegisterMetaType<DataEngine::Data>(m_engine, qScriptValueFromData, 0, QScriptValue());
00399 qScriptRegisterMetaType<KConfigGroup>(m_engine, qScriptValueFromKConfigGroup, kConfigGroupFromScriptValue, QScriptValue());
00400
00401
00402 m_interface = new AppletInterface(this);
00403 m_self = m_engine->newQObject(m_interface);
00404 m_self.setScope(global);
00405 global.setProperty("plasmoid", m_self);
00406
00407
00408 QMetaObject meta = AppletInterface::staticMetaObject;
00409 for (int i=0; i < meta.enumeratorCount(); ++i) {
00410 QMetaEnum e = meta.enumerator(i);
00411
00412 for (int i=0; i < e.keyCount(); ++i) {
00413
00414 global.setProperty(e.key(i), QScriptValue(m_engine, e.value(i)));
00415 }
00416 }
00417
00418
00419 QScriptValue fun = m_engine->newFunction(SimpleJavaScriptApplet::loadui);
00420 global.setProperty("loadui", fun);
00421
00422 fun = m_engine->newFunction(SimpleJavaScriptApplet::print);
00423 global.setProperty("print", fun);
00424
00425
00426
00427 qMetaTypeId<QVariant>();
00428
00429
00430 global.setProperty("PlasmaSvg", m_engine->newFunction(SimpleJavaScriptApplet::newPlasmaSvg));
00431 global.setProperty("PlasmaFrameSvg", m_engine->newFunction(SimpleJavaScriptApplet::newPlasmaFrameSvg));
00432
00433
00434 global.setProperty("QPainter", constructPainterClass(m_engine));
00435 global.setProperty("QGraphicsItem", constructGraphicsItemClass(m_engine));
00436 global.setProperty("QTimer", constructTimerClass(m_engine));
00437 global.setProperty("QFont", constructFontClass(m_engine));
00438 global.setProperty("QRectF", constructQRectFClass(m_engine));
00439 global.setProperty("QSizeF", constructQSizeFClass(m_engine));
00440 global.setProperty("QPoint", constructQPointClass(m_engine));
00441 global.setProperty("LinearLayout", constructLinearLayoutClass(m_engine));
00442
00443 installWidgets(m_engine);
00444 }
00445
00446 QString SimpleJavaScriptApplet::findDataResource(const QString &filename)
00447 {
00448 QString path("plasma-script/%1");
00449 return KGlobal::dirs()->findResource("data", path.arg(filename));
00450 }
00451
00452 void SimpleJavaScriptApplet::debug(const QString &msg)
00453 {
00454 kDebug() << msg;
00455 }
00456
00457 #if 0
00458 QScriptValue SimpleJavaScriptApplet::dataEngine(QScriptContext *context, QScriptEngine *engine)
00459 {
00460 if (context->argumentCount() != 1)
00461 return context->throwError("dataEngine takes one argument");
00462
00463 QString dataEngine = context->argument(0).toString();
00464
00465 Script *self = engine->fromScriptValue<Script*>(context->thisObject());
00466
00467 DataEngine *data = self->dataEngine(dataEngine);
00468 return engine->newQObject(data);
00469 }
00470 #endif
00471
00472 QScriptValue SimpleJavaScriptApplet::dataEngine(QScriptContext *context, QScriptEngine *engine)
00473 {
00474 if (context->argumentCount() != 1) {
00475 return context->throwError(i18n("DataEngine takes one argument"));
00476 }
00477
00478 QString dataEngine = context->argument(0).toString();
00479
00480 QScriptValue appletValue = engine->globalObject().property("plasmoid");
00481
00482
00483 QObject *appletObject = appletValue.toQObject();
00484 if (!appletObject) {
00485 return context->throwError(i18n("Could not extract the AppletObject"));
00486 }
00487
00488 AppletInterface *interface = qobject_cast<AppletInterface*>(appletObject);
00489 if (!interface) {
00490 return context->throwError(i18n("Could not extract the Applet"));
00491 }
00492
00493 DataEngine *data = interface->dataEngine(dataEngine);
00494 return engine->newQObject(data);
00495 }
00496
00497 QScriptValue SimpleJavaScriptApplet::service(QScriptContext *context, QScriptEngine *engine)
00498 {
00499 if (context->argumentCount() != 2) {
00500 return context->throwError(i18n("Service takes two arguments"));
00501 }
00502
00503 QString dataEngine = context->argument(0).toString();
00504
00505 QScriptValue appletValue = engine->globalObject().property("plasmoid");
00506
00507
00508 QObject *appletObject = appletValue.toQObject();
00509 if (!appletObject) {
00510 return context->throwError(i18n("Could not extract the AppletObject"));
00511 }
00512
00513 AppletInterface *interface = qobject_cast<AppletInterface*>(appletObject);
00514 if (!interface) {
00515 return context->throwError(i18n("Could not extract the Applet"));
00516 }
00517
00518 DataEngine *data = interface->dataEngine(dataEngine);
00519 QString source = context->argument(1).toString();
00520 Service *service = data->serviceForSource(source);
00521
00522 return engine->newQObject(service);
00523 }
00524
00525 QScriptValue SimpleJavaScriptApplet::loadui(QScriptContext *context, QScriptEngine *engine)
00526 {
00527 if (context->argumentCount() != 1) {
00528 return context->throwError(i18n("loadUI takes one argument"));
00529 }
00530
00531 QString filename = context->argument(0).toString();
00532 QFile f(filename);
00533 if (!f.open(QIODevice::ReadOnly)) {
00534 return context->throwError(i18n("Unable to open '%1'",filename));
00535 }
00536
00537 QUiLoader loader;
00538 QWidget *w = loader.load(&f);
00539 f.close();
00540
00541 return engine->newQObject(w);
00542 }
00543
00544 QString SimpleJavaScriptApplet::findSvg(QScriptEngine *engine, const QString &file)
00545 {
00546 QScriptValue appletValue = engine->globalObject().property("plasmoid");
00547
00548
00549 QObject *appletObject = appletValue.toQObject();
00550 if (!appletObject) {
00551 return file;
00552 }
00553
00554 AppletInterface *interface = qobject_cast<AppletInterface*>(appletObject);
00555 if (!interface) {
00556 return file;
00557 }
00558
00559 QString path = interface->package()->filePath("images", file + ".svg");
00560 if (path.isEmpty()) {
00561 path = interface->package()->filePath("images", file + ".svgz");
00562
00563 if (path.isEmpty()) {
00564 return file;
00565 }
00566 }
00567
00568 return path;
00569 }
00570
00571 QScriptValue SimpleJavaScriptApplet::newPlasmaSvg(QScriptContext *context, QScriptEngine *engine)
00572 {
00573 if (context->argumentCount() == 0) {
00574 return context->throwError(i18n("Constructor takes at least 1 argument"));
00575 }
00576
00577 QString filename = context->argument(0).toString();
00578 QObject *parent = 0;
00579
00580 if (context->argumentCount() == 2) {
00581 parent = qscriptvalue_cast<QObject *>(context->argument(1));
00582 }
00583
00584 bool parentedToApplet = false;
00585 if (!parent) {
00586 QScriptValue appletValue = engine->globalObject().property("plasmoid");
00587
00588
00589 QObject *appletObject = appletValue.toQObject();
00590 if (appletObject) {
00591 AppletInterface *interface = qobject_cast<AppletInterface*>(appletObject);
00592 if (interface) {
00593 parentedToApplet = true;
00594 parent = interface->applet();
00595 }
00596 }
00597 }
00598
00599 Svg *svg = new Svg(parent);
00600 svg->setImagePath(parentedToApplet ? filename : findSvg(engine, filename));
00601 return engine->newQObject(svg);
00602 }
00603
00604 QScriptValue SimpleJavaScriptApplet::newPlasmaFrameSvg(QScriptContext *context, QScriptEngine *engine)
00605 {
00606 if (context->argumentCount() == 0) {
00607 return context->throwError(i18n("Constructor takes at least 1 argument"));
00608 }
00609
00610 QString filename = context->argument(0).toString();
00611 QObject *parent = 0;
00612
00613 if (context->argumentCount() == 2) {
00614 parent = qscriptvalue_cast<QObject *>(context->argument(1));
00615 }
00616
00617 bool parentedToApplet = false;
00618 if (!parent) {
00619 QScriptValue appletValue = engine->globalObject().property("plasmoid");
00620
00621
00622 QObject *appletObject = appletValue.toQObject();
00623 if (appletObject) {
00624 AppletInterface *interface = qobject_cast<AppletInterface*>(appletObject);
00625 if (interface) {
00626 parentedToApplet = true;
00627 parent = interface->applet();
00628 }
00629 }
00630 }
00631
00632 FrameSvg *frameSvg = new FrameSvg(parent);
00633 frameSvg->setImagePath(parentedToApplet ? filename : findSvg(engine, filename));
00634 return engine->newQObject(frameSvg);
00635 }
00636
00637 void SimpleJavaScriptApplet::installWidgets(QScriptEngine *engine)
00638 {
00639 QScriptValue globalObject = engine->globalObject();
00640 if (!s_widgetLoader) {
00641 s_widgetLoader = new UiLoader;
00642 }
00643
00644 foreach (const QString &widget, s_widgetLoader->availableWidgets()) {
00645 QScriptValue fun = engine->newFunction(createWidget);
00646 QScriptValue name = engine->toScriptValue(widget);
00647 fun.setProperty(QString("functionName"), name,
00648 QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
00649 fun.setProperty(QString("prototype"), createPrototype(engine, name.toString()));
00650
00651 globalObject.setProperty(widget, fun);
00652 }
00653 }
00654
00655 QScriptValue SimpleJavaScriptApplet::createWidget(QScriptContext *context, QScriptEngine *engine)
00656 {
00657 if (context->argumentCount() > 1) {
00658 return context->throwError(i18n("CreateWidget takes one argument"));
00659 }
00660
00661 QGraphicsWidget *parent = 0;
00662 if (context->argumentCount()) {
00663 parent = qscriptvalue_cast<QGraphicsWidget*>(context->argument(0));
00664
00665 if (!parent) {
00666 return context->throwError(i18n("The parent must be a QGraphicsWidget"));
00667 }
00668 }
00669
00670 QString self = context->callee().property("functionName").toString();
00671 if (!s_widgetLoader) {
00672 s_widgetLoader = new UiLoader;
00673 }
00674
00675 QGraphicsWidget *w = s_widgetLoader->createWidget(self, parent);
00676
00677 if (!w) {
00678 return QScriptValue();
00679 }
00680
00681 QScriptValue fun = engine->newQObject(w);
00682 fun.setPrototype(context->callee().property("prototype"));
00683
00684 return fun;
00685 }
00686
00687 QScriptValue SimpleJavaScriptApplet::print(QScriptContext *context, QScriptEngine *engine)
00688 {
00689 if (context->argumentCount() != 1) {
00690 return context->throwError(i18n("print takes one argument"));
00691 }
00692
00693 kDebug() << context->argument(0).toString();
00694 return engine->undefinedValue();
00695 }
00696
00697 QScriptValue SimpleJavaScriptApplet::createPrototype(QScriptEngine *engine, const QString &name)
00698 {
00699 Q_UNUSED(name)
00700 QScriptValue proto = engine->newObject();
00701
00702
00703 return proto;
00704 }
00705
00706 K_EXPORT_PLASMA_APPLETSCRIPTENGINE(qscriptapplet, SimpleJavaScriptApplet)
00707
00708 #include "simplejavascriptapplet.moc"
00709
00710