diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d9370d3d..c1ba49d47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,185 +1,185 @@ cmake_minimum_required(VERSION 3.0) project(plasma-workspace) set(PROJECT_VERSION "5.12.80") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.9.0") set(KF5_MIN_VERSION "5.45.0") set(INSTALL_SDDM_THEME TRUE) -find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Widgets Quick QuickWidgets Concurrent Test Script Network) +find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Widgets Quick QuickWidgets Concurrent Test Network) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMMarkNonGuiExecutable) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckIncludeFiles) include(FeatureSummary) include(ECMOptionalAddSubdirectory) include(ECMQtDeclareLoggingCategory) include(KDEPackageAppTemplates) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma DocTools Runner JsEmbed NotifyConfig Su NewStuff Wallet IdleTime Declarative TextWidgets KDELibs4Support Crash GlobalAccel DBusAddons Wayland CoreAddons) find_package(KF5NetworkManagerQt ${KF5_MIN_VERSION}) set_package_properties(KF5NetworkManagerQt PROPERTIES DESCRIPTION "Qt wrapper for NetworkManager API" TYPE OPTIONAL PURPOSE "Needed by geolocation data engine." ) # WARNING PlasmaQuick provides unversioned CMake config find_package(KF5 REQUIRED COMPONENTS PlasmaQuick) find_package(KF5 REQUIRED COMPONENTS SysGuard) find_package(KF5 REQUIRED COMPONENTS Package) find_package(KF5Baloo) set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "File Searching" TYPE RECOMMENDED PURPOSE "Needed for the File Search runner." ) find_package(KF5TextEditor) find_package(KWinDBusInterface CONFIG REQUIRED) find_package(KScreenLocker REQUIRED) find_package(ScreenSaverDBusInterface CONFIG REQUIRED) find_package(KF5Holidays) set_package_properties(KF5Holidays PROPERTIES DESCRIPTION "Holidays provider for Plasma calendar" TYPE OPTIONAL PURPOSE "Needed to for holidays plugin for Plasma Calendar." ) find_package(Phonon4Qt5 4.6.60 REQUIRED NO_MODULE) set_package_properties(Phonon4Qt5 PROPERTIES DESCRIPTION "Qt-based audio library" TYPE REQUIRED) find_package(KF5Activities ${KF5_MIN_VERSION}) set_package_properties(KF5Activities PROPERTIES DESCRIPTION "management of Plasma activities" TYPE OPTIONAL PURPOSE "Needed by activity related plasmoids." ) find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Support for gzip compressed files and data streams" URL "http://www.zlib.net" TYPE REQUIRED ) find_package(X11) set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "http://www.x.org" TYPE OPTIONAL PURPOSE "Required for building the X11 based workspace") if(X11_FOUND) find_package(XCB MODULE REQUIRED COMPONENTS XCB RANDR) set_package_properties(XCB PROPERTIES TYPE REQUIRED) if(NOT X11_SM_FOUND) message(FATAL_ERROR "\nThe X11 Session Management (SM) development package could not be found.\nPlease install libSM.\n") endif(NOT X11_SM_FOUND) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS X11Extras) endif() if(X11_FOUND AND XCB_XCB_FOUND) set(HAVE_X11 1) endif() find_package(AppStreamQt 0.10.4) set_package_properties(AppStreamQt PROPERTIES DESCRIPTION "Access metadata for listing available software" URL "https://www.freedesktop.org/wiki/Distributions/AppStream/" TYPE OPTIONAL ) include(ConfigureChecks.cmake) include_directories("${CMAKE_CURRENT_BINARY_DIR}") configure_file(config-workspace.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-workspace.h) configure_file(config-unix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-unix.h ) configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h) configure_file(plasma.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/plasma.desktop) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/plasma.desktop DESTINATION ${KDE_INSTALL_DATADIR}/xsessions ) configure_file(plasmawayland.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/plasmawayland.desktop) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/plasmawayland.desktop DESTINATION ${KDE_INSTALL_DATADIR}/wayland-sessions ) plasma_install_package(lookandfeel org.kde.breeze.desktop look-and-feel lookandfeel) if (INSTALL_SDDM_THEME) configure_file(sddm-theme/theme.conf.cmake ${CMAKE_CURRENT_BINARY_DIR}/sddm-theme/theme.conf) # Install the login theme into the SDDM directory # Longer term we need to look at making SDDM load from look and feel somehow.. and allow copying at runtime #NOTE this trailing slash is important to rename the directory install(DIRECTORY sddm-theme/ DESTINATION ${KDE_INSTALL_FULL_DATADIR}/sddm/themes/breeze PATTERN "README.txt" EXCLUDE PATTERN "components" EXCLUDE PATTERN "dummydata" EXCLUDE PATTERN "theme.conf.cmake" EXCLUDE) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/sddm-theme/theme.conf DESTINATION ${KDE_INSTALL_FULL_DATADIR}/sddm/themes/breeze) install(DIRECTORY lookandfeel/contents/components DESTINATION ${KDE_INSTALL_FULL_DATADIR}/sddm/themes/breeze PATTERN "README.txt" EXCLUDE) endif() add_definitions(-DQT_NO_URL_CAST_FROM_STRING) add_subdirectory(doc) add_subdirectory(libkworkspace) add_subdirectory(libdbusmenuqt) add_subdirectory(appmenu) add_subdirectory(libtaskmanager) add_subdirectory(libcolorcorrect) add_subdirectory(components) add_subdirectory(plasma-windowed) add_subdirectory(shell) add_subdirectory(freespacenotifier) add_subdirectory(klipper) add_subdirectory(krunner) add_subdirectory(ksmserver) add_subdirectory(ksplash) add_subdirectory(systemmonitor) add_subdirectory(statusnotifierwatcher) add_subdirectory(startkde) add_subdirectory(themes) add_subdirectory(containmentactions) add_subdirectory(runners) add_subdirectory(applets) add_subdirectory(dataengines) add_subdirectory(wallpapers) add_subdirectory(kioslave) add_subdirectory(ktimezoned) add_subdirectory(kuiserver) add_subdirectory(menu) add_subdirectory(phonon) add_subdirectory(solidautoeject) ecm_optional_add_subdirectory(xembed-sni-proxy) ecm_optional_add_subdirectory(gmenu-dbusmenu-proxy) add_subdirectory(soliduiserver) if(KF5Holidays_FOUND) add_subdirectory(plasmacalendarintegration) endif() add_subdirectory(templates) install( FILES plasma-workspace.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt index f11929ac7..bc78e9f8f 100644 --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -1,99 +1,98 @@ configure_file(config-ktexteditor.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ktexteditor.h ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-plasma.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-plasma.h) add_definitions(-DPLASMA_DEPRECATED=) set(scripting_SRC scripting/appinterface.cpp scripting/applet.cpp scripting/containment.cpp scripting/configgroup.cpp scripting/panel.cpp scripting/scriptengine.cpp scripting/scriptengine_v1.cpp scripting/widget.cpp ) set(plasmashell_dbusXML dbus/org.kde.PlasmaShell.xml) qt5_add_dbus_adaptor(scripting_SRC ${plasmashell_dbusXML} shellcorona.h ShellCorona plasmashelladaptor) ecm_qt_declare_logging_category(plasmashell HEADER debug.h IDENTIFIER PLASMASHELL CATEGORY_NAME kde.plasmashell DEFAULT_SEVERITY Info) set (plasma_shell_SRCS alternativeshelper.cpp main.cpp containmentconfigview.cpp currentcontainmentactionsmodel.cpp desktopview.cpp panelview.cpp panelconfigview.cpp panelshadows.cpp shellcorona.cpp shellmanager.cpp standaloneappcorona osd.cpp coronatesthelper.cpp debug.cpp screenpool.cpp softwarerendernotifier.cpp ${scripting_SRC} ) set(krunner_xml ${plasma-workspace_SOURCE_DIR}/krunner/dbus/org.kde.krunner.App.xml) qt5_add_dbus_interface(plasma_shell_SRCS ${krunner_xml} krunner_interface) add_executable(plasmashell ${plasma_shell_SRCS} ) target_link_libraries(plasmashell Qt5::Quick Qt5::DBus KF5::KIOCore KF5::WindowSystem KF5::Crash KF5::Plasma KF5::PlasmaQuick - Qt5::Script KF5::Solid KF5::Declarative KF5::I18n KF5::IconThemes KF5::Activities KF5::GlobalAccel KF5::CoreAddons KF5::DBusAddons KF5::Declarative KF5::QuickAddons KF5::XmlGui KF5::Package KF5::WaylandClient KF5::WindowSystem KF5::Notifications PW::KWorkspace ) target_include_directories(plasmashell PRIVATE "${CMAKE_BINARY_DIR}") target_compile_definitions(plasmashell PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}") if(HAVE_X11) target_link_libraries(plasmashell ${X11_LIBRARIES} ${XCB_LIBRARIES} ) target_link_libraries(plasmashell Qt5::X11Extras) endif() install(TARGETS plasmashell ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES org.kde.plasmashell.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES org.kde.plasmashell.desktop DESTINATION ${KDE_INSTALL_AUTOSTARTDIR}) install( FILES dbus/org.kde.PlasmaShell.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} ) install(FILES scripting/plasma-layouttemplate.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR}) add_subdirectory(packageplugins) if(BUILD_TESTING) add_subdirectory(autotests) endif() diff --git a/shell/scripting/containment.cpp b/shell/scripting/containment.cpp index dd8c1649c..5916886aa 100644 --- a/shell/scripting/containment.cpp +++ b/shell/scripting/containment.cpp @@ -1,297 +1,297 @@ /* * Copyright 2009 Aaron Seigo * * 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 "containment.h" #include #include #include #include #include #include #include #include #include "shellcorona.h" #include "scriptengine.h" #include "widget.h" namespace WorkspaceScripting { class Containment::Private { public: QPointer engine; QPointer containment; ShellCorona *corona; QString oldWallpaperPlugin; QString wallpaperPlugin; QString oldWallpaperMode; QString wallpaperMode; QString type; QString plugin; }; Containment::Containment(Plasma::Containment *containment, ScriptEngine *engine) : Applet(engine), d(new Containment::Private) { d->engine = engine; d->containment = containment; d->corona = qobject_cast(containment->corona()); setCurrentConfigGroup(QStringList()); setCurrentGlobalConfigGroup(QStringList()); if (containment) { d->oldWallpaperPlugin = d->wallpaperPlugin = containment->wallpaper(); } } Containment::~Containment() { if (d->containment) { Plasma::Containment *containment = d->containment.data(); if (d->oldWallpaperPlugin != d->wallpaperPlugin || d->oldWallpaperMode != d->wallpaperMode) { containment->setWallpaper(d->wallpaperPlugin); } } reloadConfigIfNeeded(); delete d; } ShellCorona *Containment::corona() const { return d->corona; } int Containment::screen() const { if (!d->containment) { return -1; } return d->containment.data()->screen(); } QString Containment::wallpaperPlugin() const { return d->wallpaperPlugin; } void Containment::setWallpaperPlugin(const QString &wallpaperPlugin) { d->wallpaperPlugin = wallpaperPlugin; } QString Containment::wallpaperMode() const { return d->wallpaperMode; } void Containment::setWallpaperMode(const QString &wallpaperMode) { d->wallpaperMode = wallpaperMode; } QString Containment::formFactor() const { if (!d->containment) { return QStringLiteral("Planar"); } switch (d->containment.data()->formFactor()) { case Plasma::Types::Planar: return QStringLiteral("planar"); break; case Plasma::Types::MediaCenter: return QStringLiteral("mediacenter"); break; case Plasma::Types::Horizontal: return QStringLiteral("horizontal"); break; case Plasma::Types::Vertical: return QStringLiteral("vertical"); break; case Plasma::Types::Application: return QStringLiteral("application"); break; } return QStringLiteral("Planar"); } QList Containment::widgetIds() const { //FIXME: the ints could overflow since Applet::id() returns a uint, // however QScript deals with QList very, very poory QList w; if (d->containment) { foreach (const Plasma::Applet *applet, d->containment.data()->applets()) { w.append(applet->id()); } } return w; } QJSValue Containment::widgetById(const QJSValue ¶mId) const { if (!paramId.isNumber()) { - return d->engine->evaluate(QString("new Error('%1');").arg(i18n("widgetById requires an id"))); + return d->engine->newError(i18n("widgetById requires an id")); } const uint id = paramId.toInt(); if (d->containment) { foreach (Plasma::Applet *w, d->containment.data()->applets()) { if (w->id() == id) { return d->engine->wrap(w); } } } return QJSValue(); } QJSValue Containment::addWidget(const QJSValue &v, qreal x, qreal y, qreal w, qreal h) { if (!v.isString() && !v.isQObject()) { - return d->engine->evaluate(QString("new Error('%1');").arg(i18n("addWidget requires a name of a widget or a widget object"))); + return d->engine->newError(i18n("addWidget requires a name of a widget or a widget object")); } if (!d->containment) { return QJSValue(); } QRectF geometry(x, y, w, h); Plasma::Applet *applet = nullptr; if (v.isString()) { //A position has been supplied: search for the containment's graphics object QQuickItem *containmentItem = nullptr; if (geometry.x() >= 0 && geometry.y() >= 0) { containmentItem = d->containment.data()->property("_plasma_graphicObject").value(); if (containmentItem) { QMetaObject::invokeMethod(containmentItem , "createApplet", Qt::DirectConnection, Q_RETURN_ARG(Plasma::Applet *, applet), Q_ARG(QString, v.toString()), Q_ARG(QVariantList, QVariantList()), Q_ARG(QRectF, geometry)); } if (applet) { return d->engine->wrap(applet); } - return d->engine->evaluate(QString("new Error('%1');").arg(i18n("Could not create the %1 widget!", v.toString()))); + return d->engine->newError(i18n("Could not create the %1 widget!", v.toString())); } //Case in which either: // * a geometry wasn't provided // * containmentItem wasn't found applet = d->containment.data()->createApplet(v.toString()); if (applet) { return d->engine->wrap(applet); } - return d->engine->evaluate(QString("new Error('%1');").arg(i18n("Could not create the %1 widget!", v.toString()))); + return d->engine->newError(i18n("Could not create the %1 widget!", v.toString())); } else if (Widget *widget = qobject_cast(v.toQObject())) { applet = widget->applet(); d->containment.data()->addApplet(applet); return v; } return QJSValue(); } QJSValue Containment::widgets(const QString &widgetType) const { if (!d->containment) { return QJSValue(); } QJSValue widgets = d->engine->newArray(); int count = 0; foreach (Plasma::Applet *widget, d->containment.data()->applets()) { if (widgetType.isEmpty() || widget->pluginMetaData().pluginId() == widgetType) { widgets.setProperty(count, d->engine->wrap(widget)); ++count; } } widgets.setProperty(QStringLiteral("length"), count); return widgets; } uint Containment::id() const { if (!d->containment) { return 0; } return d->containment.data()->id(); } QString Containment::type() const { if (!d->containment) { return QString(); } return d->containment.data()->pluginMetaData().pluginId(); } void Containment::remove() { if (d->containment) { d->containment.data()->destroy(); } } void Containment::showConfigurationInterface() { if (d->containment) { QAction *configAction = d->containment.data()->actions()->action(QStringLiteral("configure")); if (configAction && configAction->isEnabled()) { configAction->trigger(); } } } Plasma::Applet *Containment::applet() const { return d->containment.data(); } Plasma::Containment *Containment::containment() const { return d->containment.data(); } } diff --git a/shell/scripting/containment.h b/shell/scripting/containment.h index 9ab6e4b9b..29000b1aa 100644 --- a/shell/scripting/containment.h +++ b/shell/scripting/containment.h @@ -1,98 +1,97 @@ /* * Copyright 2009 Aaron Seigo * * 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. */ #ifndef CONTAINMENT #define CONTAINMENT -#include #include #include #include "applet.h" namespace Plasma { class Containment; } // namespace Plasma class ShellCorona; namespace WorkspaceScripting { class ScriptEngine; class Containment : public Applet { Q_OBJECT ///FIXME: add NOTIFY Q_PROPERTY(QString version READ version) Q_PROPERTY(QStringList configKeys READ configKeys) Q_PROPERTY(QStringList configGroups READ configGroups) Q_PROPERTY(QStringList globalConfigKeys READ globalConfigKeys) Q_PROPERTY(QStringList globalConfigGroups READ globalConfigGroups) Q_PROPERTY(QStringList currentConfigGroup WRITE setCurrentConfigGroup READ currentConfigGroup) Q_PROPERTY(QString wallpaperPlugin READ wallpaperPlugin WRITE setWallpaperPlugin) Q_PROPERTY(QString wallpaperMode READ wallpaperMode WRITE setWallpaperMode) Q_PROPERTY(bool locked READ locked WRITE setLocked) Q_PROPERTY(QString type READ type) Q_PROPERTY(QString formFactor READ formFactor) Q_PROPERTY(QList widgetIds READ widgetIds) Q_PROPERTY(int screen READ screen) Q_PROPERTY(int id READ id) public: explicit Containment(Plasma::Containment *containment, ScriptEngine *parent); ~Containment() override; uint id() const; QString type() const; QString formFactor() const; QList widgetIds() const; int screen() const; Plasma::Applet *applet() const override; Plasma::Containment *containment() const; QString wallpaperPlugin() const; void setWallpaperPlugin(const QString &wallpaperPlugin); QString wallpaperMode() const; void setWallpaperMode(const QString &wallpaperMode); Q_INVOKABLE QJSValue widgetById(const QJSValue ¶mId = QJSValue()) const; Q_INVOKABLE QJSValue addWidget(const QJSValue &v = QJSValue(), qreal x = -1, qreal y = -1, qreal w = -1, qreal h = -1); Q_INVOKABLE QJSValue widgets(const QString &widgetType = QString()) const; public Q_SLOTS: void remove(); void showConfigurationInterface(); protected: ShellCorona *corona() const; private: class Private; Private * const d; }; } #endif diff --git a/shell/scripting/panel.h b/shell/scripting/panel.h index eb9ffd67a..2b617ec90 100644 --- a/shell/scripting/panel.h +++ b/shell/scripting/panel.h @@ -1,104 +1,103 @@ /* * Copyright 2009 Aaron Seigo * * 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. */ #ifndef PANEL #define PANEL -#include #include #include #include "containment.h" class PanelView; class ShellCorona; namespace WorkspaceScripting { class Panel : public Containment { Q_OBJECT Q_PROPERTY(QStringList configKeys READ configKeys) Q_PROPERTY(QStringList configGroups READ configGroups) Q_PROPERTY(QStringList currentConfigGroup WRITE setCurrentConfigGroup READ currentConfigGroup) Q_PROPERTY(QStringList globalConfigKeys READ globalConfigKeys) Q_PROPERTY(QStringList globalConfigGroups READ globalConfigGroups) Q_PROPERTY(QString version READ version) Q_PROPERTY(QString type READ type) Q_PROPERTY(QString formFactor READ formFactor) Q_PROPERTY(QList widgetIds READ widgetIds) Q_PROPERTY(int screen READ screen) Q_PROPERTY(QString location READ location WRITE setLocation) Q_PROPERTY(int id READ id) Q_PROPERTY(bool locked READ locked WRITE setLocked) // panel properties Q_PROPERTY(QString alignment READ alignment WRITE setAlignment) Q_PROPERTY(int offset READ offset WRITE setOffset) Q_PROPERTY(int length READ length WRITE setLength) Q_PROPERTY(int minimumLength READ minimumLength WRITE setMinimumLength) Q_PROPERTY(int maximumLength READ maximumLength WRITE setMaximumLength) Q_PROPERTY(int height READ height WRITE setHeight) Q_PROPERTY(QString hiding READ hiding WRITE setHiding) public: explicit Panel(Plasma::Containment *containment, ScriptEngine *parent); ~Panel() override; QString location() const; void setLocation(const QString &location); QString alignment() const; void setAlignment(const QString &alignment); int offset() const; void setOffset(int pixels); int length() const; void setLength(int pixels); int minimumLength() const; void setMinimumLength(int pixels); int maximumLength() const; void setMaximumLength(int pixels); int height() const; void setHeight(int height); QString hiding() const; void setHiding(const QString &mode); public Q_SLOTS: void remove() { Containment::remove(); } void showConfigurationInterface() { Containment::showConfigurationInterface(); } private: PanelView *panel() const; KConfigGroup panelConfig() const; ShellCorona *m_corona; }; } #endif diff --git a/shell/scripting/scriptengine.cpp b/shell/scripting/scriptengine.cpp index f74cae499..a4539ddc4 100644 --- a/shell/scripting/scriptengine.cpp +++ b/shell/scripting/scriptengine.cpp @@ -1,442 +1,447 @@ /* * Copyright 2009 Aaron Seigo * * 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 "scriptengine.h" #include "scriptengine_v1.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // KIO //#include // no camelcase include #include #include #include #include #include #include #include "appinterface.h" #include "containment.h" #include "configgroup.h" #include "panel.h" #include "widget.h" #include "../shellcorona.h" #include "../standaloneappcorona.h" #include "../screenpool.h" namespace WorkspaceScripting { ScriptEngine::ScriptEngine(Plasma::Corona *corona, QObject *parent) : QJSEngine(parent), m_corona(corona) { Q_ASSERT(m_corona); m_appInterface = new AppInterface(this); connect(m_appInterface, &AppInterface::print, this, &ScriptEngine::print); m_scriptSelf = globalObject(); m_globalScriptEngineObject = new ScriptEngine::V1(this); m_localizedContext = new KLocalizedContext(this); setupEngine(); } ScriptEngine::~ScriptEngine() { } QString ScriptEngine::errorString() const { return m_errorString; } QJSValue ScriptEngine::wrap(Plasma::Applet *w) { Widget *wrapper = new Widget(w); return newQObject(wrapper); } QJSValue ScriptEngine::wrap(Plasma::Containment *c) { Containment *wrapper = isPanel(c) ? new Panel(c, this) : new Containment(c, this); return newQObject(wrapper); } int ScriptEngine::defaultPanelScreen() const { return 1; } +QJSValue ScriptEngine::newError(const QString &message) +{ + return evaluate(QString("new Error('%1');").arg(message)); +} + QString ScriptEngine::onlyExec(const QString &commandLine) { if (commandLine.isEmpty()) { return commandLine; } return KShell::splitArgs(commandLine, KShell::TildeExpand).first(); } void ScriptEngine::setupEngine() { QJSValue globalScriptEngineObject = newQObject(m_globalScriptEngineObject); QJSValue localizedContext = newQObject(m_localizedContext); QJSValue appInterface = newQObject(m_appInterface); //AppInterface stuff //FIXME: this line doesn't have much effect for now, if QTBUG-68397 gets fixed, //all the connects to rewrite the properties won't be necessary anymore //globalObject().setPrototype(appInterface); //FIXME: remove __AppInterface if QTBUG-68397 gets solved //as workaround we build manually a js object with getters and setters m_scriptSelf.setProperty(QStringLiteral("__AppInterface"), appInterface); QJSValue res = evaluate("__proto__ = {\ get locked() {return __AppInterface.locked;},\ get hasBattery() {return __AppInterface.hasBattery;},\ get screenCount() {return __AppInterface.screenCount;},\ get activityIds() {return __AppInterface.activityIds;},\ get panelIds() {return __AppInterface.panelIds;},\ get knownPanelTypes() {return __AppInterface.knownPanelTypes;},\ get knownActivityTypes() {return __AppInterface.knownActivityTypes;},\ get knownWidgetTypes() {return __AppInterface.knownWidgetTypes;},\ get theme() {return __AppInterface.theme;},\ set theme(name) {__AppInterface.theme = name;},\ get applicationVersion() {return __AppInterface.applicationVersion;},\ get platformVersion() {return __AppInterface.platformVersion;},\ get scriptingVersion() {return __AppInterface.scriptingVersion;},\ get multihead() {return __AppInterface.multihead;},\ get multiheadScreen() {return __AppInterface.multihead;},\ get locale() {return __AppInterface.locale;},\ get language() {return __AppInterface.language;},\ get languageId() {return __AppInterface.languageId;},\ }"); Q_ASSERT(!res.isError()); //methods from AppInterface m_scriptSelf.setProperty(QStringLiteral("screenGeometry"), appInterface.property("screenGeometry")); m_scriptSelf.setProperty(QStringLiteral("lockCorona"), appInterface.property("lockCorona")); m_scriptSelf.setProperty(QStringLiteral("sleep"), appInterface.property("sleep")); m_scriptSelf.setProperty(QStringLiteral("print"), appInterface.property("print")); m_scriptSelf.setProperty(QStringLiteral("getApiVersion"), globalScriptEngineObject.property("getApiVersion")); //Constructors: prefer them js based as they make the c++ code of panel et al way simpler without hacks to get the engine m_scriptSelf.setProperty(QStringLiteral("__newPanel"), globalScriptEngineObject.property("newPanel")); m_scriptSelf.setProperty(QStringLiteral("__newConfigFile"), globalScriptEngineObject.property("configFile")); //definitions of qrectf properties from documentation //only properties/functions which were already binded are. //TODO KF6: just a plain QRectF binding res = evaluate("function QRectF(x,y,w,h) {\ return {x: x, y: y, width: w, height: h,\ get left() {return this.x},\ get top() {return this.y},\ get right() {return this.x + this.width},\ get bottom() {return this.y + this.height},\ get empty() {return this.width <= 0 || this.height <= 0},\ get null() {return this.width == 0 || this.height == 0},\ get valid() {return !this.empty},\ adjust: function(dx1, dy1, dx2, dy2) {\ this.x += dx1; this.y += dy1;\ this.width = this.width - dx1 + dx2;\ this.height = this.height - dy1 + dy2;},\ adjusted: function(dx1, dy1, dx2, dy2) {\ return new QRectF(this.x + dx1, this.y + dy1,\ this.width - dx1 + dx2,\ this.height - dy1 + dy2)},\ translate: function(dx, dy) {this.x += dx; this.y += dy;},\ setCoords: function(x1, y1, x2, y2) {\ this.x = x1; this.y = y1;\ this.width = x2 - x1;\ this.height = y2 - y1;},\ setRect: function(x1, y1, w1, h1) {\ this.x = x1; this.y = y1;\ this.width = w1; this.height = h1;},\ contains: function(x1, y1) { return x1 >= this.x && x1 <= this.x + this.width && y1 >= this.y && y1 <= this.y + this.height},\ moveBottom: function(bottom1) {this.y = bottom1 - this.height;},\ moveLeft: function(left1) {this.x = left1;},\ moveRight: function(right1) {this.x = right1 - this.width;},\ moveTop: function(top1) {this.y = top1;},\ moveTo: function(x1, y1) {this.x = x1; this.y = y1;}\ }};\ function ConfigFile(config, group){return __newConfigFile(config, group)};\ function Panel(plugin){return __newPanel(plugin)};"); Q_ASSERT(!res.isError()); m_scriptSelf.setProperty(QStringLiteral("createActivity"), globalScriptEngineObject.property("createActivity")); m_scriptSelf.setProperty(QStringLiteral("setCurrentActivity"), globalScriptEngineObject.property("setCurrentActivity")); m_scriptSelf.setProperty(QStringLiteral("currentActivity"), globalScriptEngineObject.property("currentActivity")); m_scriptSelf.setProperty(QStringLiteral("activities"), globalScriptEngineObject.property("activities")); m_scriptSelf.setProperty(QStringLiteral("activityName"), globalScriptEngineObject.property("activityName")); m_scriptSelf.setProperty(QStringLiteral("setActivityName"), globalScriptEngineObject.property("setActivityName")); m_scriptSelf.setProperty(QStringLiteral("loadSerializedLayout"), globalScriptEngineObject.property("loadSerializedLayout")); m_scriptSelf.setProperty(QStringLiteral("desktopsForActivity"), globalScriptEngineObject.property("desktopsForActivity")); m_scriptSelf.setProperty(QStringLiteral("desktops"), globalScriptEngineObject.property("desktops")); m_scriptSelf.setProperty(QStringLiteral("desktopById"), globalScriptEngineObject.property("desktopById")); m_scriptSelf.setProperty(QStringLiteral("desktopForScreen"), globalScriptEngineObject.property("desktopForScreen")); m_scriptSelf.setProperty(QStringLiteral("panelById"), globalScriptEngineObject.property("panelById")); m_scriptSelf.setProperty(QStringLiteral("panels"), globalScriptEngineObject.property("panels")); m_scriptSelf.setProperty(QStringLiteral("fileExists"), globalScriptEngineObject.property("fileExists")); m_scriptSelf.setProperty(QStringLiteral("loadTemplate"), globalScriptEngineObject.property("loadTemplate")); m_scriptSelf.setProperty(QStringLiteral("applicationExists"), globalScriptEngineObject.property("applicationExists")); m_scriptSelf.setProperty(QStringLiteral("defaultApplication"), globalScriptEngineObject.property("defaultApplication")); m_scriptSelf.setProperty(QStringLiteral("userDataPath"), globalScriptEngineObject.property("userDataPath")); m_scriptSelf.setProperty(QStringLiteral("applicationPath"), globalScriptEngineObject.property("applicationPath")); m_scriptSelf.setProperty(QStringLiteral("knownWallpaperPlugins"), globalScriptEngineObject.property("knownWallpaperPlugins")); m_scriptSelf.setProperty(QStringLiteral("gridUnit"), globalScriptEngineObject.property("gridUnit")); m_scriptSelf.setProperty(QStringLiteral("setImmutability"), globalScriptEngineObject.property("setImmutability")); m_scriptSelf.setProperty(QStringLiteral("immutability"), globalScriptEngineObject.property("immutability")); //i18n m_scriptSelf.setProperty(QStringLiteral("i18n"), localizedContext.property("i18n")); m_scriptSelf.setProperty(QStringLiteral("i18nc"), localizedContext.property("i18nc")); m_scriptSelf.setProperty(QStringLiteral("i18np"), localizedContext.property("i18np")); m_scriptSelf.setProperty(QStringLiteral("i18ncp"), localizedContext.property("i18ncp")); m_scriptSelf.setProperty(QStringLiteral("i18nd"), localizedContext.property("i18nd")); m_scriptSelf.setProperty(QStringLiteral("i18ndc"), localizedContext.property("i18ndc")); m_scriptSelf.setProperty(QStringLiteral("i18ndp"), localizedContext.property("i18ndp")); m_scriptSelf.setProperty(QStringLiteral("i18ndcp"), localizedContext.property("i18ndcp")); m_scriptSelf.setProperty(QStringLiteral("xi18n"), localizedContext.property("xi18n")); m_scriptSelf.setProperty(QStringLiteral("xi18nc"), localizedContext.property("xi18nc")); m_scriptSelf.setProperty(QStringLiteral("xi18np"), localizedContext.property("xi18np")); m_scriptSelf.setProperty(QStringLiteral("xi18ncp"), localizedContext.property("xi18ncp")); m_scriptSelf.setProperty(QStringLiteral("xi18nd"), localizedContext.property("xi18nd")); m_scriptSelf.setProperty(QStringLiteral("xi18ndc"), localizedContext.property("xi18ndc")); m_scriptSelf.setProperty(QStringLiteral("xi18ndp"), localizedContext.property("xi18ndp")); m_scriptSelf.setProperty(QStringLiteral("xi18ndcp"), localizedContext.property("xi18ndcp")); } bool ScriptEngine::isPanel(const Plasma::Containment *c) { if (!c) { return false; } return c->containmentType() == Plasma::Types::PanelContainment || c->containmentType() == Plasma::Types::CustomPanelContainment; } Plasma::Corona *ScriptEngine::corona() const { return m_corona; } bool ScriptEngine::evaluateScript(const QString &script, const QString &path) { m_errorString = QString(); QJSValue result = evaluate(script, path); if (result.isError()) { //qDebug() << "catch the exception!"; QString error = i18n("Error: %1 at line %2\n\nBacktrace:\n%3", result.toString(), result.property("lineNumber").toInt(), result.property("stack").toVariant().value().join(QStringLiteral("\n "))); emit printError(error); emit exception(result); m_errorString = error; return false; } return true; } void ScriptEngine::exception(const QJSValue &value) { //qDebug() << "exception caught!" << value.toVariant(); emit printError(value.toVariant().toString()); } QStringList ScriptEngine::pendingUpdateScripts(Plasma::Corona *corona) { if (!corona->package().metadata().isValid()) { qWarning() << "Warning: corona package invalid"; return QStringList(); } const QString appName = corona->package().metadata().pluginName(); QStringList scripts; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "plasma/shells/" + appName + QStringLiteral("/contents/updates"), QStandardPaths::LocateDirectory); Q_FOREACH(const QString& dir, dirs) { QDirIterator it(dir, QStringList() << QStringLiteral("*.js")); while (it.hasNext()) { scripts.append(it.next()); } } QStringList scriptPaths; if (scripts.isEmpty()) { //qDebug() << "no update scripts"; return scriptPaths; } KConfigGroup cg(KSharedConfig::openConfig(), "Updates"); QStringList performed = cg.readEntry("performed", QStringList()); const QString localXdgDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); foreach (const QString &script, scripts) { if (performed.contains(script)) { continue; } if (script.startsWith(localXdgDir)) { // qDebug() << "skipping user local script: " << script; continue; } scriptPaths.append(script); performed.append(script); } cg.writeEntry("performed", performed); KSharedConfig::openConfig()->sync(); return scriptPaths; } QStringList ScriptEngine::availableActivities() const { ShellCorona *sc = qobject_cast(m_corona); StandaloneAppCorona *ac = qobject_cast(m_corona); if (sc) { return sc->availableActivities(); } else if (ac) { return ac->availableActivities(); } return QStringList(); } QList ScriptEngine::desktopsForActivity(const QString &id) { QList result; // confirm this activity actually exists bool found = false; for (const QString &act: availableActivities()) { if (act == id) { found = true; break; } } if (!found) { return result; } foreach (Plasma::Containment *c, m_corona->containments()) { if (c->activity() == id && !isPanel(c)) { result << new Containment(c, this); } } if (result.count() == 0) { // we have no desktops for this activity, so lets make them now // this can happen when the activity already exists but has never been activated // with the current shell package and layout.js is run to set up the shell for the // first time ShellCorona *sc = qobject_cast(m_corona); StandaloneAppCorona *ac = qobject_cast(m_corona); if (sc) { foreach (int i, sc->screenIds()) { result << new Containment(sc->createContainmentForActivity(id, i), this); } } else if (ac) { const int numScreens = m_corona->numScreens(); for (int i = 0; i < numScreens; ++i) { result << new Containment(ac->createContainmentForActivity(id, i), this); } } } return result; } Plasma::Containment *ScriptEngine::createContainment(const QString &type, const QString &plugin) { bool exists = false; const KPluginInfo::List list = Plasma::PluginLoader::listContainmentsOfType(type); foreach (const KPluginInfo &info, list) { if (info.pluginName() == plugin) { exists = true; break; } } if (!exists) { return nullptr; } Plasma::Containment *c = nullptr; if (type == QLatin1String("Panel")) { ShellCorona *sc = qobject_cast(m_corona); StandaloneAppCorona *ac = qobject_cast(m_corona); if (sc) { c = sc->addPanel(plugin); } else if (ac) { c = ac->addPanel(plugin); } } else { c = m_corona->createContainment(plugin); } if (c) { if (type == QLatin1String("Panel")) { // some defaults c->setFormFactor(Plasma::Types::Horizontal); c->setLocation(Plasma::Types::TopEdge); //we have to force lastScreen of the newly created containment, //or it won't have a screen yet at that point, breaking JS code //that relies on it //NOTE: if we'll allow setting a panel screen from JS, it will have to use the following lines as well KConfigGroup cg=c->config(); cg.writeEntry(QStringLiteral("lastScreen"), 0); c->restore(cg); } c->updateConstraints(Plasma::Types::AllConstraints | Plasma::Types::StartupCompletedConstraint); c->flushPendingConstraintsEvents(); } return c; } Containment *ScriptEngine::createContainmentWrapper(const QString &type, const QString &plugin) { Plasma::Containment *c = createContainment(type, plugin); return isPanel(c) ? new Panel(c, this) : new Containment(c, this); } } // namespace WorkspaceScripting diff --git a/shell/scripting/scriptengine.h b/shell/scripting/scriptengine.h index 77bdf3cdc..d48bfc351 100644 --- a/shell/scripting/scriptengine.h +++ b/shell/scripting/scriptengine.h @@ -1,103 +1,104 @@ /* * Copyright 2009 Aaron Seigo * * 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. */ #ifndef SCRIPTENGINE #define SCRIPTENGINE #include #include #include #include #include "../shellcorona.h" namespace Plasma { class Applet; class Containment; } // namespace Plasma class KLocalizedContext; namespace WorkspaceScripting { class AppInterface; class Containment; class V1; class ScriptEngine : public QJSEngine { Q_OBJECT public: explicit ScriptEngine(Plasma::Corona *corona, QObject *parent = nullptr); ~ScriptEngine() override; QString errorString() const; static QStringList pendingUpdateScripts(Plasma::Corona *corona); Plasma::Corona *corona() const; QJSValue wrap(Plasma::Applet *w); QJSValue wrap(Plasma::Containment *c); virtual int defaultPanelScreen() const; + QJSValue newError(const QString &message); static bool isPanel(const Plasma::Containment *c); Plasma::Containment *createContainment(const QString &type, const QString &plugin); public Q_SLOTS: bool evaluateScript(const QString &script, const QString &path = QString()); Q_SIGNALS: void print(const QString &string); void printError(const QString &string); private: void setupEngine(); static QString onlyExec(const QString &commandLine); // Script API versions class V1; // helpers QStringList availableActivities() const; QList desktopsForActivity(const QString &id); Containment *createContainmentWrapper(const QString &type, const QString &plugin); private Q_SLOTS: void exception(const QJSValue &value); private: Plasma::Corona *m_corona; ScriptEngine::V1 *m_globalScriptEngineObject; KLocalizedContext *m_localizedContext; AppInterface *m_appInterface; QJSValue m_scriptSelf; QString m_errorString; }; static const int PLASMA_DESKTOP_SCRIPTING_VERSION = 20; } #endif diff --git a/shell/scripting/scriptengine_v1.cpp b/shell/scripting/scriptengine_v1.cpp index ef4f11a76..486df853c 100644 --- a/shell/scripting/scriptengine_v1.cpp +++ b/shell/scripting/scriptengine_v1.cpp @@ -1,898 +1,898 @@ /* * Copyright 2009 Aaron Seigo * * 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 "scriptengine_v1.h" #include #include #include #include #include #include #include #include #include #include #include #include // KIO //#include // no camelcase include #include #include #include #include #include #include #include "appinterface.h" #include "containment.h" #include "configgroup.h" #include "panel.h" #include "widget.h" #include "../shellcorona.h" #include "../standaloneappcorona.h" #include "../screenpool.h" namespace { template inline void awaitFuture(const QFuture &future) { while (!future.isFinished()) { QCoreApplication::processEvents(); } } class ScriptArray_forEach_Helper { public: ScriptArray_forEach_Helper(const QJSValue &array) : array(array) { } // operator + is commonly used for these things // to avoid having the lambda inside the parenthesis template void operator+ (Function function) const { if (!array.isArray()) return; int length = array.property("length").toInt(); for (int i = 0; i < length; ++i) { function(array.property(i)); } } private: const QJSValue &array; }; #define SCRIPT_ARRAY_FOREACH(Variable, Array) \ ScriptArray_forEach_Helper(Array) + [&] (const QJSValue &Variable) class ScriptObject_forEach_Helper { public: ScriptObject_forEach_Helper(const QJSValue &object) : object(object) { } // operator + is commonly used for these things // to avoid having the lambda inside the parenthesis template void operator+ (Function function) const { QJSValueIterator it(object); while (it.hasNext()) { it.next(); function(it.name(), it.value()); } } private: const QJSValue &object; }; #define SCRIPT_OBJECT_FOREACH(Key, Value, Array) \ ScriptObject_forEach_Helper(Array) + [&] (const QString &Key, const QJSValue &Value) // Case insensitive comparison of two strings template inline bool matches(const QString &object, const StringType &string) { return object.compare(string, Qt::CaseInsensitive) == 0; } } namespace WorkspaceScripting { ScriptEngine::V1::V1(ScriptEngine *parent) : QObject(parent), m_engine(parent) {} ScriptEngine::V1::~V1() {} QJSValue ScriptEngine::V1::getApiVersion(const QJSValue ¶m) { if (param.toInt() != 1) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("maximum api version supported is 1"))); + return m_engine->newError(i18n("maximum api version supported is 1")); } return m_engine->newQObject(this); } int ScriptEngine::V1::gridUnit() const { int gridUnit = QFontMetrics(QGuiApplication::font()).boundingRect(QStringLiteral("M")).height(); if (gridUnit % 2 != 0) { gridUnit++; } return gridUnit; } QJSValue ScriptEngine::V1::desktopById(const QJSValue ¶m) const { //this needs to work also for string of numberls, like "20" if (param.isUndefined()) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("desktopById required an id"))); + return m_engine->newError(i18n("desktopById required an id")); } const quint32 id = param.toInt(); foreach (Plasma::Containment *c, m_engine->m_corona->containments()) { if (c->id() == id && !isPanel(c)) { return m_engine->wrap(c); } } return QJSValue(); } QJSValue ScriptEngine::V1::desktopsForActivity(const QJSValue &actId) const { if (!actId.isString()) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("desktopsForActivity requires an id"))); + return m_engine->newError(i18n("desktopsForActivity requires an id")); } QJSValue containments = m_engine->newArray(); int count = 0; const QString id = actId.toString(); const auto result = m_engine->desktopsForActivity(id); for (Containment* c: result) { containments.setProperty(count, m_engine->newQObject(c)); ++count; } containments.setProperty(QStringLiteral("length"), count); return containments; } QJSValue ScriptEngine::V1::desktopForScreen(const QJSValue ¶m) const { //this needs to work also for string of numberls, like "20" if (param.isUndefined()) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("activityForScreen requires a screen id"))); + return m_engine->newError(i18n("activityForScreen requires a screen id")); } const uint screen = param.toInt(); return m_engine->wrap(m_engine->m_corona->containmentForScreen(screen)); } QJSValue ScriptEngine::V1::createActivity(const QJSValue &nameParam, const QString &pluginParam) { if (!nameParam.isString()) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("createActivity required the activity name"))); + return m_engine->newError(i18n("createActivity required the activity name")); } QString plugin = pluginParam; const QString name = nameParam.toString(); KActivities::Controller controller; // This is not the nicest way to do this, but createActivity // is a synchronous API :/ QFuture futureId = controller.addActivity(name); awaitFuture(futureId); QString id = futureId.result(); qDebug() << "Setting default Containment plugin:" << plugin; ShellCorona *sc = qobject_cast(m_engine->m_corona); StandaloneAppCorona *ac = qobject_cast(m_engine->m_corona); if (sc) { if (plugin.isEmpty() || plugin == QLatin1String("undefined")) { plugin = sc->defaultContainmentPlugin(); } sc->insertActivity(id, plugin); } else if (ac) { if (plugin.isEmpty() || plugin == QLatin1String("undefined")) { KConfigGroup shellCfg = KConfigGroup(KSharedConfig::openConfig(m_engine->m_corona->package().filePath("defaults")), "Desktop"); plugin = shellCfg.readEntry("Containment", "org.kde.desktopcontainment"); } ac->insertActivity(id, plugin); } return m_engine->toScriptValue(id); } QJSValue ScriptEngine::V1::setCurrentActivity(const QJSValue ¶m) { if (!param.isString()) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("setCurrentActivity required the activity id"))); + return m_engine->newError(i18n("setCurrentActivity required the activity id")); } const QString id = param.toString(); KActivities::Controller controller; QFuture task = controller.setCurrentActivity(id); awaitFuture(task); return task.result(); } QJSValue ScriptEngine::V1::setActivityName(const QJSValue &idParam, const QJSValue &nameParam) { if (!idParam.isString() || !nameParam.isString()) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("setActivityName required the activity id and name"))); + return m_engine->newError(i18n("setActivityName required the activity id and name")); } const QString id = idParam.toString(); const QString name = nameParam.toString(); KActivities::Controller controller; QFuture task = controller.setActivityName(id, name); awaitFuture(task); return QJSValue(); } QJSValue ScriptEngine::V1::activityName(const QJSValue &idParam) const { if (!idParam.isString()) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("activityName required the activity id"))); + return m_engine->newError(i18n("activityName required the activity id")); } const QString id = idParam.toString(); KActivities::Info info(id); return QJSValue(info.name()); } QString ScriptEngine::V1::currentActivity() const { KActivities::Consumer consumer; return consumer.currentActivity(); } QJSValue ScriptEngine::V1::activities() const { QJSValue acts = m_engine->newArray(); int count = 0; const auto result = m_engine->availableActivities(); for (const auto a : result) { acts.setProperty(count, a); ++count; } acts.setProperty(QStringLiteral("length"), count); return acts; } // Utility function to process configs and config groups template void loadSerializedConfigs(Object *object, const QJSValue &configs) { SCRIPT_OBJECT_FOREACH(escapedGroup, config, configs) { // If the config group is set, pass it on to the containment QStringList groups = escapedGroup.split('/', QString::SkipEmptyParts); for (QString &group: groups) { group = QUrl::fromPercentEncoding(group.toUtf8()); } qDebug() << "Config group" << groups; object->setCurrentConfigGroup(groups); // Read other properties and set the configuration SCRIPT_OBJECT_FOREACH(key, value, config) { object->writeConfig(key, value.toVariant()); }; }; } QJSValue ScriptEngine::V1::loadSerializedLayout(const QJSValue &data) { if (!data.isObject()) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("loadSerializedLayout requires the JSON object to deserialize from"))); + return m_engine->newError(i18n("loadSerializedLayout requires the JSON object to deserialize from")); } if (data.property("serializationFormatVersion").toInt() != 1) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("loadSerializedLayout: invalid version of the serialized object"))); + return m_engine->newError(i18n("loadSerializedLayout: invalid version of the serialized object")); } const auto desktops = m_engine->desktopsForActivity(KActivities::Consumer().currentActivity()); Q_ASSERT_X(desktops.size() != 0, "V1::loadSerializedLayout", "We need desktops"); // qDebug() << "DESKTOP DESERIALIZATION: Loading desktops..."; int count = 0; SCRIPT_ARRAY_FOREACH(desktopData, data.property("desktops")) { // If the template has more desktops than we do, ignore them if (count >= desktops.size()) return; auto desktop = desktops[count]; // qDebug() << "DESKTOP DESERIALIZATION: var cont = desktopsArray[...]; " << count << " -> " << desktop; // Setting the wallpaper plugin because it is special desktop->setWallpaperPlugin(desktopData.property("wallpaperPlugin").toString()); // qDebug() << "DESKTOP DESERIALIZATION: cont->setWallpaperPlugin(...) " << desktop->wallpaperPlugin(); // Now, lets go through the configs loadSerializedConfigs(desktop, desktopData.property("config")); // After the config, we want to load the applets SCRIPT_ARRAY_FOREACH(appletData, desktopData.property("applets")) { // qDebug() << "DESKTOP DESERIALIZATION: Applet: " << appletData.toString(); auto appletObject = desktop->addWidget( appletData.property("plugin"), appletData.property("geometry.x").toInt() * gridUnit(), appletData.property("geometry.y").toInt() * gridUnit(), appletData.property("geometry.width").toInt() * gridUnit(), appletData.property("geometry.height").toInt() * gridUnit()); if (auto applet = qobject_cast(appletObject.toQObject())) { // Now, lets go through the configs for the applet loadSerializedConfigs(applet, appletData.property("config")); } }; count++; }; // qDebug() << "PANEL DESERIALIZATION: Loading panels..."; SCRIPT_ARRAY_FOREACH(panelData, data.property("panels")) { const auto panel = qobject_cast(m_engine->createContainmentWrapper( QStringLiteral("Panel"), QStringLiteral("org.kde.panel"))); Q_ASSERT(panel); // Basic panel setup panel->setLocation(panelData.property("location").toString()); panel->setHeight(panelData.property("height").toNumber() * gridUnit()); panel->setMaximumLength(panelData.property("maximumLength").toNumber() * gridUnit()); panel->setMinimumLength(panelData.property("minimumLength").toNumber() * gridUnit()); panel->setOffset(panelData.property("offset").toNumber() * gridUnit()); panel->setAlignment(panelData.property("alignment").toString()); panel->setHiding(panelData.property("hiding").toString()); // Loading the config for the panel loadSerializedConfigs(panel, panelData.property("config")); // Now dealing with the applets SCRIPT_ARRAY_FOREACH(appletData, panelData.property("applets")) { // qDebug() << "PANEL DESERIALIZATION: Applet: " << appletData.toString(); auto appletObject = panel->addWidget(appletData.property("plugin")); // qDebug() << "PANEL DESERIALIZATION: addWidget" // << appletData.property("plugin").toString() // ; if (auto applet = qobject_cast(appletObject.toQObject())) { // Now, lets go through the configs for the applet loadSerializedConfigs(applet, appletData.property("config")); } }; }; return QJSValue(); } QJSValue ScriptEngine::V1::newPanel(const QString &plugin) { return createContainment(QStringLiteral("Panel"), QStringLiteral("org.kde.panel"), plugin); } QJSValue ScriptEngine::V1::panelById(const QJSValue &idParam) const { //this needs to work also for string of numberls, like "20" if (idParam.isUndefined()) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("panelById requires an id"))); + return m_engine->newError(i18n("panelById requires an id")); } const quint32 id = idParam.toInt(); foreach (Plasma::Containment *c, m_engine->m_corona->containments()) { if (c->id() == id && isPanel(c)) { return m_engine->wrap(c); } } return QJSValue(); } QJSValue ScriptEngine::V1::desktops() const { QJSValue containments = m_engine->newArray(); int count = 0; const auto result = m_engine->m_corona->containments(); for (const auto c : result) { // make really sure we get actual desktops, so check for a non empty // activty id if (!isPanel(c) && !c->activity().isEmpty()) { containments.setProperty(count, m_engine->wrap(c)); ++count; } } containments.setProperty(QStringLiteral("length"), count); return containments; } QJSValue ScriptEngine::V1::panels() const { QJSValue panels = m_engine->newArray(); int count = 0; const auto result = m_engine->m_corona->containments(); for (const auto c : result) { panels.setProperty(count, m_engine->wrap(c)); ++count; } panels.setProperty(QStringLiteral("length"), count); return panels; } bool ScriptEngine::V1::fileExists(const QString &path) const { if (path.isEmpty()) { return false; } QFile f(KShell::tildeExpand(path)); return f.exists(); } bool ScriptEngine::V1::loadTemplate(const QString &layout) { if (layout.isEmpty() || layout.contains(QStringLiteral("'"))) { // qDebug() << "layout is empty"; return false; } auto filter = [&layout](const KPluginMetaData &md) -> bool { return md.pluginId() == layout && KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-ContainmentCategories")).contains(QStringLiteral("panel")); }; QList offers = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/LayoutTemplate"), QString(), filter); if (offers.isEmpty()) { // qDebug() << "offers fail" << constraint; return false; } KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LayoutTemplate")); KPluginMetaData pluginData(offers.first()); QString path; { ShellCorona *sc = qobject_cast(m_engine->m_corona); if (sc) { const QString overridePackagePath = sc->lookAndFeelPackage().path() + QStringLiteral("contents/layouts/") + pluginData.pluginId(); path = overridePackagePath + QStringLiteral("/metadata.json"); if (QFile::exists(path)) { package.setPath(overridePackagePath); } path = overridePackagePath + QStringLiteral("/metadata.desktop"); if (QFile::exists(path)) { package.setPath(overridePackagePath); } } } if (!package.isValid()) { path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, package.defaultPackageRoot() + pluginData.pluginId() + "/metadata.json"); if (path.isEmpty()) { path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, package.defaultPackageRoot() + pluginData.pluginId() + "/metadata.desktop"); } if (path.isEmpty()) { // qDebug() << "script path is empty"; return false; } package.setPath(pluginData.pluginId()); } const QString scriptFile = package.filePath("mainscript"); if (scriptFile.isEmpty()) { // qDebug() << "scriptfile is empty"; return false; } QFile file(scriptFile); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << i18n("Unable to load script file: %1", path); return false; } QString script = file.readAll(); if (script.isEmpty()) { // qDebug() << "script is empty"; return false; } ScriptEngine *engine = new ScriptEngine(m_engine->corona(), this); engine->globalObject().setProperty(QStringLiteral("templateName"), pluginData.name()); engine->globalObject().setProperty(QStringLiteral("templateComment"), pluginData.description()); engine->evaluateScript(script, path); engine->deleteLater(); return true; } bool ScriptEngine::V1::applicationExists(const QString &application) const { if (application.isEmpty()) { return false; } // first, check for it in $PATH if (!QStandardPaths::findExecutable(application).isEmpty()) { return true; } if (KService::serviceByStorageId(application)) { return true; } if (application.contains(QStringLiteral("'"))) { // apostrophes just screw up the trader lookups below, so check for it return false; } // next, consult ksycoca for an app by that name if (!KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("Name =~ '%1'").arg(application)).isEmpty()) { return true; } // next, consult ksycoca for an app by that generic name if (!KServiceTypeTrader::self()->query(QStringLiteral("Application"), QStringLiteral("GenericName =~ '%1'").arg(application)).isEmpty()) { return true; } return false; } QJSValue ScriptEngine::V1::defaultApplication(const QString &application, bool storageId) const { if (application.isEmpty()) { return false; } // FIXME: there are some pretty horrible hacks below, in the sense that they // assume a very // specific implementation system. there is much room for improvement here. // see // kdebase-runtime/kcontrol/componentchooser/ for all the gory details ;) if (matches(application, QLatin1String("mailer"))) { // KEMailSettings settings; // in KToolInvocation, the default is kmail; but let's be friendlier :) // QString command = settings.getSetting(KEMailSettings::ClientProgram); QString command; if (command.isEmpty()) { if (KService::Ptr kontact = KService::serviceByStorageId(QStringLiteral("kontact"))) { return storageId ? kontact->storageId() : onlyExec(kontact->exec()); } else if (KService::Ptr kmail = KService::serviceByStorageId(QStringLiteral("kmail"))) { return storageId ? kmail->storageId() : onlyExec(kmail->exec()); } } if (!command.isEmpty()) { if (false) { KConfigGroup confGroup(KSharedConfig::openConfig(), "General"); const QString preferredTerminal = confGroup.readPathEntry( "TerminalApplication", QStringLiteral("konsole")); command = preferredTerminal + QLatin1String(" -e ") + command; } return command; } } else if (matches(application, QLatin1String("browser"))) { KConfigGroup config(KSharedConfig::openConfig(), "General"); QString browserApp = config.readPathEntry("BrowserApplication", QString()); if (browserApp.isEmpty()) { const KService::Ptr htmlApp = KMimeTypeTrader::self()->preferredService(QStringLiteral("text/html")); if (htmlApp) { browserApp = storageId ? htmlApp->storageId() : htmlApp->exec(); } } else if (browserApp.startsWith('!')) { browserApp = browserApp.mid(1); } return onlyExec(browserApp); } else if (matches(application, QLatin1String("terminal"))) { KConfigGroup confGroup(KSharedConfig::openConfig(), "General"); return onlyExec(confGroup.readPathEntry("TerminalApplication", QStringLiteral("konsole"))); } else if (matches(application, QLatin1String("filemanager"))) { KService::Ptr service = KMimeTypeTrader::self()->preferredService( QStringLiteral("inode/directory")); if (service) { return storageId ? service->storageId() : onlyExec(service->exec()); } } else if (matches(application, QLatin1String("windowmanager"))) { KConfig cfg(QStringLiteral("ksmserverrc"), KConfig::NoGlobals); KConfigGroup confGroup(&cfg, "General"); return onlyExec( confGroup.readEntry("windowManager", QStringLiteral("kwin"))); } else if (KService::Ptr service = KMimeTypeTrader::self()->preferredService(application)) { return storageId ? service->storageId() : onlyExec(service->exec()); } else { // try the files in share/apps/kcm_componentchooser/ const QStringList services = QStandardPaths::locateAll( QStandardPaths::GenericDataLocation, QStringLiteral("kcm_componentchooser/")); qDebug() << "ok, trying in" << services; foreach (const QString &service, services) { if (!service.endsWith(QLatin1String(".desktop"))) { continue; } KConfig config(service, KConfig::SimpleConfig); KConfigGroup cg = config.group(QByteArray()); const QString type = cg.readEntry("valueName", QString()); // qDebug() << " checking" << service << type << application; if (matches(type, application)) { KConfig store( cg.readPathEntry("storeInFile", QStringLiteral("null"))); KConfigGroup storeCg(&store, cg.readEntry("valueSection", QString())); const QString exec = storeCg.readPathEntry( cg.readEntry("valueName", "kcm_componenchooser_null"), cg.readEntry("defaultImplementation", QString())); if (!exec.isEmpty()) { return exec; } break; } } } return false; } QJSValue ScriptEngine::V1::applicationPath(const QString &application) const { if (application.isEmpty()) { return false; } // first, check for it in $PATH const QString path = QStandardPaths::findExecutable(application); if (!path.isEmpty()) { return path; } if (KService::Ptr service = KService::serviceByStorageId(application)) { return QStandardPaths::locate(QStandardPaths::ApplicationsLocation, service->entryPath()); } if (application.contains(QStringLiteral("'"))) { // apostrophes just screw up the trader lookups below, so check for it return QString(); } // next, consult ksycoca for an app by that name KService::List offers = KServiceTypeTrader::self()->query( QStringLiteral("Application"), QStringLiteral("Name =~ '%1'").arg(application)); if (offers.isEmpty()) { // next, consult ksycoca for an app by that generic name offers = KServiceTypeTrader::self()->query( QStringLiteral("Application"), QStringLiteral("GenericName =~ '%1'").arg(application)); } if (!offers.isEmpty()) { KService::Ptr offer = offers.first(); return QStandardPaths::locate(QStandardPaths::ApplicationsLocation, offer->entryPath()); } return QString(); } QJSValue ScriptEngine::V1::userDataPath(const QString &type, const QString &path) const { if (type.isEmpty()) { return QDir::homePath(); } QStandardPaths::StandardLocation location = QStandardPaths::GenericDataLocation; if (matches(type, QLatin1String("desktop"))) { location = QStandardPaths::DesktopLocation; } else if (matches(type, QLatin1String("documents"))) { location = QStandardPaths::DocumentsLocation; } else if (matches(type, QLatin1String("music"))) { location = QStandardPaths::MusicLocation; } else if (matches(type, QLatin1String("video"))) { location = QStandardPaths::MoviesLocation; } else if (matches(type, QLatin1String("downloads"))) { location = QStandardPaths::DownloadLocation; } else if (matches(type, QLatin1String("pictures"))) { location = QStandardPaths::PicturesLocation; } else if (matches(type, QLatin1String("config"))) { location = QStandardPaths::GenericConfigLocation; } if (!path.isEmpty()) { QString loc = QStandardPaths::writableLocation(location); loc.append(QDir::separator()); loc.append(path); return loc; } const QStringList &locations = QStandardPaths::standardLocations(location); return locations.count() ? locations.first() : QString(); } QJSValue ScriptEngine::V1::knownWallpaperPlugins(const QString &formFactor) const { QString constraint; if (!formFactor.isEmpty()) { constraint.append("[X-Plasma-FormFactors] ~~ '") .append(formFactor) .append("'"); } const QList wallpapers = KPackage::PackageLoader::self()->listPackages( QStringLiteral("Plasma/Wallpaper"), QString()); QJSValue rv = m_engine->newArray(wallpapers.size()); for (auto wp : wallpapers) { rv.setProperty(wp.name(), m_engine->newArray(0)); } return rv; } QJSValue ScriptEngine::V1::configFile(const QJSValue &config, const QString &group) { ConfigGroup *file = nullptr; if (!config.isUndefined()) { if (config.isString()) { file = new ConfigGroup; const Plasma::Corona *corona = m_engine->corona(); const QString &fileName = config.toString(); if (fileName == corona->config()->name()) { file->setConfig(corona->config()); } else { file->setFile(fileName); } if (!group.isEmpty()) { file->setGroup(group); } } else if (ConfigGroup *parent = qobject_cast( config.toQObject())) { file = new ConfigGroup(parent); if (!group.isEmpty()) { file->setGroup(group); } } } else { file = new ConfigGroup; } QJSValue v = m_engine->newQObject(file); return v; } void ScriptEngine::V1::setImmutability(const QString &immutability) { if (immutability.isEmpty()) { return; } if (immutability == QLatin1String("systemImmutable")) { m_engine->corona()->setImmutability(Plasma::Types::SystemImmutable); } else if (immutability == QLatin1String("userImmutable")) { m_engine->corona()->setImmutability(Plasma::Types::UserImmutable); } else { m_engine->corona()->setImmutability(Plasma::Types::Mutable); } return; } QString ScriptEngine::V1::immutability() const { switch (m_engine->corona()->immutability()) { case Plasma::Types::SystemImmutable: return QLatin1String("systemImmutable"); case Plasma::Types::UserImmutable: return QLatin1String("userImmutable"); default: return QLatin1String("mutable"); } } QJSValue ScriptEngine::V1::createContainment(const QString &type, const QString &defaultPlugin, const QString &plugin) { const QString actualPlugin = plugin.isEmpty() ? defaultPlugin : plugin; auto result = m_engine->createContainmentWrapper(type, actualPlugin); if (!result) { - return m_engine->evaluate(QString("new Error('%1');").arg(i18n("Could not find a plugin for %1 named %2.", type, actualPlugin))); + return m_engine->newError(i18n("Could not find a plugin for %1 named %2.", type, actualPlugin)); } return m_engine->newQObject(result); } } // namespace WorkspaceScripting