add more spacing
[personal-kdebase.git] / workspace / plasma / scriptengines / javascript / simplejavascriptapplet.cpp
blob302b218ef77d664180b88610e6a53fe5d3ce1a31
1 /*
2 * Copyright 2007-2008 Richard J. Moore <rich@kde.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Library General Public License version 2 as
6 * published by the Free Software Foundation
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details
13 * You should have received a copy of the GNU Library General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include "simplejavascriptapplet.h"
21 #include <QScriptEngine>
22 #include <QFile>
23 #include <QUiLoader>
24 #include <QGraphicsLayout>
25 #include <QWidget>
27 #include <KDebug>
28 #include <KLocale>
29 #include <KStandardDirs>
30 #include <KConfigGroup>
32 #include <Plasma/Applet>
33 #include <Plasma/Svg>
34 #include <Plasma/FrameSvg>
35 #include <Plasma/Package>
37 #include "appletinterface.h"
39 using namespace Plasma;
41 #include "bind_dataengine.h"
43 Q_DECLARE_METATYPE(QPainter*)
44 Q_DECLARE_METATYPE(QStyleOptionGraphicsItem*)
45 Q_DECLARE_METATYPE(SimpleJavaScriptApplet*)
46 Q_DECLARE_METATYPE(AppletInterface*)
47 Q_DECLARE_METATYPE(Applet*)
48 Q_DECLARE_METATYPE(QGraphicsWidget*)
49 Q_DECLARE_METATYPE(QGraphicsLayout*)
50 Q_DECLARE_METATYPE(KConfigGroup)
52 Q_SCRIPT_DECLARE_QMETAOBJECT(AppletInterface, SimpleJavaScriptApplet*)
54 QScriptValue constructPainterClass(QScriptEngine *engine);
55 QScriptValue constructGraphicsItemClass(QScriptEngine *engine);
56 QScriptValue constructLinearLayoutClass(QScriptEngine *engine);
57 QScriptValue constructTimerClass(QScriptEngine *engine);
58 QScriptValue constructFontClass(QScriptEngine *engine);
59 QScriptValue constructQRectFClass(QScriptEngine *engine);
60 QScriptValue constructQPointClass(QScriptEngine *engine);
61 QScriptValue constructQSizeFClass(QScriptEngine *engine);
63 class DummyService : public Service
65 public:
66 ServiceJob *createJob(const QString &operation, QMap<QString, QVariant> &parameters)
68 Q_UNUSED(operation)
69 Q_UNUSED(parameters)
70 return 0;
75 * Workaround the fact that QtScripts handling of variants seems a bit broken.
77 QScriptValue variant2ScriptValue(QScriptEngine *engine, QVariant var)
79 if (var.isNull()) {
80 return engine->nullValue();
83 switch(var.type())
85 case QVariant::Invalid:
86 return engine->nullValue();
87 case QVariant::Bool:
88 return QScriptValue(engine, var.toBool());
89 case QVariant::Date:
90 return engine->newDate(var.toDateTime());
91 case QVariant::DateTime:
92 return engine->newDate(var.toDateTime());
93 case QVariant::Double:
94 return QScriptValue(engine, var.toDouble());
95 case QVariant::Int:
96 case QVariant::LongLong:
97 return QScriptValue(engine, var.toInt());
98 case QVariant::String:
99 return QScriptValue(engine, var.toString());
100 case QVariant::Time: {
101 QDateTime t(QDate::currentDate(), var.toTime());
102 return engine->newDate(t);
104 case QVariant::UInt:
105 return QScriptValue(engine, var.toUInt());
106 default:
107 break;
110 return qScriptValueFromValue(engine, var);
113 QScriptValue qScriptValueFromData(QScriptEngine *engine, const DataEngine::Data &data)
115 DataEngine::Data::const_iterator begin = data.begin();
116 DataEngine::Data::const_iterator end = data.end();
117 DataEngine::Data::const_iterator it;
119 QScriptValue obj = engine->newObject();
121 for (it = begin; it != end; ++it) {
122 //kDebug() << "setting" << it.key() << "to" << it.value();
123 QString prop = it.key();
124 prop.replace(' ', '_');
125 obj.setProperty(prop, variant2ScriptValue(engine, it.value()));
128 return obj;
131 QScriptValue qScriptValueFromKConfigGroup(QScriptEngine *engine, const KConfigGroup &config)
133 QScriptValue obj = engine->newObject();
135 if (!config.isValid()) {
136 return obj;
139 QMap<QString, QString> entryMap = config.entryMap();
140 QMap<QString, QString>::const_iterator it = entryMap.constBegin();
141 QMap<QString, QString>::const_iterator begin = it;
142 QMap<QString, QString>::const_iterator end = entryMap.constEnd();
144 //setting the group name
145 obj.setProperty("__name", QScriptValue(engine, config.name()));
147 //setting the key/value pairs
148 for (it = begin; it != end; ++it) {
149 //kDebug() << "setting" << it.key() << "to" << it.value();
150 QString prop = it.key();
151 prop.replace(' ', '_');
152 obj.setProperty(prop, variant2ScriptValue(engine, it.value()));
155 return obj;
158 void kConfigGroupFromScriptValue(const QScriptValue& obj, KConfigGroup &config)
160 KConfigSkeleton *skel = new KConfigSkeleton();
161 config = KConfigGroup(skel->config(), obj.property("__name").toString());
163 QScriptValueIterator it(obj);
165 while (it.hasNext()) {
166 it.next();
167 //kDebug() << it.name() << "is" << it.value().toString();
168 if (it.name() != "__name") {
169 config.writeEntry(it.name(), it.value().toString());
174 KSharedPtr<UiLoader> SimpleJavaScriptApplet::s_widgetLoader;
176 SimpleJavaScriptApplet::SimpleJavaScriptApplet(QObject *parent, const QVariantList &args)
177 : Plasma::AppletScript(parent)
179 //kDebug() << "Script applet launched, args" << args;
181 m_engine = new QScriptEngine(this);
182 importExtensions();
185 SimpleJavaScriptApplet::~SimpleJavaScriptApplet()
187 if (s_widgetLoader.count() == 1) {
188 s_widgetLoader.clear();
192 void SimpleJavaScriptApplet::reportError()
194 kDebug() << "Error: " << m_engine->uncaughtException().toString()
195 << " at line " << m_engine->uncaughtExceptionLineNumber() << endl;
196 kDebug() << m_engine->uncaughtExceptionBacktrace();
199 void SimpleJavaScriptApplet::showConfigurationInterface()
201 kDebug() << "Script: showConfigurationInterface";
203 // Here we'll load a ui file...
204 QScriptValue global = m_engine->globalObject();
206 QScriptValue fun = m_self.property("showConfigurationInterface");
207 if (!fun.isFunction()) {
208 kDebug() << "Script: ShowConfiguratioInterface is not a function, " << fun.toString();
209 return;
212 QScriptContext *ctx = m_engine->pushContext();
213 ctx->setActivationObject(m_self);
214 fun.call(m_self);
215 m_engine->popContext();
217 if (m_engine->hasUncaughtException()) {
218 reportError();
222 void SimpleJavaScriptApplet::configAccepted()
224 QScriptValue fun = m_self.property("configAccepted");
225 if (!fun.isFunction()) {
226 kDebug() << "Script: configAccepted is not a function, " << fun.toString();
227 return;
230 QScriptContext *ctx = m_engine->pushContext();
231 ctx->setActivationObject(m_self);
232 fun.call(m_self);
233 m_engine->popContext();
235 if (m_engine->hasUncaughtException()) {
236 reportError();
240 void SimpleJavaScriptApplet::dataUpdated(const QString &name, const DataEngine::Data &data)
242 QScriptValue fun = m_self.property("dataUpdate");
243 if (!fun.isFunction()) {
244 kDebug() << "Script: dataUpdate is not a function, " << fun.toString();
245 return;
248 QScriptValueList args;
249 args << m_engine->toScriptValue(name) << m_engine->toScriptValue(data);
251 QScriptContext *ctx = m_engine->pushContext();
252 ctx->setActivationObject(m_self);
253 fun.call(m_self, args);
254 m_engine->popContext();
256 if (m_engine->hasUncaughtException()) {
257 reportError();
261 void SimpleJavaScriptApplet::executeAction(const QString &name)
263 callFunction("action_" + name);
265 QScriptValue fun = m_self.property("action_" + name);
266 if (fun.isFunction()) {
267 QScriptContext *ctx = m_engine->pushContext();
268 ctx->setActivationObject(m_self);
269 fun.call(m_self);
270 m_engine->popContext();
272 if (m_engine->hasUncaughtException()) {
273 reportError();
278 void SimpleJavaScriptApplet::paintInterface(QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
280 Q_UNUSED(option)
281 Q_UNUSED(contentsRect)
283 //kDebug() << "paintInterface() (c++)";
284 QScriptValue fun = m_self.property("paintInterface");
285 if (!fun.isFunction()) {
286 //kDebug() << "Script: paintInterface is not a function, " << fun.toString();
287 AppletScript::paintInterface(p, option, contentsRect);
288 return;
291 QScriptValueList args;
292 args << m_engine->toScriptValue(p);
293 args << m_engine->toScriptValue(const_cast<QStyleOptionGraphicsItem*>(option));
294 args << m_engine->toScriptValue(contentsRect);
296 QScriptContext *ctx = m_engine->pushContext();
297 ctx->setActivationObject(m_self);
298 fun.call(m_self, args);
299 m_engine->popContext();
301 if (m_engine->hasUncaughtException()) {
302 reportError();
306 QList<QAction*> SimpleJavaScriptApplet::contextualActions()
308 return m_interface->contextualActions();
311 void SimpleJavaScriptApplet::callFunction(const QString &functionName, const QScriptValueList &args)
313 QScriptValue fun = m_self.property(functionName);
314 if (fun.isFunction()) {
315 QScriptContext *ctx = m_engine->pushContext();
316 ctx->setActivationObject(m_self);
317 fun.call(m_self, args);
318 m_engine->popContext();
320 if (m_engine->hasUncaughtException()) {
321 reportError();
326 void SimpleJavaScriptApplet::constraintsEvent(Plasma::Constraints constraints)
328 QString functionName;
330 if (constraints & Plasma::FormFactorConstraint) {
331 callFunction("formFactorChanged");
334 if (constraints & Plasma::LocationConstraint) {
335 callFunction("locationChanged");
338 if (constraints & Plasma::ContextConstraint) {
339 callFunction("contextChanged");
343 bool SimpleJavaScriptApplet::init()
345 setupObjects();
347 kDebug() << "ScriptName:" << applet()->name();
348 kDebug() << "ScriptCategory:" << applet()->category();
350 QFile file(mainScript());
351 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
352 kWarning() << "Unable to load script file";
353 return false;
356 QString script = file.readAll();
357 //kDebug() << "Script says" << script;
359 m_engine->evaluate(script);
360 if (m_engine->hasUncaughtException()) {
361 reportError();
362 return false;
365 return true;
368 void SimpleJavaScriptApplet::importExtensions()
370 return; // no extension, so do bother wasting cycles
373 QStringList extensions;
374 //extensions << "qt.core" << "qt.gui" << "qt.svg" << "qt.xml" << "qt.plasma";
375 //extensions << "qt.core" << "qt.gui" << "qt.xml";
376 foreach (const QString &ext, extensions) {
377 kDebug() << "importing " << ext << "...";
378 QScriptValue ret = m_engine->importExtension(ext);
379 if (ret.isError()) {
380 kDebug() << "failed to import extension" << ext << ":" << ret.toString();
383 kDebug() << "done importing extensions.";
387 void SimpleJavaScriptApplet::setupObjects()
389 QScriptValue global = m_engine->globalObject();
391 // Bindings for data engine
392 m_engine->setDefaultPrototype(qMetaTypeId<DataEngine*>(), m_engine->newQObject(new DataEngine()));
393 m_engine->setDefaultPrototype(qMetaTypeId<Service*>(), m_engine->newQObject(new DummyService()));
394 m_engine->setDefaultPrototype(qMetaTypeId<ServiceJob*>(), m_engine->newQObject(new ServiceJob(QString(), QString(), QMap<QString, QVariant>())));
396 global.setProperty("dataEngine", m_engine->newFunction(SimpleJavaScriptApplet::dataEngine));
397 global.setProperty("service", m_engine->newFunction(SimpleJavaScriptApplet::service));
398 qScriptRegisterMetaType<DataEngine::Data>(m_engine, qScriptValueFromData, 0, QScriptValue());
399 qScriptRegisterMetaType<KConfigGroup>(m_engine, qScriptValueFromKConfigGroup, kConfigGroupFromScriptValue, QScriptValue());
401 // Expose applet interface
402 m_interface = new AppletInterface(this);
403 m_self = m_engine->newQObject(m_interface);
404 m_self.setScope(global);
405 global.setProperty("plasmoid", m_self);
407 //manually create enum values. ugh
408 QMetaObject meta = AppletInterface::staticMetaObject;
409 for (int i=0; i < meta.enumeratorCount(); ++i) {
410 QMetaEnum e = meta.enumerator(i);
411 //kDebug() << e.name();
412 for (int i=0; i < e.keyCount(); ++i) {
413 //kDebug() << e.key(i) << e.value(i);
414 global.setProperty(e.key(i), QScriptValue(m_engine, e.value(i)));
418 // Add a global loadui method for ui files
419 QScriptValue fun = m_engine->newFunction(SimpleJavaScriptApplet::loadui);
420 global.setProperty("loadui", fun);
422 fun = m_engine->newFunction(SimpleJavaScriptApplet::print);
423 global.setProperty("print", fun);
426 // Work around bug in 4.3.0
427 qMetaTypeId<QVariant>();
429 // Add constructors
430 global.setProperty("PlasmaSvg", m_engine->newFunction(SimpleJavaScriptApplet::newPlasmaSvg));
431 global.setProperty("PlasmaFrameSvg", m_engine->newFunction(SimpleJavaScriptApplet::newPlasmaFrameSvg));
433 // Add stuff from 4.4
434 global.setProperty("QPainter", constructPainterClass(m_engine));
435 global.setProperty("QGraphicsItem", constructGraphicsItemClass(m_engine));
436 global.setProperty("QTimer", constructTimerClass(m_engine));
437 global.setProperty("QFont", constructFontClass(m_engine));
438 global.setProperty("QRectF", constructQRectFClass(m_engine));
439 global.setProperty("QSizeF", constructQSizeFClass(m_engine));
440 global.setProperty("QPoint", constructQPointClass(m_engine));
441 global.setProperty("LinearLayout", constructLinearLayoutClass(m_engine));
443 installWidgets(m_engine);
446 QString SimpleJavaScriptApplet::findDataResource(const QString &filename)
448 QString path("plasma-script/%1");
449 return KGlobal::dirs()->findResource("data", path.arg(filename));
452 void SimpleJavaScriptApplet::debug(const QString &msg)
454 kDebug() << msg;
457 #if 0
458 QScriptValue SimpleJavaScriptApplet::dataEngine(QScriptContext *context, QScriptEngine *engine)
460 if (context->argumentCount() != 1)
461 return context->throwError("dataEngine takes one argument");
463 QString dataEngine = context->argument(0).toString();
465 Script *self = engine->fromScriptValue<Script*>(context->thisObject());
467 DataEngine *data = self->dataEngine(dataEngine);
468 return engine->newQObject(data);
470 #endif
472 QScriptValue SimpleJavaScriptApplet::dataEngine(QScriptContext *context, QScriptEngine *engine)
474 if (context->argumentCount() != 1) {
475 return context->throwError(i18n("DataEngine takes one argument"));
478 QString dataEngine = context->argument(0).toString();
480 QScriptValue appletValue = engine->globalObject().property("plasmoid");
481 //kDebug() << "appletValue is " << appletValue.toString();
483 QObject *appletObject = appletValue.toQObject();
484 if (!appletObject) {
485 return context->throwError(i18n("Could not extract the AppletObject"));
488 AppletInterface *interface = qobject_cast<AppletInterface*>(appletObject);
489 if (!interface) {
490 return context->throwError(i18n("Could not extract the Applet"));
493 DataEngine *data = interface->dataEngine(dataEngine);
494 return engine->newQObject(data);
497 QScriptValue SimpleJavaScriptApplet::service(QScriptContext *context, QScriptEngine *engine)
499 if (context->argumentCount() != 2) {
500 return context->throwError(i18n("Service takes two arguments"));
503 QString dataEngine = context->argument(0).toString();
505 QScriptValue appletValue = engine->globalObject().property("plasmoid");
506 //kDebug() << "appletValue is " << appletValue.toString();
508 QObject *appletObject = appletValue.toQObject();
509 if (!appletObject) {
510 return context->throwError(i18n("Could not extract the AppletObject"));
513 AppletInterface *interface = qobject_cast<AppletInterface*>(appletObject);
514 if (!interface) {
515 return context->throwError(i18n("Could not extract the Applet"));
518 DataEngine *data = interface->dataEngine(dataEngine);
519 QString source = context->argument(1).toString();
520 Service *service = data->serviceForSource(source);
521 //kDebug( )<< "lets try to get" << source << "from" << dataEngine;
522 return engine->newQObject(service);
525 QScriptValue SimpleJavaScriptApplet::loadui(QScriptContext *context, QScriptEngine *engine)
527 if (context->argumentCount() != 1) {
528 return context->throwError(i18n("loadUI takes one argument"));
531 QString filename = context->argument(0).toString();
532 QFile f(filename);
533 if (!f.open(QIODevice::ReadOnly)) {
534 return context->throwError(i18n("Unable to open '%1'",filename));
537 QUiLoader loader;
538 QWidget *w = loader.load(&f);
539 f.close();
541 return engine->newQObject(w);
544 QString SimpleJavaScriptApplet::findSvg(QScriptEngine *engine, const QString &file)
546 QScriptValue appletValue = engine->globalObject().property("plasmoid");
547 //kDebug() << "appletValue is " << appletValue.toString();
549 QObject *appletObject = appletValue.toQObject();
550 if (!appletObject) {
551 return file;
554 AppletInterface *interface = qobject_cast<AppletInterface*>(appletObject);
555 if (!interface) {
556 return file;
559 QString path = interface->package()->filePath("images", file + ".svg");
560 if (path.isEmpty()) {
561 path = interface->package()->filePath("images", file + ".svgz");
563 if (path.isEmpty()) {
564 return file;
568 return path;
571 QScriptValue SimpleJavaScriptApplet::newPlasmaSvg(QScriptContext *context, QScriptEngine *engine)
573 if (context->argumentCount() == 0) {
574 return context->throwError(i18n("Constructor takes at least 1 argument"));
577 QString filename = context->argument(0).toString();
578 QObject *parent = 0;
580 if (context->argumentCount() == 2) {
581 parent = qscriptvalue_cast<QObject *>(context->argument(1));
584 bool parentedToApplet = false;
585 if (!parent) {
586 QScriptValue appletValue = engine->globalObject().property("plasmoid");
587 //kDebug() << "appletValue is " << appletValue.toString();
589 QObject *appletObject = appletValue.toQObject();
590 if (appletObject) {
591 AppletInterface *interface = qobject_cast<AppletInterface*>(appletObject);
592 if (interface) {
593 parentedToApplet = true;
594 parent = interface->applet();
599 Svg *svg = new Svg(parent);
600 svg->setImagePath(parentedToApplet ? filename : findSvg(engine, filename));
601 return engine->newQObject(svg);
604 QScriptValue SimpleJavaScriptApplet::newPlasmaFrameSvg(QScriptContext *context, QScriptEngine *engine)
606 if (context->argumentCount() == 0) {
607 return context->throwError(i18n("Constructor takes at least 1 argument"));
610 QString filename = context->argument(0).toString();
611 QObject *parent = 0;
613 if (context->argumentCount() == 2) {
614 parent = qscriptvalue_cast<QObject *>(context->argument(1));
617 bool parentedToApplet = false;
618 if (!parent) {
619 QScriptValue appletValue = engine->globalObject().property("plasmoid");
620 //kDebug() << "appletValue is " << appletValue.toString();
622 QObject *appletObject = appletValue.toQObject();
623 if (appletObject) {
624 AppletInterface *interface = qobject_cast<AppletInterface*>(appletObject);
625 if (interface) {
626 parentedToApplet = true;
627 parent = interface->applet();
632 FrameSvg *frameSvg = new FrameSvg(parent);
633 frameSvg->setImagePath(parentedToApplet ? filename : findSvg(engine, filename));
634 return engine->newQObject(frameSvg);
637 void SimpleJavaScriptApplet::installWidgets(QScriptEngine *engine)
639 QScriptValue globalObject = engine->globalObject();
640 if (!s_widgetLoader) {
641 s_widgetLoader = new UiLoader;
644 foreach (const QString &widget, s_widgetLoader->availableWidgets()) {
645 QScriptValue fun = engine->newFunction(createWidget);
646 QScriptValue name = engine->toScriptValue(widget);
647 fun.setProperty(QString("functionName"), name,
648 QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);
649 fun.setProperty(QString("prototype"), createPrototype(engine, name.toString()));
651 globalObject.setProperty(widget, fun);
655 QScriptValue SimpleJavaScriptApplet::createWidget(QScriptContext *context, QScriptEngine *engine)
657 if (context->argumentCount() > 1) {
658 return context->throwError(i18n("CreateWidget takes one argument"));
661 QGraphicsWidget *parent = 0;
662 if (context->argumentCount()) {
663 parent = qscriptvalue_cast<QGraphicsWidget*>(context->argument(0));
665 if (!parent) {
666 return context->throwError(i18n("The parent must be a QGraphicsWidget"));
670 QString self = context->callee().property("functionName").toString();
671 if (!s_widgetLoader) {
672 s_widgetLoader = new UiLoader;
675 QGraphicsWidget *w = s_widgetLoader->createWidget(self, parent);
677 if (!w) {
678 return QScriptValue();
681 QScriptValue fun = engine->newQObject(w);
682 fun.setPrototype(context->callee().property("prototype"));
684 return fun;
687 QScriptValue SimpleJavaScriptApplet::print(QScriptContext *context, QScriptEngine *engine)
689 if (context->argumentCount() != 1) {
690 return context->throwError(i18n("print takes one argument"));
693 kDebug() << context->argument(0).toString();
694 return engine->undefinedValue();
697 QScriptValue SimpleJavaScriptApplet::createPrototype(QScriptEngine *engine, const QString &name)
699 Q_UNUSED(name)
700 QScriptValue proto = engine->newObject();
702 // Hook for adding extra properties/methods
703 return proto;
706 K_EXPORT_PLASMA_APPLETSCRIPTENGINE(qscriptapplet, SimpleJavaScriptApplet)
708 #include "simplejavascriptapplet.moc"