/* * Copyright 2009 by Alan Alpert * Copyright 2010 by Ménard Alexis * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "plasmoid/declarativeappletscript.h" #include "engineaccess.h" #include "plasmoid/appletinterface.h" #include "plasmoid/themedsvg.h" #include "common/scriptenv.h" #include "declarative/packageaccessmanagerfactory.h" #include "simplebindings/bytearrayclass.h" #include "simplebindings/dataenginereceiver.h" #include "simplebindings/i18n.h" K_EXPORT_PLASMA_APPLETSCRIPTENGINE(declarativeappletscript, DeclarativeAppletScript) QScriptValue constructIconClass(QScriptEngine *engine); QScriptValue constructKUrlClass(QScriptEngine *engine); void registerSimpleAppletMetaTypes(QScriptEngine *engine); void registerNonGuiMetaTypes(QScriptEngine *engine); DeclarativeAppletScript::DeclarativeAppletScript(QObject *parent, const QVariantList &args) : AbstractJsAppletScript(parent, args), m_engine(0), m_env(0), m_auth(this) { Q_UNUSED(args); } DeclarativeAppletScript::~DeclarativeAppletScript() { } bool DeclarativeAppletScript::init() { m_declarativeWidget = new Plasma::DeclarativeWidget(applet()); m_declarativeWidget->setInitializationDelayed(true); //make possible to import extensions from the package //FIXME: probably to be removed, would make possible to use native code from within the package :/ m_declarativeWidget->engine()->addImportPath(package()->path()+"/contents/imports"); //use our own custom network access manager that will acces Plasma packages and to manage security (i.e. deny access to remote stuff when the proper extension isn't enabled m_declarativeWidget->engine()->setNetworkAccessManagerFactory(new PackageAccessManagerFactory(package(), &m_auth)); m_declarativeWidget->setQmlPath(mainScript()); if (!m_declarativeWidget->engine() || !m_declarativeWidget->engine()->rootContext() || !m_declarativeWidget->engine()->rootContext()->isValid() || m_declarativeWidget->mainComponent()->isError()) { QString reason; foreach (QDeclarativeError error, m_declarativeWidget->mainComponent()->errors()) { reason += error.toString(); } setFailedToLaunch(true, reason); return false; } Plasma::Applet *a = applet(); Plasma::PopupApplet *pa = qobject_cast(a); if (pa) { pa->setPopupIcon(a->icon()); pa->setGraphicsWidget(m_declarativeWidget); } else { QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(a); lay->setContentsMargins(0, 0, 0, 0); lay->addItem(m_declarativeWidget); } m_interface = pa ? new PopupAppletInterface(this) : new AppletInterface(this); m_engineAccess = new EngineAccess(this); m_declarativeWidget->engine()->rootContext()->setContextProperty("__engineAccess", m_engineAccess); connect(applet(), SIGNAL(extenderItemRestored(Plasma::ExtenderItem*)), this, SLOT(extenderItemRestored(Plasma::ExtenderItem*))); connect(applet(), SIGNAL(activate()), this, SLOT(activate())); //Glorious hack:steal the engine QDeclarativeExpression *expr = new QDeclarativeExpression(m_declarativeWidget->engine()->rootContext(), m_declarativeWidget->rootObject(), "__engineAccess.setEngine(this)"); expr->evaluate(); delete expr; return true; } void DeclarativeAppletScript::collectGarbage() { m_engine->collectGarbage(); } QString DeclarativeAppletScript::filePath(const QString &type, const QString &file) const { const QString path = m_env->filePathFromScriptContext(type.toLocal8Bit().constData(), file); if (!path.isEmpty()) { return path; } return package()->filePath(type.toLocal8Bit().constData(), file); } void DeclarativeAppletScript::configChanged() { if (!m_env) { return; } m_env->callEventListeners("configchanged"); } QScriptValue DeclarativeAppletScript::loadui(QScriptContext *context, QScriptEngine *engine) { if (context->argumentCount() != 1) { return context->throwError(i18n("loadui() takes one argument")); } QString filename = context->argument(0).toString(); QFile f(filename); if (!f.open(QIODevice::ReadOnly)) { return context->throwError(i18n("Unable to open '%1'",filename)); } QUiLoader loader; QWidget *w = loader.load(&f); f.close(); return engine->newQObject(w, QScriptEngine::AutoOwnership); } QScriptValue DeclarativeAppletScript::newPlasmaSvg(QScriptContext *context, QScriptEngine *engine) { if (context->argumentCount() == 0) { return context->throwError(i18n("Constructor takes at least 1 argument")); } const QString filename = context->argument(0).toString(); Plasma::Svg *svg = new ThemedSvg(0); svg->setImagePath(ThemedSvg::findSvg(engine, filename)); QScriptValue obj = engine->newQObject(svg); ScriptEnv::registerEnums(obj, *svg->metaObject()); return obj; } QScriptValue DeclarativeAppletScript::variantToScriptValue(QVariant var) { return m_engine->newVariant(var); } QScriptValue DeclarativeAppletScript::newPlasmaFrameSvg(QScriptContext *context, QScriptEngine *engine) { if (context->argumentCount() == 0) { return context->throwError(i18n("Constructor takes at least 1 argument")); } QString filename = context->argument(0).toString(); bool parentedToApplet = false; QGraphicsWidget *parent = extractParent(context, engine, 1, &parentedToApplet); Plasma::FrameSvg *frameSvg = new ThemedFrameSvg(parent); frameSvg->setImagePath(ThemedSvg::findSvg(engine, filename)); QScriptValue obj = engine->newQObject(frameSvg); ScriptEnv::registerEnums(obj, *frameSvg->metaObject()); return obj; } QScriptValue DeclarativeAppletScript::newPlasmaExtenderItem(QScriptContext *context, QScriptEngine *engine) { Plasma::Extender *extender = 0; if (context->argumentCount() > 0) { extender = qobject_cast(context->argument(0).toQObject()); } if (!extender) { AppletInterface *interface = AppletInterface::extract(engine); if (!interface) { engine->undefinedValue(); } extender = interface->extender(); } Plasma::ExtenderItem *extenderItem = new Plasma::ExtenderItem(extender); QScriptValue fun = engine->newQObject(extenderItem); ScriptEnv::registerEnums(fun, *extenderItem->metaObject()); return fun; } QGraphicsWidget *DeclarativeAppletScript::extractParent(QScriptContext *context, QScriptEngine *engine, int argIndex, bool *parentedToApplet) { if (parentedToApplet) { *parentedToApplet = false; } QGraphicsWidget *parent = 0; if (context->argumentCount() >= argIndex) { parent = qobject_cast(context->argument(argIndex).toQObject()); } if (!parent) { AppletInterface *interface = AppletInterface::extract(engine); if (!interface) { return 0; } //kDebug() << "got the applet!"; parent = interface->applet(); if (parentedToApplet) { *parentedToApplet = true; } } return parent; } void DeclarativeAppletScript::constraintsEvent(Plasma::Constraints constraints) { if (constraints & Plasma::FormFactorConstraint) { emit formFactorChanged(); } if (constraints & Plasma::LocationConstraint) { emit locationChanged(); } if (constraints & Plasma::ContextConstraint) { emit contextChanged(); } } void DeclarativeAppletScript::popupEvent(bool popped) { if (!m_env) { return; } QScriptValueList args; args << popped; m_env->callEventListeners("popupEvent", args); } void DeclarativeAppletScript::dataUpdated(const QString &name, const Plasma::DataEngine::Data &data) { QScriptValueList args; args << m_engine->toScriptValue(name) << m_engine->toScriptValue(data); m_env->callEventListeners("dataUpdated", args); } void DeclarativeAppletScript::extenderItemRestored(Plasma::ExtenderItem* item) { if (!m_env) { return; } QScriptValueList args; args << m_engine->newQObject(item, QScriptEngine::AutoOwnership, QScriptEngine::PreferExistingWrapperObject); m_env->callEventListeners("initExtenderItem", args); } void DeclarativeAppletScript::activate() { if (!m_env) { return; } m_env->callEventListeners("activate"); } void DeclarativeAppletScript::executeAction(const QString &name) { if (!m_env) { return; } const QString func("action_" + name); m_env->callEventListeners(func); } bool DeclarativeAppletScript::include(const QString &path) { return m_env->include(path); } ScriptEnv *DeclarativeAppletScript::scriptEnv() { return m_env; } void DeclarativeAppletScript::setupObjects() { QScriptValue global = m_engine->globalObject(); m_declarativeWidget->engine()->rootContext()->setContextProperty("__engineAccess", 0); m_engineAccess->deleteLater(); m_self = m_engine->newQObject(m_interface); m_self.setScope(global); global.setProperty("plasmoid", m_self); m_env->addMainObjectProperties(m_self); QScriptValue args = m_engine->newArray(); int i = 0; foreach (const QVariant &arg, applet()->startupArguments()) { args.setProperty(i, m_engine->newVariant(arg)); ++i; } global.setProperty("startupArguments", args); // Add a global loadui method for ui files QScriptValue fun = m_engine->newFunction(DeclarativeAppletScript::loadui); global.setProperty("loadui", fun); bindI18N(m_engine); global.setProperty("dataEngine", m_engine->newFunction(DeclarativeAppletScript::dataEngine)); global.setProperty("service", m_engine->newFunction(DeclarativeAppletScript::service)); global.setProperty("loadService", m_engine->newFunction(DeclarativeAppletScript::loadService)); //Add stuff from Qt ByteArrayClass *baClass = new ByteArrayClass(m_engine); global.setProperty("ByteArray", baClass->constructor()); global.setProperty("QIcon", constructIconClass(m_engine)); // Add stuff from KDE libs qScriptRegisterSequenceMetaType(m_engine); global.setProperty("Url", constructKUrlClass(m_engine)); // Add stuff from Plasma global.setProperty("Svg", m_engine->newFunction(DeclarativeAppletScript::newPlasmaSvg)); global.setProperty("FrameSvg", m_engine->newFunction(DeclarativeAppletScript::newPlasmaFrameSvg)); global.setProperty("ExtenderItem", m_engine->newFunction(DeclarativeAppletScript::newPlasmaExtenderItem)); } QScriptValue DeclarativeAppletScript::dataEngine(QScriptContext *context, QScriptEngine *engine) { if (context->argumentCount() != 1) { return context->throwError(i18n("dataEngine() takes one argument")); } AppletInterface *interface = AppletInterface::extract(engine); if (!interface) { return context->throwError(i18n("Could not extract the Applet")); } const QString dataEngineName = context->argument(0).toString(); Plasma::DataEngine *dataEngine = interface->dataEngine(dataEngineName); QScriptValue v = engine->newQObject(dataEngine, QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); v.setProperty("connectSource", engine->newFunction(DataEngineReceiver::connectSource)); v.setProperty("disconnectSource", engine->newFunction(DataEngineReceiver::disconnectSource)); return v; } QScriptValue DeclarativeAppletScript::service(QScriptContext *context, QScriptEngine *engine) { if (context->argumentCount() != 2) { return context->throwError(i18n("service() takes two arguments")); } QString dataEngine = context->argument(0).toString(); AppletInterface *interface = AppletInterface::extract(engine); if (!interface) { return context->throwError(i18n("Could not extract the Applet")); } Plasma::DataEngine *data = interface->dataEngine(dataEngine); QString source = context->argument(1).toString(); Plasma::Service *service = data->serviceForSource(source); //kDebug( )<< "lets try to get" << source << "from" << dataEngine; return engine->newQObject(service, QScriptEngine::AutoOwnership); } QScriptValue DeclarativeAppletScript::loadService(QScriptContext *context, QScriptEngine *engine) { if (context->argumentCount() != 1) { return context->throwError(i18n("service() takes one argument")); } QString pluginName = context->argument(0).toString(); AppletInterface *interface = AppletInterface::extract(engine); if (!interface) { return context->throwError(i18n("Could not extract the Applet")); } Plasma::Service *service = Plasma::Service::load(pluginName, interface); //kDebug( )<< "lets try to get" << source << "from" << dataEngine; return engine->newQObject(service, QScriptEngine::AutoOwnership); } void DeclarativeAppletScript::setEngine(QScriptValue &val) { if (val.engine() == m_engine) { return; } m_engine = val.engine(); connect(m_engine, SIGNAL(signalHandlerException(const QScriptValue &)), this, SLOT(signalHandlerException(const QScriptValue &))); QScriptValue originalGlobalObject = m_engine->globalObject(); QScriptValue newGlobalObject = m_engine->newObject(); QString eval = QLatin1String("eval"); QString version = QLatin1String("version"); { QScriptValueIterator iter(originalGlobalObject); QVector names; QVector values; QVector flags; while (iter.hasNext()) { iter.next(); QString name = iter.name(); if (name == version) { continue; } if (name != eval) { names.append(name); values.append(iter.value()); flags.append(iter.flags() | QScriptValue::Undeletable); } newGlobalObject.setProperty(iter.scriptName(), iter.value()); // m_illegalNames.insert(name); } } m_engine->setGlobalObject(newGlobalObject); delete m_env; m_env = new ScriptEnv(this, m_engine); //m_env->addMainObjectProperties(newGlobalObject); setupObjects(); if (!m_env->importExtensions(description(), m_self, m_auth)) { return; } qScriptRegisterSequenceMetaType(m_engine); registerNonGuiMetaTypes(m_engine); registerSimpleAppletMetaTypes(m_engine); QTimer::singleShot(0, this, SLOT(configChanged())); } void DeclarativeAppletScript::signalHandlerException(const QScriptValue &exception) { kWarning()<<"Exception caught: "<