File indexing completed on 2024-05-12 17:10:24
0001 /* 0002 SPDX-FileCopyrightText: 2009 Aaron Seigo <aseigo@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "scriptengine_v1.h" 0008 #include "debug.h" 0009 0010 #include <QDir> 0011 #include <QDirIterator> 0012 #include <QFile> 0013 #include <QFileInfo> 0014 #include <QFutureWatcher> 0015 #include <QJSValueIterator> 0016 #include <QStandardPaths> 0017 0018 #include <KApplicationTrader> 0019 #include <QDebug> 0020 #include <klocalizedstring.h> 0021 #include <kshell.h> 0022 0023 // KIO 0024 // #include <kemailsettings.h> // no camelcase include 0025 0026 #include <KPackage/Package> 0027 #include <KPackage/PackageLoader> 0028 #include <Plasma/Applet> 0029 #include <Plasma/Containment> 0030 #include <Plasma/PluginLoader> 0031 #include <qstandardpaths.h> 0032 0033 #include "../screenpool.h" 0034 #include "../standaloneappcorona.h" 0035 #include "appinterface.h" 0036 #include "configgroup.h" 0037 #include "containment.h" 0038 #include "panel.h" 0039 #include "widget.h" 0040 0041 namespace 0042 { 0043 template<typename T> 0044 inline void awaitFuture(const QFuture<T> &future) 0045 { 0046 while (!future.isFinished()) { 0047 QCoreApplication::processEvents(); 0048 } 0049 } 0050 0051 class ScriptArray_forEach_Helper 0052 { 0053 public: 0054 ScriptArray_forEach_Helper(const QJSValue &array) 0055 : array(array) 0056 { 0057 } 0058 0059 // operator + is commonly used for these things 0060 // to avoid having the lambda inside the parenthesis 0061 template<typename Function> 0062 void operator+(Function function) const 0063 { 0064 if (!array.isArray()) 0065 return; 0066 0067 int length = array.property("length").toInt(); 0068 for (int i = 0; i < length; ++i) { 0069 function(array.property(i)); 0070 } 0071 } 0072 0073 private: 0074 const QJSValue &array; 0075 }; 0076 0077 #define SCRIPT_ARRAY_FOREACH(Variable, Array) ScriptArray_forEach_Helper(Array) + [&](const QJSValue &Variable) 0078 0079 class ScriptObject_forEach_Helper 0080 { 0081 public: 0082 ScriptObject_forEach_Helper(const QJSValue &object) 0083 : object(object) 0084 { 0085 } 0086 0087 // operator + is commonly used for these things 0088 // to avoid having the lambda inside the parenthesis 0089 template<typename Function> 0090 void operator+(Function function) const 0091 { 0092 QJSValueIterator it(object); 0093 while (it.hasNext()) { 0094 it.next(); 0095 function(it.name(), it.value()); 0096 } 0097 } 0098 0099 private: 0100 const QJSValue &object; 0101 }; 0102 0103 #define SCRIPT_OBJECT_FOREACH(Key, Value, Array) ScriptObject_forEach_Helper(Array) + [&](const QString &Key, const QJSValue &Value) 0104 0105 // Case insensitive comparison of two strings 0106 template<typename StringType> 0107 inline bool matches(const QString &object, const StringType &string) 0108 { 0109 return object.compare(string, Qt::CaseInsensitive) == 0; 0110 } 0111 } 0112 0113 namespace WorkspaceScripting 0114 { 0115 ScriptEngine::V1::V1(ScriptEngine *parent) 0116 : QObject(parent) 0117 , m_engine(parent) 0118 { 0119 } 0120 0121 ScriptEngine::V1::~V1() 0122 { 0123 } 0124 0125 QJSValue ScriptEngine::V1::getApiVersion(const QJSValue ¶m) 0126 { 0127 if (param.toInt() != 1) { 0128 return m_engine->newError(i18n("maximum api version supported is 1")); 0129 } 0130 return m_engine->newQObject(this); 0131 } 0132 0133 int ScriptEngine::V1::gridUnit() const 0134 { 0135 int gridUnit = QFontMetrics(QGuiApplication::font()).boundingRect(QStringLiteral("M")).height(); 0136 if (gridUnit % 2 != 0) { 0137 gridUnit++; 0138 } 0139 0140 return gridUnit; 0141 } 0142 0143 QJSValue ScriptEngine::V1::desktopById(const QJSValue ¶m) const 0144 { 0145 // this needs to work also for string of numberls, like "20" 0146 if (param.isUndefined()) { 0147 return m_engine->newError(i18n("desktopById required an id")); 0148 } 0149 0150 const quint32 id = param.toInt(); 0151 0152 foreach (Plasma::Containment *c, m_engine->m_corona->containments()) { 0153 if (c->id() == id && !isPanel(c)) { 0154 return m_engine->wrap(c); 0155 } 0156 } 0157 0158 return QJSValue(); 0159 } 0160 0161 QJSValue ScriptEngine::V1::desktopsForActivity(const QJSValue &actId) const 0162 { 0163 if (!actId.isString()) { 0164 return m_engine->newError(i18n("desktopsForActivity requires an id")); 0165 } 0166 0167 QJSValue containments = m_engine->newArray(); 0168 int count = 0; 0169 0170 const QString id = actId.toString(); 0171 0172 const auto result = m_engine->desktopsForActivity(id); 0173 0174 for (Containment *c : result) { 0175 containments.setProperty(count, m_engine->newQObject(c)); 0176 ++count; 0177 } 0178 0179 containments.setProperty(QStringLiteral("length"), count); 0180 return containments; 0181 } 0182 0183 QJSValue ScriptEngine::V1::desktopForScreen(const QJSValue ¶m) const 0184 { 0185 // this needs to work also for string of numberls, like "20" 0186 if (param.isUndefined()) { 0187 return m_engine->newError(i18n("activityForScreen requires a screen id")); 0188 } 0189 0190 const uint screen = param.toInt(); 0191 // "null": don't create a containment if it doesn't exist, 0192 // return nullptr instead. 0193 const auto containment = m_engine->m_corona->containmentForScreen(screen, currentActivity(), QStringLiteral("null")); 0194 return m_engine->wrap(containment); 0195 } 0196 0197 // TODO Plasma6: remove this 0198 QJSValue ScriptEngine::V1::screenForConnector(const QJSValue ¶m) const 0199 { 0200 // this needs to work also for string of numerals, like "20" 0201 if (param.isUndefined()) { 0202 return m_engine->newError(i18n("screenForConnector requires a connector name")); 0203 } 0204 0205 const QString connector = param.toString(); 0206 ShellCorona *sc = qobject_cast<ShellCorona *>(m_engine->m_corona); 0207 if (sc) { 0208 return m_engine->toScriptValue<int>(sc->screenPool()->idForName(connector)); 0209 } 0210 return m_engine->toScriptValue<int>(-1); 0211 } 0212 0213 QJSValue ScriptEngine::V1::createActivity(const QJSValue &nameParam, const QString &pluginParam) 0214 { 0215 if (!nameParam.isString()) { 0216 return m_engine->newError(i18n("createActivity required the activity name")); 0217 } 0218 0219 QString plugin = pluginParam; 0220 const QString name = nameParam.toString(); 0221 0222 KActivities::Controller controller; 0223 0224 // This is not the nicest way to do this, but createActivity 0225 // is a synchronous API :/ 0226 QFuture<QString> futureId = controller.addActivity(name); 0227 awaitFuture(futureId); 0228 0229 QString id = futureId.result(); 0230 0231 qDebug() << "Setting default Containment plugin:" << plugin; 0232 0233 ShellCorona *sc = qobject_cast<ShellCorona *>(m_engine->m_corona); 0234 StandaloneAppCorona *ac = qobject_cast<StandaloneAppCorona *>(m_engine->m_corona); 0235 if (sc) { 0236 if (plugin.isEmpty() || plugin == QLatin1String("undefined")) { 0237 plugin = sc->defaultContainmentPlugin(); 0238 } 0239 sc->insertActivity(id, plugin); 0240 } else if (ac) { 0241 if (plugin.isEmpty() || plugin == QLatin1String("undefined")) { 0242 KConfigGroup shellCfg = KConfigGroup(KSharedConfig::openConfig(m_engine->m_corona->kPackage().filePath("defaults")), "Desktop"); 0243 plugin = shellCfg.readEntry("Containment", "org.kde.desktopcontainment"); 0244 } 0245 ac->insertActivity(id, plugin); 0246 } 0247 0248 return m_engine->toScriptValue<QString>(id); 0249 } 0250 0251 QJSValue ScriptEngine::V1::setCurrentActivity(const QJSValue ¶m) 0252 { 0253 if (!param.isString()) { 0254 return m_engine->newError(i18n("setCurrentActivity required the activity id")); 0255 } 0256 0257 const QString id = param.toString(); 0258 0259 KActivities::Controller controller; 0260 0261 QFuture<bool> task = controller.setCurrentActivity(id); 0262 awaitFuture(task); 0263 0264 return task.result(); 0265 } 0266 0267 QJSValue ScriptEngine::V1::setActivityName(const QJSValue &idParam, const QJSValue &nameParam) 0268 { 0269 if (!idParam.isString() || !nameParam.isString()) { 0270 return m_engine->newError(i18n("setActivityName required the activity id and name")); 0271 } 0272 0273 const QString id = idParam.toString(); 0274 const QString name = nameParam.toString(); 0275 0276 KActivities::Controller controller; 0277 0278 QFuture<void> task = controller.setActivityName(id, name); 0279 awaitFuture(task); 0280 return QJSValue(); 0281 } 0282 0283 QJSValue ScriptEngine::V1::activityName(const QJSValue &idParam) const 0284 { 0285 if (!idParam.isString()) { 0286 return m_engine->newError(i18n("activityName required the activity id")); 0287 } 0288 0289 const QString id = idParam.toString(); 0290 0291 KActivities::Info info(id); 0292 0293 return QJSValue(info.name()); 0294 } 0295 0296 QString ScriptEngine::V1::currentActivity() const 0297 { 0298 KActivities::Consumer consumer; 0299 return consumer.currentActivity(); 0300 } 0301 0302 QJSValue ScriptEngine::V1::activities() const 0303 { 0304 QJSValue acts = m_engine->newArray(); 0305 int count = 0; 0306 0307 const auto result = m_engine->availableActivities(); 0308 0309 for (const auto &a : result) { 0310 acts.setProperty(count, a); 0311 ++count; 0312 } 0313 acts.setProperty(QStringLiteral("length"), count); 0314 0315 return acts; 0316 } 0317 0318 // Utility function to process configs and config groups 0319 template<typename Object> 0320 void loadSerializedConfigs(Object *object, const QJSValue &configs) 0321 { 0322 SCRIPT_OBJECT_FOREACH(escapedGroup, config, configs) 0323 { 0324 // If the config group is set, pass it on to the containment 0325 QStringList groups = escapedGroup.split('/', Qt::SkipEmptyParts); 0326 for (QString &group : groups) { 0327 group = QUrl::fromPercentEncoding(group.toUtf8()); 0328 } 0329 qDebug() << "Config group" << groups; 0330 object->setCurrentConfigGroup(groups); 0331 0332 // Read other properties and set the configuration 0333 SCRIPT_OBJECT_FOREACH(key, value, config) 0334 { 0335 object->writeConfig(key, value); 0336 }; 0337 }; 0338 } 0339 0340 QJSValue ScriptEngine::V1::loadSerializedLayout(const QJSValue &data) 0341 { 0342 if (!data.isObject()) { 0343 return m_engine->newError(i18n("loadSerializedLayout requires the JSON object to deserialize from")); 0344 } 0345 0346 if (data.property("serializationFormatVersion").toInt() != 1) { 0347 return m_engine->newError(i18n("loadSerializedLayout: invalid version of the serialized object")); 0348 } 0349 0350 const auto desktops = m_engine->desktopsForActivity(KActivities::Consumer().currentActivity()); 0351 Q_ASSERT_X(desktops.size() != 0, "V1::loadSerializedLayout", "We need desktops"); 0352 0353 // qDebug() << "DESKTOP DESERIALIZATION: Loading desktops..."; 0354 0355 int count = 0; 0356 SCRIPT_ARRAY_FOREACH(desktopData, data.property("desktops")) 0357 { 0358 // If the template has more desktops than we do, ignore them 0359 if (count >= desktops.size()) 0360 return; 0361 0362 auto desktop = desktops[count]; 0363 // qDebug() << "DESKTOP DESERIALIZATION: var cont = desktopsArray[...]; " << count << " -> " << desktop; 0364 0365 // Setting the wallpaper plugin because it is special 0366 desktop->setWallpaperPlugin(desktopData.property("wallpaperPlugin").toString()); 0367 // qDebug() << "DESKTOP DESERIALIZATION: cont->setWallpaperPlugin(...) " << desktop->wallpaperPlugin(); 0368 0369 // Now, lets go through the configs 0370 loadSerializedConfigs(desktop, desktopData.property("config")); 0371 0372 // After the config, we want to load the applets 0373 SCRIPT_ARRAY_FOREACH(appletData, desktopData.property("applets")) 0374 { 0375 // qDebug() << "DESKTOP DESERIALIZATION: Applet: " << appletData.toString(); 0376 0377 auto appletObject = desktop->addWidget(appletData.property("plugin"), 0378 appletData.property("geometry.x").toInt() * gridUnit(), 0379 appletData.property("geometry.y").toInt() * gridUnit(), 0380 appletData.property("geometry.width").toInt() * gridUnit(), 0381 appletData.property("geometry.height").toInt() * gridUnit()); 0382 0383 if (auto applet = qobject_cast<Widget *>(appletObject.toQObject())) { 0384 // Now, lets go through the configs for the applet 0385 loadSerializedConfigs(applet, appletData.property("config")); 0386 } 0387 }; 0388 0389 count++; 0390 }; 0391 0392 // qDebug() << "PANEL DESERIALIZATION: Loading panels..."; 0393 0394 SCRIPT_ARRAY_FOREACH(panelData, data.property("panels")) 0395 { 0396 const auto panel = qobject_cast<Panel *>(m_engine->createContainmentWrapper(QStringLiteral("Panel"), QStringLiteral("org.kde.panel"))); 0397 0398 Q_ASSERT(panel); 0399 0400 // Basic panel setup 0401 panel->setLocation(panelData.property("location").toString()); 0402 panel->setHeight(panelData.property("height").toNumber() * gridUnit()); 0403 panel->setMaximumLength(panelData.property("maximumLength").toNumber() * gridUnit()); 0404 panel->setMinimumLength(panelData.property("minimumLength").toNumber() * gridUnit()); 0405 panel->setOffset(panelData.property("offset").toNumber() * gridUnit()); 0406 panel->setAlignment(panelData.property("alignment").toString()); 0407 panel->setHiding(panelData.property("hiding").toString()); 0408 0409 // Loading the config for the panel 0410 loadSerializedConfigs(panel, panelData.property("config")); 0411 0412 // Now dealing with the applets 0413 SCRIPT_ARRAY_FOREACH(appletData, panelData.property("applets")) 0414 { 0415 // qDebug() << "PANEL DESERIALIZATION: Applet: " << appletData.toString(); 0416 0417 auto appletObject = panel->addWidget(appletData.property("plugin")); 0418 // qDebug() << "PANEL DESERIALIZATION: addWidget" 0419 // << appletData.property("plugin").toString() 0420 // ; 0421 0422 if (auto applet = qobject_cast<Widget *>(appletObject.toQObject())) { 0423 // Now, lets go through the configs for the applet 0424 loadSerializedConfigs(applet, appletData.property("config")); 0425 } 0426 }; 0427 }; 0428 0429 return QJSValue(); 0430 } 0431 0432 QJSValue ScriptEngine::V1::newPanel(const QString &plugin) 0433 { 0434 return createContainment(QStringLiteral("Panel"), QStringLiteral("org.kde.panel"), plugin); 0435 } 0436 0437 QJSValue ScriptEngine::V1::panelById(const QJSValue &idParam) const 0438 { 0439 // this needs to work also for string of numberls, like "20" 0440 if (idParam.isUndefined()) { 0441 return m_engine->newError(i18n("panelById requires an id")); 0442 } 0443 0444 const quint32 id = idParam.toInt(); 0445 0446 foreach (Plasma::Containment *c, m_engine->m_corona->containments()) { 0447 if (c->id() == id && isPanel(c)) { 0448 return m_engine->wrap(c); 0449 } 0450 } 0451 0452 return QJSValue(); 0453 } 0454 0455 QJSValue ScriptEngine::V1::desktops() const 0456 { 0457 QJSValue containments = m_engine->newArray(); 0458 int count = 0; 0459 0460 const auto result = m_engine->m_corona->containments(); 0461 0462 for (const auto c : result) { 0463 // make really sure we get actual desktops, so check for a non empty 0464 // activity id 0465 if (!isPanel(c) && !c->activity().isEmpty()) { 0466 containments.setProperty(count, m_engine->wrap(c)); 0467 ++count; 0468 } 0469 } 0470 0471 containments.setProperty(QStringLiteral("length"), count); 0472 return containments; 0473 } 0474 0475 QJSValue ScriptEngine::V1::panels() const 0476 { 0477 QJSValue panels = m_engine->newArray(); 0478 int count = 0; 0479 0480 const auto result = m_engine->m_corona->containments(); 0481 0482 for (const auto c : result) { 0483 if (isPanel(c)) { 0484 panels.setProperty(count, m_engine->wrap(c)); 0485 ++count; 0486 } 0487 } 0488 panels.setProperty(QStringLiteral("length"), count); 0489 0490 return panels; 0491 } 0492 0493 bool ScriptEngine::V1::fileExists(const QString &path) const 0494 { 0495 if (path.isEmpty()) { 0496 return false; 0497 } 0498 0499 QFile f(KShell::tildeExpand(path)); 0500 return f.exists(); 0501 } 0502 0503 bool ScriptEngine::V1::loadTemplate(const QString &layout) 0504 { 0505 if (layout.isEmpty() || layout.contains(QLatin1Char('\''))) { 0506 // qDebug() << "layout is empty"; 0507 return false; 0508 } 0509 0510 auto filter = [&layout](const KPluginMetaData &md) -> bool { 0511 return md.pluginId() == layout && md.value(QStringLiteral("X-Plasma-ContainmentCategories"), QStringList()).contains(QLatin1String("panel")); 0512 }; 0513 QList<KPluginMetaData> offers = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/LayoutTemplate"), QString(), filter); 0514 0515 if (offers.isEmpty()) { 0516 // qDebug() << "offers fail" << constraint; 0517 return false; 0518 } 0519 0520 KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LayoutTemplate")); 0521 KPluginMetaData pluginData(offers.first()); 0522 0523 QString path; 0524 { 0525 ShellCorona *sc = qobject_cast<ShellCorona *>(m_engine->m_corona); 0526 if (sc) { 0527 const QString overridePackagePath = sc->lookAndFeelPackage().path() + QLatin1String("contents/layouts/") + pluginData.pluginId(); 0528 0529 path = overridePackagePath + QStringLiteral("/metadata.json"); 0530 if (QFile::exists(path)) { 0531 package.setPath(overridePackagePath); 0532 } 0533 0534 path = overridePackagePath + QStringLiteral("/metadata.desktop"); 0535 if (QFile::exists(path)) { 0536 package.setPath(overridePackagePath); 0537 } 0538 } 0539 } 0540 0541 if (!package.isValid()) { 0542 path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, package.defaultPackageRoot() + pluginData.pluginId() + "/metadata.json"); 0543 if (path.isEmpty()) { 0544 path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, package.defaultPackageRoot() + pluginData.pluginId() + "/metadata.desktop"); 0545 } 0546 if (path.isEmpty()) { 0547 // qDebug() << "script path is empty"; 0548 return false; 0549 } 0550 0551 package.setPath(pluginData.pluginId()); 0552 } 0553 0554 const QString scriptFile = package.filePath("mainscript"); 0555 if (scriptFile.isEmpty()) { 0556 // qDebug() << "scriptfile is empty"; 0557 return false; 0558 } 0559 0560 QFile file(scriptFile); 0561 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { 0562 qCWarning(PLASMASHELL) << "Unable to load script file:" << path; 0563 return false; 0564 } 0565 0566 QString script = file.readAll(); 0567 if (script.isEmpty()) { 0568 // qDebug() << "script is empty"; 0569 return false; 0570 } 0571 0572 ScriptEngine *engine = new ScriptEngine(m_engine->corona(), this); 0573 engine->globalObject().setProperty(QStringLiteral("templateName"), pluginData.name()); 0574 engine->globalObject().setProperty(QStringLiteral("templateComment"), pluginData.description()); 0575 0576 engine->evaluateScript(script, path); 0577 0578 engine->deleteLater(); 0579 return true; 0580 } 0581 0582 bool ScriptEngine::V1::applicationExists(const QString &application) const 0583 { 0584 if (application.isEmpty()) { 0585 return false; 0586 } 0587 0588 // first, check for it in $PATH 0589 if (!QStandardPaths::findExecutable(application).isEmpty()) { 0590 return true; 0591 } 0592 0593 if (KService::serviceByStorageId(application)) { 0594 return true; 0595 } 0596 0597 if (application.contains(QLatin1Char('\''))) { 0598 // apostrophes just screw up the trader lookups below, so check for it 0599 return false; 0600 } 0601 0602 // next, consult ksycoca for an app by that name 0603 const auto servciesByName = KApplicationTrader::query([&application](const KService::Ptr &service) { 0604 return service->name().compare(application, Qt::CaseInsensitive) == 0 || service->genericName().compare(application, Qt::CaseInsensitive) == 0; 0605 }); 0606 return !servciesByName.isEmpty(); 0607 } 0608 0609 QJSValue ScriptEngine::V1::defaultApplication(const QString &application, bool storageId) const 0610 { 0611 if (application.isEmpty()) { 0612 return false; 0613 } 0614 0615 // FIXME: there are some pretty horrible hacks below, in the sense that they 0616 // assume a very 0617 // specific implementation system. there is much room for improvement here. 0618 // see 0619 // kdebase-runtime/kcontrol/componentchooser/ for all the gory details ;) 0620 if (matches(application, QLatin1String("mailer"))) { 0621 // KEMailSettings settings; 0622 0623 // in KToolInvocation, the default is kmail; but let's be friendlier :) 0624 // QString command = settings.getSetting(KEMailSettings::ClientProgram); 0625 QString command; 0626 if (command.isEmpty()) { 0627 if (KService::Ptr kontact = KService::serviceByStorageId(QStringLiteral("kontact"))) { 0628 return storageId ? kontact->storageId() : onlyExec(kontact->exec()); 0629 } else if (KService::Ptr kmail = KService::serviceByStorageId(QStringLiteral("kmail"))) { 0630 return storageId ? kmail->storageId() : onlyExec(kmail->exec()); 0631 } 0632 } 0633 0634 if (!command.isEmpty()) { 0635 if (false) { 0636 KConfigGroup confGroup(KSharedConfig::openConfig(), "General"); 0637 const QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QStringLiteral("konsole")); 0638 command = preferredTerminal + QLatin1String(" -e ") + command; 0639 } 0640 0641 return command; 0642 } 0643 0644 } else if (matches(application, QLatin1String("browser"))) { 0645 KConfigGroup config(KSharedConfig::openConfig(), "General"); 0646 QString browserApp = config.readPathEntry("BrowserApplication", QString()); 0647 if (browserApp.isEmpty()) { 0648 const KService::Ptr htmlApp = KApplicationTrader::preferredService(QStringLiteral("text/html")); 0649 if (htmlApp) { 0650 browserApp = storageId ? htmlApp->storageId() : htmlApp->exec(); 0651 } 0652 } else if (browserApp.startsWith('!')) { 0653 browserApp.remove(0, 1); 0654 } 0655 0656 return onlyExec(browserApp); 0657 0658 } else if (matches(application, QLatin1String("terminal"))) { 0659 KConfigGroup confGroup(KSharedConfig::openConfig(), "General"); 0660 return onlyExec(confGroup.readPathEntry("TerminalApplication", QStringLiteral("konsole"))); 0661 0662 } else if (matches(application, QLatin1String("filemanager"))) { 0663 KService::Ptr service = KApplicationTrader::preferredService(QStringLiteral("inode/directory")); 0664 if (service) { 0665 return storageId ? service->storageId() : onlyExec(service->exec()); 0666 } 0667 0668 } else if (matches(application, QLatin1String("windowmanager"))) { 0669 KConfig cfg(QStringLiteral("ksmserverrc"), KConfig::NoGlobals); 0670 KConfigGroup confGroup(&cfg, "General"); 0671 return onlyExec(confGroup.readEntry("windowManager", QStringLiteral("kwin"))); 0672 0673 } else if (KService::Ptr service = KApplicationTrader::preferredService(application)) { 0674 return storageId ? service->storageId() : onlyExec(service->exec()); 0675 } 0676 0677 return false; 0678 } 0679 0680 QJSValue ScriptEngine::V1::applicationPath(const QString &application) const 0681 { 0682 if (application.isEmpty()) { 0683 return false; 0684 } 0685 0686 // first, check for it in $PATH 0687 const QString path = QStandardPaths::findExecutable(application); 0688 if (!path.isEmpty()) { 0689 return path; 0690 } 0691 0692 if (KService::Ptr service = KService::serviceByStorageId(application)) { 0693 return QStandardPaths::locate(QStandardPaths::ApplicationsLocation, service->entryPath()); 0694 } 0695 0696 if (application.contains(QLatin1Char('\''))) { 0697 // apostrophes just screw up the trader lookups below, so check for it 0698 return QString(); 0699 } 0700 0701 // next, consult ksycoca for an app by that name 0702 const auto offers = KApplicationTrader::query([&application](const KService::Ptr &service) { 0703 return service->name().compare(application, Qt::CaseInsensitive) == 0 || service->genericName().compare(application, Qt::CaseInsensitive) == 0; 0704 }); 0705 0706 if (!offers.isEmpty()) { 0707 KService::Ptr offer = offers.first(); 0708 return QStandardPaths::locate(QStandardPaths::ApplicationsLocation, offer->entryPath()); 0709 } 0710 0711 return QString(); 0712 } 0713 0714 QJSValue ScriptEngine::V1::userDataPath(const QString &type, const QString &path) const 0715 { 0716 if (type.isEmpty()) { 0717 return QDir::homePath(); 0718 } 0719 0720 QStandardPaths::StandardLocation location = QStandardPaths::GenericDataLocation; 0721 if (matches(type, QLatin1String("desktop"))) { 0722 location = QStandardPaths::DesktopLocation; 0723 0724 } else if (matches(type, QLatin1String("documents"))) { 0725 location = QStandardPaths::DocumentsLocation; 0726 0727 } else if (matches(type, QLatin1String("music"))) { 0728 location = QStandardPaths::MusicLocation; 0729 0730 } else if (matches(type, QLatin1String("video"))) { 0731 location = QStandardPaths::MoviesLocation; 0732 0733 } else if (matches(type, QLatin1String("downloads"))) { 0734 location = QStandardPaths::DownloadLocation; 0735 0736 } else if (matches(type, QLatin1String("pictures"))) { 0737 location = QStandardPaths::PicturesLocation; 0738 0739 } else if (matches(type, QLatin1String("config"))) { 0740 location = QStandardPaths::GenericConfigLocation; 0741 } 0742 0743 if (!path.isEmpty()) { 0744 QString loc = QStandardPaths::writableLocation(location); 0745 loc.append(QDir::separator()); 0746 loc.append(path); 0747 return loc; 0748 } 0749 0750 const QStringList &locations = QStandardPaths::standardLocations(location); 0751 return locations.count() ? locations.first() : QString(); 0752 } 0753 0754 QJSValue ScriptEngine::V1::knownWallpaperPlugins(const QString &formFactor) const 0755 { 0756 QString constraint; 0757 if (!formFactor.isEmpty()) { 0758 constraint.append("[X-Plasma-FormFactors] ~~ '").append(formFactor).append("'"); 0759 } 0760 0761 const QList<KPluginMetaData> wallpapers = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/Wallpaper"), QString()); 0762 QJSValue rv = m_engine->newArray(wallpapers.size()); 0763 for (auto wp : wallpapers) { 0764 rv.setProperty(wp.name(), m_engine->newArray(0)); 0765 } 0766 0767 return rv; 0768 } 0769 0770 QJSValue ScriptEngine::V1::configFile(const QJSValue &config, const QString &group) 0771 { 0772 ConfigGroup *file = nullptr; 0773 0774 if (!config.isUndefined()) { 0775 if (config.isString()) { 0776 file = new ConfigGroup; 0777 0778 const Plasma::Corona *corona = m_engine->corona(); 0779 const QString &fileName = config.toString(); 0780 0781 if (fileName == corona->config()->name()) { 0782 file->setConfig(corona->config()); 0783 } else { 0784 file->setFile(fileName); 0785 } 0786 0787 if (!group.isEmpty()) { 0788 file->setGroup(group); 0789 } 0790 0791 } else if (ConfigGroup *parent = qobject_cast<ConfigGroup *>(config.toQObject())) { 0792 file = new ConfigGroup(parent); 0793 0794 if (!group.isEmpty()) { 0795 file->setGroup(group); 0796 } 0797 } 0798 0799 } else { 0800 file = new ConfigGroup; 0801 } 0802 0803 QJSValue v = m_engine->newQObject(file); 0804 return v; 0805 } 0806 0807 void ScriptEngine::V1::setImmutability(const QString &immutability) 0808 { 0809 if (immutability.isEmpty()) { 0810 return; 0811 } 0812 0813 if (immutability == QLatin1String("systemImmutable")) { 0814 m_engine->corona()->setImmutability(Plasma::Types::SystemImmutable); 0815 } else if (immutability == QLatin1String("userImmutable")) { 0816 m_engine->corona()->setImmutability(Plasma::Types::UserImmutable); 0817 } else { 0818 m_engine->corona()->setImmutability(Plasma::Types::Mutable); 0819 } 0820 0821 return; 0822 } 0823 0824 QString ScriptEngine::V1::immutability() const 0825 { 0826 switch (m_engine->corona()->immutability()) { 0827 case Plasma::Types::SystemImmutable: 0828 return QLatin1String("systemImmutable"); 0829 case Plasma::Types::UserImmutable: 0830 return QLatin1String("userImmutable"); 0831 default: 0832 return QLatin1String("mutable"); 0833 } 0834 } 0835 0836 QJSValue ScriptEngine::V1::createContainment(const QString &type, const QString &defaultPlugin, const QString &plugin) 0837 { 0838 const QString actualPlugin = plugin.isEmpty() ? defaultPlugin : plugin; 0839 0840 auto result = m_engine->createContainmentWrapper(type, actualPlugin); 0841 0842 if (!result) { 0843 return m_engine->newError(i18n("Could not find a plugin for %1 named %2.", type, actualPlugin)); 0844 } 0845 0846 return m_engine->newQObject(result); 0847 } 0848 0849 } // namespace WorkspaceScripting