diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index edda0f15e..392042e11 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,78 +1,77 @@ find_package(Qt5Test ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE) set_package_properties(Qt5Test PROPERTIES PURPOSE "Required for tests") set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) include(ECMMarkAsTest) include(ECMAddTests) find_package(KF5CoreAddons REQUIRED) find_package(KF5XmlGui REQUIRED) find_package(Qt5Widgets REQUIRED) # add_definitions( -DKDESRCDIR=${CMAKE_CURRENT_SOURCE_DIR} ) if(KDE_PLATFORM_FEATURE_BINARY_COMPATIBLE_FEATURE_REDUCTION) set(PLASMA_NO_KIO TRUE) set(PLASMA_NO_PACKAGEKIT TRUE) set(PLASMA_NO_PACKAGE_EXTRADATA TRUE) endif() MACRO(PLASMA_UNIT_TESTS) FOREACH(_testname ${ARGN}) set(libs Qt5::Qml Qt5::Test KF5::Plasma KF5::PlasmaQuick KF5::Archive KF5::CoreAddons KF5::ConfigGui KF5::I18n KF5::KIOCore KF5::Service KF5::IconThemes KF5::Declarative) if(QT_QTOPENGL_FOUND) list(APPEND libs Qt5::OpenGL) endif() ecm_add_test(${_testname}.cpp LINK_LIBRARIES ${libs} NAME_PREFIX "plasma-") target_include_directories(${_testname} PRIVATE "$>;") ENDFOREACH(_testname) ENDMACRO(PLASMA_UNIT_TESTS) PLASMA_UNIT_TESTS( dialogqmltest dialogstatetest fallbackpackagetest packagestructuretest - packageurlinterceptortest pluginloadertest framesvgtest iconitemtest themetest configmodeltest # plasmoidpackagetest ) set(storagetest_libs Qt5::Gui Qt5::Test Qt5::Sql KF5::KIOCore KF5::Plasma KF5::CoreAddons) if(QT_QTOPENGL_FOUND) list(APPEND storagetest_libs Qt5::OpenGL) endif() set(storagetest_srcs storagetest.cpp ../src/plasma/private/storage.cpp ../src/plasma/private/storagethread.cpp ../src/plasma/debug_p.cpp) ecm_add_test(${storagetest_srcs} TEST_NAME plasma-storagetest LINK_LIBRARIES ${storagetest_libs}) if(HAVE_X11) set(dialognativetest_srcs dialognativetest.cpp) ecm_add_test(${dialognativetest_srcs} TEST_NAME dialognativetest LINK_LIBRARIES Qt5::Gui Qt5::Test Qt5::Qml Qt5::Quick KF5::WindowSystem KF5::Plasma KF5::PlasmaQuick) endif() set(coronatest_srcs coronatest.cpp) qt5_add_resources(coronatest_srcs coronatestresources.qrc) ecm_add_test(${coronatest_srcs} TEST_NAME coronatest LINK_LIBRARIES Qt5::Gui Qt5::Widgets Qt5::Test KF5::KIOCore KF5::Plasma KF5::CoreAddons KF5::XmlGui) set(sortfiltermodeltest_srcs sortfiltermodeltest.cpp ../src/declarativeimports/core/datamodel.cpp ../src/declarativeimports/core/datasource.cpp ) ecm_add_test(${sortfiltermodeltest_srcs} TEST_NAME plasma-sortfiltermodeltest LINK_LIBRARIES KF5::Plasma Qt5::Gui Qt5::Test KF5::I18n KF5::Service Qt5::Qml) #Add a test that i18n is not used directly in any import. # It should /always/ be i18nd find_program(SH bash) add_test(i18ndcheck ${SH} ${CMAKE_CURRENT_SOURCE_DIR}/i18ndcheck.sh ${CMAKE_SOURCE_DIR}/src/declarativeimports) diff --git a/autotests/packageurlinterceptortest.cpp b/autotests/packageurlinterceptortest.cpp deleted file mode 100644 index 9fb769479..000000000 --- a/autotests/packageurlinterceptortest.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/****************************************************************************** -* Copyright 2013 Sebastian Kügler * -* * -* This library 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 of the License, or (at your option) any later version. * -* * -* This library 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 * -* Library General Public License for more details. * -* * -* You should have received a copy of the GNU Library General Public License * -* along with this library; see the file COPYING.LIB. If not, write to * -* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * -* Boston, MA 02110-1301, USA. * -*******************************************************************************/ - -#include "packageurlinterceptortest.h" - -#include - -#include -#include - -QTEST_MAIN(PackageUrlInterceptorTest) - -void PackageUrlInterceptorTest::loadAccessManager() -{ - const Plasma::Package &pkg = Plasma::Package(); - //TODO: tests on path resolution -} - -#include "moc_packageurlinterceptortest.cpp" - diff --git a/autotests/packageurlinterceptortest.h b/autotests/packageurlinterceptortest.h deleted file mode 100644 index 303554881..000000000 --- a/autotests/packageurlinterceptortest.h +++ /dev/null @@ -1,35 +0,0 @@ -/****************************************************************************** -* Copyright 2013 Sebastian Kügler * -* * -* This library 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 of the License, or (at your option) any later version. * -* * -* This library 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 * -* Library General Public License for more details. * -* * -* You should have received a copy of the GNU Library General Public License * -* along with this library; see the file COPYING.LIB. If not, write to * -* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * -* Boston, MA 02110-1301, USA. * -*******************************************************************************/ - -#ifndef PACKAGEURLINTERCEPTORTEST_H -#define PACKAGEURLINTERCEPTORTEST_H - -#include - -class PackageUrlInterceptorTest : public QObject -{ - Q_OBJECT -public: - PackageUrlInterceptorTest() {} - -private Q_SLOTS: - void loadAccessManager(); -}; - -#endif diff --git a/src/plasma/package.cpp b/src/plasma/package.cpp index d23267769..c8b507a94 100644 --- a/src/plasma/package.cpp +++ b/src/plasma/package.cpp @@ -1,310 +1,317 @@ /****************************************************************************** * Copyright 2007 by Aaron Seigo * * Copyright 2010 by Marco Martin * * Copyright 2010 by Kevin Ottens * * Copyright 2009 by Rob Scheepmaker * * * * This library 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 of the License, or (at your option) any later version. * * * * This library 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #include "package.h" #include #include #include #include #include #include #include #include "config-plasma.h" #include #include #include "packagestructure.h" #include "pluginloader.h" #include "private/package_p.h" #include "private/packagestructure_p.h" namespace Plasma { PackagePrivate::PackagePrivate() : internalPackage(0), fallbackPackage(0), structure(0) { } PackagePrivate::~PackagePrivate() { } Package::Package(PackageStructure *structure) : d(new Plasma::PackagePrivate()) { d->structure = structure; if (!structure) { d->internalPackage = new KPackage::Package(); return; } if (!structure->d->internalStructure) { d->structure->d->internalStructure = new KPackage::PackageStructure; } d->internalPackage = new KPackage::Package(structure->d->internalStructure); PackageStructureWrapper::s_packagesMap[d->internalPackage] = this; structure->initPackage(this); } Package::Package(const Package &other) : d(new Plasma::PackagePrivate()) { d->internalPackage = new KPackage::Package(*other.d->internalPackage); d->structure = other.d->structure; PackageStructureWrapper::s_packagesMap[d->internalPackage] = this; } +Package::Package(const KPackage::Package &other) + : d(new Plasma::PackagePrivate()) +{ + d->internalPackage = new KPackage::Package(other); + PackageStructureWrapper::s_packagesMap[d->internalPackage] = this; +} + Package::~Package() { PackageStructureWrapper::s_packagesMap.remove(d->internalPackage); delete d->internalPackage; } Package &Package::operator=(const Package &rhs) { if (&rhs != this) { d->internalPackage = new KPackage::Package(*rhs.d->internalPackage); d->structure = rhs.d->structure; PackageStructureWrapper::s_packagesMap[d->internalPackage] = this; } return *this; } bool Package::hasValidStructure() const { return d->internalPackage->hasValidStructure(); } bool Package::isValid() const { return d->internalPackage->isValid(); } QString Package::name(const char *key) const { return d->internalPackage->name(key); } bool Package::isRequired(const char *key) const { return d->internalPackage->isRequired(key); } QStringList Package::mimeTypes(const char *key) const { return d->internalPackage->mimeTypes(key); } QString Package::defaultPackageRoot() const { return d->internalPackage->defaultPackageRoot(); } void Package::setDefaultPackageRoot(const QString &packageRoot) { d->internalPackage->setDefaultPackageRoot(packageRoot); } void Package::setFallbackPackage(const Plasma::Package &package) { d->fallbackPackage = new Package(package); d->internalPackage->setFallbackPackage(*package.d->internalPackage); } Plasma::Package Package::fallbackPackage() const { if (d->fallbackPackage) { return (*d->fallbackPackage); } else { return Package(); } } QString Package::servicePrefix() const { return d->servicePrefix; } void Package::setServicePrefix(const QString &servicePrefix) { d->servicePrefix = servicePrefix; } KPackage::Package Package::kPackage() const { return *d->internalPackage; } bool Package::allowExternalPaths() const { return d->internalPackage->allowExternalPaths(); } void Package::setAllowExternalPaths(bool allow) { d->internalPackage->setAllowExternalPaths(allow); } KPluginInfo Package::metadata() const { return KPluginInfo::fromMetaData(d->internalPackage->metadata()); } QString Package::filePath(const char *fileType, const QString &filename) const { return d->internalPackage->filePath(fileType, filename); } QStringList Package::entryList(const char *key) const { return d->internalPackage->entryList(key); } void Package::setPath(const QString &path) { if (path == d->internalPackage->path()) { return; } d->internalPackage->setPath(path); } const QString Package::path() const { return d->internalPackage->path(); } QStringList Package::contentsPrefixPaths() const { return d->internalPackage->contentsPrefixPaths(); } void Package::setContentsPrefixPaths(const QStringList &prefixPaths) { d->internalPackage->setContentsPrefixPaths(prefixPaths); } QString Package::contentsHash() const { return d->internalPackage->contentsHash(); } void Package::addDirectoryDefinition(const char *key, const QString &path, const QString &name) { d->internalPackage->addDirectoryDefinition(key, path, name); } void Package::addFileDefinition(const char *key, const QString &path, const QString &name) { d->internalPackage->addFileDefinition(key, path, name); } void Package::removeDefinition(const char *key) { d->internalPackage->removeDefinition(key); } void Package::setRequired(const char *key, bool required) { d->internalPackage->setRequired(key, required); } void Package::setDefaultMimeTypes(QStringList mimeTypes) { d->internalPackage->setDefaultMimeTypes(mimeTypes); } void Package::setMimeTypes(const char *key, QStringList mimeTypes) { d->internalPackage->setMimeTypes(key, mimeTypes); } QList Package::directories() const { QList dirs; foreach (const auto &data, d->internalPackage->directories()) { dirs << data.constData(); } return dirs; } QList Package::requiredDirectories() const { QList dirs; foreach (const auto &data, d->internalPackage->requiredDirectories()) { dirs << data.constData(); } return dirs; } QList Package::files() const { QList files; foreach (const auto &data, d->internalPackage->files()) { files << data.constData(); } return files; } QList Package::requiredFiles() const { QList files; foreach (const auto &data, d->internalPackage->requiredFiles()) { files << data.constData(); } return files; } KJob *Package::install(const QString &sourcePackage, const QString &packageRoot) { const QString src = sourcePackage; const QString dest = packageRoot.isEmpty() ? defaultPackageRoot() : packageRoot; //qCDebug(LOG_PLASMA) << "Source: " << src; //qCDebug(LOG_PLASMA) << "PackageRoot: " << dest; KJob *j = d->structure->install(this, src, dest); return j; } KJob *Package::uninstall(const QString &packageName, const QString &packageRoot) { setPath(packageRoot + packageName); return d->structure->uninstall(this, packageRoot); } } // Namespace diff --git a/src/plasma/package.h b/src/plasma/package.h index ae47dd7ba..1748adb8b 100644 --- a/src/plasma/package.h +++ b/src/plasma/package.h @@ -1,370 +1,376 @@ /****************************************************************************** * Copyright 2007-2011 by Aaron Seigo * * * * This library 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 of the License, or (at your option) any later version. * * * * This library 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #ifndef PLASMA_PACKAGE_H #define PLASMA_PACKAGE_H #include #include #include #include class KJob; #ifndef PLASMA_NO_DEPRECATED namespace KPackage { class Package; } namespace Plasma { /** * @class Package plasma/package.h * * @short object representing an installed Plasma package * * @deprecated USe KPackage::Package instead * Package defines what is in a package and provides easy access to the contents. * * To define a package, one might write the following code: * @code Package package; package.addDirectoryDefinition("images", "pics/", i18n("Images")); QStringList mimeTypes; mimeTypes << "image/svg" << "image/png" << "image/jpeg"; package.setMimeTypes("images", mimeTypes); package.addDirectoryDefinition("scripts", "code/", i18n("Executable Scripts")); mimeTypes.clear(); mimeTypes << "text/\*"; package.setMimeTypes("scripts", mimeTypes); package.addFileDefinition("mainscript", "code/main.js", i18n("Main Script File")); package.setRequired("mainscript", true); @endcode * One may also choose to create a subclass of PackageStructure and include the setup * in the constructor. * * Either way, Package creates a self-documenting contract between the packager and * the application without exposing package internals such as actual on-disk structure * of the package or requiring that all contents be explicitly known ahead of time. * * Subclassing PackageStructure does have provide a number of potential const benefits: * * the package can be notified of path changes via the virtual pathChanged() method * * the subclass may implement mechanisms to install and remove packages using the * virtual install and uninstall methods * * subclasses can be compiled as plugins for easy re-use **/ //TODO: write documentation on USING a package class PackagePrivate; class PackageStructure; class PLASMA_EXPORT Package { public: /** * Default constructor * * @arg structure if a NULL pointer is passed in, this will creates an empty (invalid) Package; * otherwise the structure is allowed to set up the Package's initial layout * @since 4.6 */ PLASMA_DEPRECATED explicit Package(PackageStructure *structure = 0); + /** + * Copy constructore + * @since 4.6 + */ + PLASMA_DEPRECATED Package(const KPackage::Package &other); + /** * Copy constructore * @since 4.6 */ PLASMA_DEPRECATED Package(const Package &other); ~Package(); /** * Assignment operator * @since 4.6 */ PLASMA_DEPRECATED Package &operator=(const Package &rhs); /** * @return true if this package has a valid PackageStructure associatedw it with it. * A package may not be valid, but have a valid structure. Useful when dealing with * Package objects in a semi-initialized state (e.g. before calling setPath()) * @since 5.1 */ PLASMA_DEPRECATED bool hasValidStructure() const; /** * @return true if all the required components exist **/ PLASMA_DEPRECATED bool isValid() const; /** * Sets the path to the root of this package * @param path an absolute path, or a relative path to the default package root * @since 4.3 */ PLASMA_DEPRECATED void setPath(const QString &path); /** * @return the path to the root of this particular package */ PLASMA_DEPRECATED const QString path() const; /** * Get the path to a given file based on the key and an optional filename. * Example: finding the main script in a scripting package: * filePath("mainscript") * * Example: finding a specific image in the images directory: * filePath("images", "myimage.png") * * @param key the key of the file type to look for, * @param filename optional name of the file to locate within the package * @return path to the file on disk. QString() if not found. **/ PLASMA_DEPRECATED QString filePath(const char *key, const QString &filename = QString()) const; /** * Get the list of files of a given type. * * @param fileType the type of file to look for, as defined in the * package structure. * @return list of files by name, suitable for passing to filePath **/ PLASMA_DEPRECATED QStringList entryList(const char *key) const; /** * @return user visible name for the given entry **/ PLASMA_DEPRECATED QString name(const char *key) const; /** * @return true if the item at path exists and is required **/ PLASMA_DEPRECATED bool isRequired(const char *key) const; /** * @return the mimeTypes associated with the path, if any **/ PLASMA_DEPRECATED QStringList mimeTypes(const char *key) const; /** * @return the prefix paths inserted between the base path and content entries, in order of priority. * When searching for a file, all paths will be tried in order. * @since 4.6 */ PLASMA_DEPRECATED QStringList contentsPrefixPaths() const; /** * @return preferred package root. This defaults to plasma/plasmoids/ */ PLASMA_DEPRECATED QString defaultPackageRoot() const; /** * @return service prefix used in desktop files. This defaults to plasma-applet- */ PLASMA_DEPRECATED QString servicePrefix() const; /** * @return true if paths/symlinks outside the package itself should be followed. * By default this is set to false for security reasons. */ PLASMA_DEPRECATED bool allowExternalPaths() const; /** * @return the package metadata object. */ PLASMA_DEPRECATED KPluginInfo metadata() const; /** * @return a SHA1 hash digest of the contents of the package in hexadecimal form * @since 4.4 */ PLASMA_DEPRECATED QString contentsHash() const; /** * Adds a directory to the structure of the package. It is added as * a not-required element with no associated mimeTypes. * * Starting in 4.6, if an entry with the given key * already exists, the path is added to it as a search alternative. * * @param key used as an internal label for this directory * @param path the path within the package for this directory * @param name the user visible (translated) name for the directory **/ PLASMA_DEPRECATED void addDirectoryDefinition(const char *key, const QString &path, const QString &name); /** * Adds a file to the structure of the package. It is added as * a not-required element with no associated mimeTypes. * * Starting in 4.6, if an entry with the given key * already exists, the path is added to it as a search alternative. * * @param key used as an internal label for this file * @param path the path within the package for this file * @param name the user visible (translated) name for the file **/ PLASMA_DEPRECATED void addFileDefinition(const char *key, const QString &path, const QString &name); /** * Removes a definition from the structure of the package. * @since 4.6 * @param key the internal label of the file or directory to remove */ PLASMA_DEPRECATED void removeDefinition(const char *key); /** * Sets whether or not a given part of the structure is required or not. * The path must already have been added using addDirectoryDefinition * or addFileDefinition. * * @param key the entry within the package * @param required true if this entry is required, false if not */ PLASMA_DEPRECATED void setRequired(const char *key, bool required); /** * Defines the default mimeTypes for any definitions that do not have * associated mimeTypes. Handy for packages with only one or predominantly * one file type. * * @param mimeTypes a list of mimeTypes **/ PLASMA_DEPRECATED void setDefaultMimeTypes(QStringList mimeTypes); /** * Define mimeTypes for a given part of the structure * The path must already have been added using addDirectoryDefinition * or addFileDefinition. * * @param key the entry within the package * @param mimeTypes a list of mimeTypes **/ PLASMA_DEPRECATED void setMimeTypes(const char *key, QStringList mimeTypes); /** * Sets the prefixes that all the contents in this package should * appear under. This defaults to "contents/" and is added automatically * between the base path and the entries as defined by the package * structure. Multiple entries can be added. * In this case each file request will be searched in all prefixes in order, * and the first found will be returned. * * @param prefix paths the directory prefix to use * @since 4.6 */ PLASMA_DEPRECATED void setContentsPrefixPaths(const QStringList &prefixPaths); /** * Sets service prefix. */ PLASMA_DEPRECATED void setServicePrefix(const QString &servicePrefix); /** * Sets whether or not external paths/symlinks can be followed by a package * @param allow true if paths/symlinks outside of the package should be followed, * false if they should be rejected. */ PLASMA_DEPRECATED void setAllowExternalPaths(bool allow); /** * Sets preferred package root. */ PLASMA_DEPRECATED void setDefaultPackageRoot(const QString &packageRoot); /** * Sets the fallback package root path * If a file won't be found in this package, it will search it in the package * with the same structure identified by path * It is intended to be used by the packageStructure * @param path package root path @see setPath */ PLASMA_DEPRECATED void setFallbackPackage(const Plasma::Package &package); /** * @return The fallback package root path */ PLASMA_DEPRECATED Plasma::Package fallbackPackage() const; // Content structure description methods /** * @return all directories registered as part of this Package's structure */ PLASMA_DEPRECATED QList directories() const; /** * @return all directories registered as part of this Package's required structure */ PLASMA_DEPRECATED QList requiredDirectories() const; /** * @return all files registered as part of this Package's structure */ PLASMA_DEPRECATED QList files() const; /** * @return all files registered as part of this Package's required structure */ PLASMA_DEPRECATED QList requiredFiles() const; /** * Installs a package matching this package structure. By default installs a * native Plasma::Package. * * @return KJob to track installation progress and result **/ PLASMA_DEPRECATED KJob *install(const QString &sourcePackage, const QString &packageRoot = QString()); /** * Uninstalls a package matching this package structure. * * @return KJob to track removal progress and result */ PLASMA_DEPRECATED KJob *uninstall(const QString &packageName, const QString &packageRoot); /** * @returns the wrapped KPackage::Package instance, which deprecated this class * * @since 5.28 */ KPackage::Package kPackage() const; private: QExplicitlySharedDataPointer d; friend class PackagePrivate; friend class PackageStructure; friend class AppletPrivate; friend class Applet; friend class Corona; }; } #endif Q_DECLARE_METATYPE(Plasma::Package) #endif diff --git a/src/plasma/pluginloader.cpp b/src/plasma/pluginloader.cpp index 49df4d685..6f8b13ebe 100644 --- a/src/plasma/pluginloader.cpp +++ b/src/plasma/pluginloader.cpp @@ -1,869 +1,879 @@ /* * Copyright 2010 Ryan Rix * * 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 "pluginloader.h" #include #include #include #include #include #include #include #include "config-plasma.h" #if !PLASMA_NO_KIO #include #endif #include "applet.h" #include "containment.h" #include "containmentactions.h" #include "dataengine.h" #include "package.h" #include "private/applet_p.h" #include "private/service_p.h" // for NullService #include "private/storage_p.h" #include "private/package_p.h" #include "private/packagestructure_p.h" #include #include "debug_p.h" namespace Plasma { static PluginLoader *s_pluginLoader = 0; class PluginLoaderPrivate { public: PluginLoaderPrivate() : isDefaultLoader(false) { } static QSet knownCategories(); static QString parentAppConstraint(const QString &parentApp = QString()); static QSet s_customCategories; QHash > structures; bool isDefaultLoader; static QString s_dataEnginePluginDir; static QString s_packageStructurePluginDir; static QString s_plasmoidsPluginDir; static QString s_servicesPluginDir; static QString s_containmentActionsPluginDir; }; QSet PluginLoaderPrivate::s_customCategories; QString PluginLoaderPrivate::s_dataEnginePluginDir = QStringLiteral("plasma/dataengine"); QString PluginLoaderPrivate::s_packageStructurePluginDir = QStringLiteral("plasma/packagestructure"); QString PluginLoaderPrivate::s_plasmoidsPluginDir = QStringLiteral("plasma/applets"); QString PluginLoaderPrivate::s_servicesPluginDir = QStringLiteral("plasma/services"); QString PluginLoaderPrivate::s_containmentActionsPluginDir = QStringLiteral("plasma/containmentactions"); QSet PluginLoaderPrivate::knownCategories() { // this is to trick the tranlsation tools into making the correct // strings for translation QSet categories = s_customCategories; categories << QStringLiteral(I18N_NOOP("Accessibility")).toLower() << QStringLiteral(I18N_NOOP("Application Launchers")).toLower() << QStringLiteral(I18N_NOOP("Astronomy")).toLower() << QStringLiteral(I18N_NOOP("Date and Time")).toLower() << QStringLiteral(I18N_NOOP("Development Tools")).toLower() << QStringLiteral(I18N_NOOP("Education")).toLower() << QStringLiteral(I18N_NOOP("Environment and Weather")).toLower() << QStringLiteral(I18N_NOOP("Examples")).toLower() << QStringLiteral(I18N_NOOP("File System")).toLower() << QStringLiteral(I18N_NOOP("Fun and Games")).toLower() << QStringLiteral(I18N_NOOP("Graphics")).toLower() << QStringLiteral(I18N_NOOP("Language")).toLower() << QStringLiteral(I18N_NOOP("Mapping")).toLower() << QStringLiteral(I18N_NOOP("Miscellaneous")).toLower() << QStringLiteral(I18N_NOOP("Multimedia")).toLower() << QStringLiteral(I18N_NOOP("Online Services")).toLower() << QStringLiteral(I18N_NOOP("Productivity")).toLower() << QStringLiteral(I18N_NOOP("System Information")).toLower() << QStringLiteral(I18N_NOOP("Utilities")).toLower() << QStringLiteral(I18N_NOOP("Windows and Tasks")).toLower() << QStringLiteral(I18N_NOOP("Clipboard")).toLower() << QStringLiteral(I18N_NOOP("Tasks")).toLower(); return categories; } QString PluginLoaderPrivate::parentAppConstraint(const QString &parentApp) { if (parentApp.isEmpty()) { QCoreApplication *app = QCoreApplication::instance(); if (!app) { return QString(); } return QStringLiteral("((not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '') or [X-KDE-ParentApp] == '%1')") .arg(app->applicationName()); } return QStringLiteral("[X-KDE-ParentApp] == '%1'").arg(parentApp); } PluginLoader::PluginLoader() : d(new PluginLoaderPrivate) { } PluginLoader::~PluginLoader() { typedef QWeakPointer pswp; foreach (pswp wp, d->structures) { delete wp.data(); } delete d; } void PluginLoader::setPluginLoader(PluginLoader *loader) { if (!s_pluginLoader) { s_pluginLoader = loader; } else { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Cannot set pluginLoader, already set!" << s_pluginLoader; #endif } } PluginLoader *PluginLoader::self() { if (!s_pluginLoader) { // we have been called before any PluginLoader was set, so just use the default // implementation. this prevents plugins from nefariously injecting their own // plugin loader if the app doesn't s_pluginLoader = new PluginLoader; s_pluginLoader->d->isDefaultLoader = true; } return s_pluginLoader; } Applet *PluginLoader::loadApplet(const QString &name, uint appletId, const QVariantList &args) { if (name.isEmpty()) { return 0; } Applet *applet = d->isDefaultLoader ? 0 : internalLoadApplet(name, appletId, args); if (applet) { return applet; } if (appletId == 0) { appletId = ++AppletPrivate::s_maxAppletId; } //if name wasn't a path, pluginName == name const QString pluginName = name.split('/').last(); // Look for C++ plugins first auto filter = [&pluginName](const KPluginMetaData &md) -> bool { return md.pluginId() == pluginName; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_plasmoidsPluginDir, filter); if (plugins.isEmpty()) { // COMPAT CODE for applets installed into the toplevel plugins dir by mistake. plugins = KPluginLoader::findPlugins(QString(), filter); } if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); if (!isPluginVersionCompatible(loader)) { return 0; } KPluginFactory *factory = loader.factory(); if (factory) { QVariantList allArgs; allArgs << loader.metaData().toVariantMap() << appletId << args; applet = factory->create(0, allArgs); } } if (applet) { return applet; } const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), name); if (!applet) { //qCDebug(LOG_PLASMA) << name << "not a C++ applet: Falling back to an empty one"; QVariantList allArgs; allArgs << p.metadata().fileName() << appletId << args; if (p.metadata().serviceTypes().contains(QStringLiteral("Plasma/Containment"))) { return new Containment(0, allArgs); } else { return new Applet(0, allArgs); } } return applet; } DataEngine *PluginLoader::loadDataEngine(const QString &name) { DataEngine *engine = d->isDefaultLoader ? 0 : internalLoadDataEngine(name); if (engine) { return engine; } // Look for C++ plugins first auto filter = [&name](const KPluginMetaData &md) -> bool { return md.pluginId() == name; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filter); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap(); KPluginFactory *factory = loader.factory(); if (factory) { engine = factory->create(0, argsWithMetaData); } } if (engine) { return engine; } const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/DataEngine"), name); if (!p.isValid()) { return 0; } return new DataEngine(KPluginInfo(p.metadata().fileName()), 0); } QStringList PluginLoader::listAllEngines(const QString &parentApp) { QStringList engines; // Look for C++ plugins first auto filter = [&parentApp](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-KDE-ParentApp")) == parentApp; }; QVector plugins; if (parentApp.isEmpty()) { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir); } else { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filter); } foreach (auto& plugin, plugins) { engines << plugin.pluginId(); } const QList packagePlugins = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/DataEngine")); for (auto& plugin : packagePlugins) { engines << plugin.pluginId(); } return engines; } KPluginInfo::List PluginLoader::listEngineInfo(const QString &parentApp) { return PluginLoader::self()->listDataEngineInfo(parentApp); } KPluginInfo::List PluginLoader::listEngineInfoByCategory(const QString &category, const QString &parentApp) { KPluginInfo::List list; // Look for C++ plugins first auto filterNormal = [&category](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-KDE-PluginInfo-Category")) == category; }; auto filterParentApp = [&category, &parentApp](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-KDE-ParentApp")) == parentApp && md.value(QStringLiteral("X-KDE-PluginInfo-Category")) == category; }; QVector plugins; if (parentApp.isEmpty()) { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filterNormal); } else { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filterParentApp); } list = KPluginInfo::fromMetaData(plugins); //TODO FIXME: PackageLoader needs to have a function to inject packageStructures const QList packagePlugins = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/DataEngine")); list << KPluginInfo::fromMetaData(packagePlugins.toVector()); return list; } Service *PluginLoader::loadService(const QString &name, const QVariantList &args, QObject *parent) { Service *service = d->isDefaultLoader ? 0 : internalLoadService(name, args, parent); if (service) { return service; } //TODO: scripting API support if (name.isEmpty()) { return new NullService(QString(), parent); } else if (name == QLatin1String("org.kde.servicestorage")) { return new Storage(parent); } // Look for C++ plugins first auto filter = [&name](const KPluginMetaData &md) -> bool { return md.pluginId() == name; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_servicesPluginDir, filter); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); if (!isPluginVersionCompatible(loader)) { return 0; } KPluginFactory *factory = loader.factory(); if (factory) { service = factory->create(0, args); } } if (service) { if (service->name().isEmpty()) { service->setName(name); } return service; } else { return new NullService(name, parent); } } ContainmentActions *PluginLoader::loadContainmentActions(Containment *parent, const QString &name, const QVariantList &args) { if (name.isEmpty()) { return 0; } ContainmentActions *actions = d->isDefaultLoader ? 0 : internalLoadContainmentActions(parent, name, args); if (actions) { return actions; } // Look for C++ plugins first auto filter = [&name](const KPluginMetaData &md) -> bool { return md.pluginId() == name; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_containmentActionsPluginDir, filter); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap(); KPluginFactory *factory = loader.factory(); if (factory) { actions = factory->create(0, argsWithMetaData); } } if (actions) { return actions; } //FIXME: this is only for backwards compatibility, but probably will have to stay //for the time being QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(name); KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("Plasma/ContainmentActions"), constraint); if (offers.isEmpty()) { #ifndef NDEBUG qCDebug(LOG_PLASMA) << "offers is empty for " << name; #endif return 0; } KService::Ptr offer = offers.first(); KPluginLoader plugin(*offer); if (!isPluginVersionCompatible(plugin)) { return 0; } QVariantList allArgs; allArgs << offer->storageId() << args; QString error; actions = offer->createInstance(parent, allArgs, &error); if (!actions) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Couldn't load containmentActions \"" << name << "\"! reason given: " << error; #endif } return actions; } #ifndef PLASMA_NO_DEPRECATED Package PluginLoader::loadPackage(const QString &packageFormat, const QString &specialization) { if (!d->isDefaultLoader) { Package p = internalLoadPackage(packageFormat, specialization); if (p.hasValidStructure()) { return p; } } if (packageFormat.isEmpty()) { return Package(); } const QString hashkey = packageFormat + '%' + specialization; PackageStructure *structure = d->structures.value(hashkey).data(); if (structure) { return Package(structure); } KPackage::PackageStructure *internalStructure = KPackage::PackageLoader::self()->loadPackageStructure(packageFormat); if (internalStructure) { structure = new PackageStructure(); structure->d->internalStructure = internalStructure; //fallback to old structures } else { const QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(packageFormat); structure = KPluginTrader::createInstanceFromQuery(PluginLoaderPrivate::s_packageStructurePluginDir, QStringLiteral("Plasma/PackageStructure"), constraint, 0); if (structure) { structure->d->internalStructure = new PackageStructureWrapper(structure); } } if (structure) { d->structures.insert(hashkey, structure); return Package(structure); } #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Couldn't load Package for" << packageFormat << "! reason given: " << error; #endif return Package(); } #endif QList PluginLoader::listAppletMetaData(const QString &category, const QString &parentApp) { //FIXME: this assumes we are always use packages.. no pure c++ std::function filter; if (category.isEmpty()) { //use all but the excluded categories KConfigGroup group(KSharedConfig::openConfig(), "General"); QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); filter = [excluded, parentApp](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); return (pa.isEmpty() || pa == parentApp) && !excluded.contains(md.category()); }; } else { //specific category (this could be an excluded one - is that bad?) filter = [category, parentApp](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); if (category == QLatin1String("Miscellaneous")) { return (pa.isEmpty() || pa == parentApp) && (md.category() == category || md.category().isEmpty()); } else { return (pa.isEmpty() || pa == parentApp) && md.category() == category; } }; } QList list; if (!d->isDefaultLoader && (parentApp.isEmpty() || parentApp == QCoreApplication::instance()->applicationName())) { list = KPluginInfo::toMetaData(internalAppletInfo(category)).toList(); } return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); } KPluginInfo::List PluginLoader::listAppletInfo(const QString &category, const QString &parentApp) { KPluginInfo::List list; const auto plugins = listAppletMetaData(category, parentApp); //NOTE: it still produces kplugininfos from KServices because some user code expects //info.sevice() to be valid and would crash ohtherwise foreach (auto& md, plugins) { auto pi = md.metaDataFileName().endsWith(".json") ? KPluginInfo(md) : KPluginInfo(KService::serviceByStorageId(md.metaDataFileName())); if (!pi.isValid()) { qCWarning(LOG_PLASMA) << "Could not load plugin info for plugin :" << md.pluginId() << "skipping plugin"; continue; } list << pi; } return list; } -KPluginInfo::List PluginLoader::listAppletInfoForMimeType(const QString &mimeType) +QList PluginLoader::listAppletMetaDataForMimeType(const QString &mimeType) { auto filter = [&mimeType](const KPluginMetaData &md) -> bool { return KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimeType); }; - return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector()); + return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); } -KPluginInfo::List PluginLoader::listAppletInfoForUrl(const QUrl &url) +KPluginInfo::List PluginLoader::listAppletInfoForMimeType(const QString &mimeType) +{ + return KPluginInfo::fromMetaData(listAppletMetaDataForMimeType(mimeType).toVector()); +} + +QList PluginLoader::listAppletMetaDataForUrl(const QUrl &url) { QString parentApp; QCoreApplication *app = QCoreApplication::instance(); if (app) { parentApp = app->applicationName(); } auto filter = [&parentApp](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); return (pa.isEmpty() || pa == parentApp) && !KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropUrlPatterns")).isEmpty(); }; - QList allApplets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); + const QList allApplets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); - KPluginInfo::List filtered; + QList filtered; foreach (const KPluginMetaData &md, allApplets) { QStringList urlPatterns = KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropUrlPatterns")); foreach (const QString &glob, urlPatterns) { QRegExp rx(glob); rx.setPatternSyntax(QRegExp::Wildcard); if (rx.exactMatch(url.toString())) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << md.name() << "matches" << glob << url; #endif - filtered << KPluginInfo::fromMetaData(md); + filtered << md; } } } return filtered; } +KPluginInfo::List PluginLoader::listAppletInfoForUrl(const QUrl &url) +{ + return KPluginInfo::fromMetaData(listAppletMetaDataForUrl(url).toVector()); +} + QStringList PluginLoader::listAppletCategories(const QString &parentApp, bool visibleOnly) { KConfigGroup group(KSharedConfig::openConfig(), "General"); const QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); auto filter = [&parentApp, &excluded, visibleOnly](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); return (pa.isEmpty() || pa == parentApp) && (excluded.isEmpty() || excluded.contains(md.value(QStringLiteral("X-KDE-PluginInfo-Category")))) && (!visibleOnly || !md.isHidden()); }; const QList allApplets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); QStringList categories; foreach (auto& plugin, allApplets) { if (plugin.category().isEmpty()) { if (!categories.contains(i18nc("misc category", "Miscellaneous"))) { categories << i18nc("misc category", "Miscellaneous"); } } else { categories << plugin.category(); } } categories.sort(); return categories; } void PluginLoader::setCustomAppletCategories(const QStringList &categories) { PluginLoaderPrivate::s_customCategories = QSet::fromList(categories); } QStringList PluginLoader::customAppletCategories() const { return PluginLoaderPrivate::s_customCategories.toList(); } QString PluginLoader::appletCategory(const QString &appletName) { if (appletName.isEmpty()) { return QString(); } const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), appletName); if (!p.isValid()) { return QString(); } return p.metadata().category(); } KPluginInfo::List PluginLoader::listContainments(const QString &category, const QString &parentApp) { return listContainmentsOfType(QString(), category, parentApp); } KPluginInfo::List PluginLoader::listContainmentsOfType(const QString &type, const QString &category, const QString &parentApp) { KConfigGroup group(KSharedConfig::openConfig(), "General"); const QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); auto filter = [&type, &category, &parentApp](const KPluginMetaData &md) -> bool { if (!md.serviceTypes().contains(QStringLiteral("Plasma/Containment"))) { return false; } const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); if (!pa.isEmpty() && pa != parentApp) { return false; } if (!type.isEmpty() && md.value(QStringLiteral("X-Plasma-ContainmentType")) != type) { return false; } if (!category.isEmpty() && md.value(QStringLiteral("X-KDE-PluginInfo-Category")) != category) { return false; } return true; }; return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector()); } KPluginInfo::List PluginLoader::listContainmentsForMimeType(const QString &mimeType) { auto filter = [&mimeType](const KPluginMetaData &md) -> bool { return md.serviceTypes().contains(QLatin1String("Plasma/Containment")) && KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimeType); }; return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector()); } QStringList PluginLoader::listContainmentTypes() { KPluginInfo::List containmentInfos = listContainments(); QSet types; foreach (const KPluginInfo &containmentInfo, containmentInfos) { QStringList theseTypes = containmentInfo.service()->property(QStringLiteral("X-Plasma-ContainmentType")).toStringList(); foreach (const QString &type, theseTypes) { types.insert(type); } } return types.toList(); } KPluginInfo::List PluginLoader::listDataEngineInfo(const QString &parentApp) { KPluginInfo::List list; if (!d->isDefaultLoader && (parentApp.isEmpty() || parentApp == QCoreApplication::instance()->applicationName())) { list = internalDataEngineInfo(); } QString constraint; if (parentApp.isEmpty()) { constraint = QStringLiteral("not exist [X-KDE-ParentApp]"); } else { constraint = QLatin1String("[X-KDE-ParentApp] == '") + parentApp + '\''; } list.append(KPluginTrader::self()->query(PluginLoaderPrivate::s_dataEnginePluginDir, QStringLiteral("Plasma/DataEngine"), constraint)); return list; } KPluginInfo::List PluginLoader::listContainmentActionsInfo(const QString &parentApp) { KPluginInfo::List list; if (!d->isDefaultLoader && (parentApp.isEmpty() || parentApp == QCoreApplication::instance()->applicationName())) { list = internalContainmentActionsInfo(); } QString constraint; if (parentApp.isEmpty()) { constraint = QStringLiteral("not exist [X-KDE-ParentApp]"); } else { constraint = QLatin1String("[X-KDE-ParentApp] == '") + parentApp + '\''; } list.append(KPluginTrader::self()->query(PluginLoaderPrivate::s_containmentActionsPluginDir, QStringLiteral("Plasma/ContainmentActions"), constraint)); QSet knownPlugins; foreach (const KPluginInfo &p, list) { knownPlugins.insert(p.pluginName()); } //FIXME: this is only for backwards compatibility, but probably will have to stay //for the time being KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("Plasma/ContainmentActions"), constraint); foreach (KService::Ptr s, offers) { if (!knownPlugins.contains(s->pluginKeyword())) { list.append(KPluginInfo(s)); } } return list; } Applet *PluginLoader::internalLoadApplet(const QString &name, uint appletId, const QVariantList &args) { Q_UNUSED(name) Q_UNUSED(appletId) Q_UNUSED(args) return 0; } DataEngine *PluginLoader::internalLoadDataEngine(const QString &name) { Q_UNUSED(name) return 0; } ContainmentActions *PluginLoader::internalLoadContainmentActions(Containment *containment, const QString &name, const QVariantList &args) { Q_UNUSED(containment) Q_UNUSED(name) Q_UNUSED(args) return 0; } Service *PluginLoader::internalLoadService(const QString &name, const QVariantList &args, QObject *parent) { Q_UNUSED(name) Q_UNUSED(args) Q_UNUSED(parent) return 0; } #ifndef PLASMA_NO_DEPRECATED Package PluginLoader::internalLoadPackage(const QString &name, const QString &specialization) { Q_UNUSED(name); Q_UNUSED(specialization); return Package(); } #endif KPluginInfo::List PluginLoader::internalAppletInfo(const QString &category) const { Q_UNUSED(category) return KPluginInfo::List(); } KPluginInfo::List PluginLoader::internalDataEngineInfo() const { return KPluginInfo::List(); } KPluginInfo::List PluginLoader::internalServiceInfo() const { return KPluginInfo::List(); } KPluginInfo::List PluginLoader::internalContainmentActionsInfo() const { return KPluginInfo::List(); } static KPluginInfo::List standardInternalInfo(const QString &type, const QString &category = QString()) { QStringList files = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/internal/") + type + QLatin1String("/*.desktop"), QStandardPaths::LocateFile); KPluginInfo::List allInfo = KPluginInfo::fromFiles(files); if (category.isEmpty() || allInfo.isEmpty()) { return allInfo; } KPluginInfo::List matchingInfo; foreach (const KPluginInfo &info, allInfo) { if (info.category().compare(category, Qt::CaseInsensitive) == 0) { matchingInfo << info; } } return matchingInfo; } KPluginInfo::List PluginLoader::standardInternalAppletInfo(const QString &category) const { return standardInternalInfo(QStringLiteral("applets"), category); } KPluginInfo::List PluginLoader::standardInternalDataEngineInfo() const { return standardInternalInfo(QStringLiteral("dataengines")); } KPluginInfo::List PluginLoader::standardInternalServiceInfo() const { return standardInternalInfo(QStringLiteral("services")); } bool PluginLoader::isPluginVersionCompatible(KPluginLoader &loader) { const quint32 version = loader.pluginVersion(); if (version == quint32(-1)) { // unversioned, just let it through qCWarning(LOG_PLASMA) << loader.fileName() << "unversioned plugin detected, may result in instability"; return true; } // we require PLASMA_VERSION_MAJOR and PLASMA_VERSION_MINOR const quint32 minVersion = PLASMA_MAKE_VERSION(PLASMA_VERSION_MAJOR, 0, 0); const quint32 maxVersion = PLASMA_MAKE_VERSION(PLASMA_VERSION_MAJOR, PLASMA_VERSION_MINOR, 60); if (version < minVersion || version > maxVersion) { #ifndef NDEBUG qCDebug(LOG_PLASMA) << loader.fileName() << ": this plugin is compiled against incompatible Plasma version" << version << "This build is compatible with" << PLASMA_VERSION_MAJOR << ".0.0 (" << minVersion << ") to" << PLASMA_VERSION_STRING << "(" << maxVersion << ")"; #endif return false; } return true; } } // Plasma Namespace diff --git a/src/plasma/pluginloader.h b/src/plasma/pluginloader.h index 7678ae4d1..12f7ea8c7 100644 --- a/src/plasma/pluginloader.h +++ b/src/plasma/pluginloader.h @@ -1,514 +1,532 @@ /* * Copyright 2010 by Ryan Rix * * 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 PLUGIN_LOADER_H #define PLUGIN_LOADER_H #include #include #include namespace Plasma { class Applet; class Containment; class ContainmentActions; class DataEngine; class Service; class PluginLoaderPrivate; //TODO: // * add loadWallpaper // * add KPluginInfo listing support for Containments (already loaded via the applet loading code) /** * @class PluginLoader plasma/pluginloader.h * * This is an abstract base class which defines an interface to which Plasma's * Applet Loading logic can communicate with a parent application. The plugin loader * must be set before any plugins are loaded, otherwise (for safety reasons), the * default PluginLoader implementation will be used. The reimplemented version should * not do more than simply returning a loaded plugin. It should not init() it, and it should not * hang on to it. The associated methods will be called only when a component of Plasma * needs to load a _new_ plugin. (e.g. DataEngine does its own caching). * * @author Ryan Rix * @since 4.6 **/ class PLASMA_EXPORT PluginLoader { public: /** * Load an Applet plugin. * * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param appletId unique ID to assign the applet, or zero to have one * assigned automatically. * @param args to send the applet extra arguments * @return a pointer to the loaded applet, or 0 on load failure **/ Applet *loadApplet(const QString &name, uint appletId = 0, const QVariantList &args = QVariantList()); /** * Load a dataengine plugin. * * @param name the name of the engine * @return the dataengine that was loaded, or the NullEngine on failure. **/ DataEngine *loadDataEngine(const QString &name); /** * @return a listing of all known dataengines by name * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only dataengines not specifically * registered to an application. */ static QStringList listAllEngines(const QString &parentApp = QString()); /** * Returns a list of all known dataengines. * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only dataengines not specifically * registered to an application. * @return list of dataengines **/ static KPluginInfo::List listEngineInfo(const QString &parentApp = QString()); /** * Returns a list of all known dataengines filtering by category. * * @param category the category to filter dataengines on. Uses the * X-KDE-PluginInfo-Category entry (if any) in the * plugin info. The value of QString() will * result in a list of dataengines with an empty category. * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only dataengines not specifically * registered to an application. * @return list of dataengines * @since 4.3 **/ static KPluginInfo::List listEngineInfoByCategory(const QString &category, const QString &parentApp = QString()); /** * Load a Service plugin. * * @param name the plugin name of the service to load * @param args a list of arguments to supply to the service plugin when loading it * @param parent the parent object, if any, for the service * * @return a Service object, unlike Plasma::Service::loadService, this can return null. **/ Service *loadService(const QString &name, const QVariantList &args, QObject *parent = 0); /** * Load a ContainmentActions plugin. * * Returns a pointer to the containmentactions if successful. * The caller takes responsibility for the containmentactions, including * deleting it when no longer needed. * * @param parent the parent containment. @since 4.6 null is allowed. * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param args to send the containmentactions extra arguments * @return a ContaimentActions object **/ ContainmentActions *loadContainmentActions(Containment *parent, const QString &containmentActionsName, const QVariantList &args = QVariantList()); /** * Load a Package plugin. * * @param name the plugin name of the package to load * @param specialization used to find script extensions for the given format, e.g. "QML" for "Plasma/Applet" * * @return a Package object matching name, or an invalid package on failure * @deprecated since 5.29 use KPackage::PackageLoader::loadPackage **/ #ifndef PLASMA_NO_DEPRECATED PLASMA_DEPRECATED Package loadPackage(const QString &packageFormat, const QString &specialization = QString()); #endif /** * Returns a list of all known applets. * This may skip applets based on security settings and ExcludeCategories in the application's config. * * @param category Only applets matchin this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @param parentApp the application to filter applets on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only applets not specifically * registered to an application. * @return list of applets * * @deprecated use listAppletMetaData. Doesn't support metadata.json packages. **/ PLASMA_DEPRECATED KPluginInfo::List listAppletInfo(const QString &category, const QString &parentApp = QString()); /** * Returns a list of all known applets. * This may skip applets based on security settings and ExcludeCategories in the application's config. * * @param category Only applets matchin this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @param parentApp the application to filter applets on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only applets not specifically * registered to an application. * @return list of applets * * @since 5.28 **/ QList listAppletMetaData(const QString &category, const QString &parentApp = QString()); /** * Returns a list of all known applets associated with a certain mimetype. * * @return list of applets + * + * @deprecated use listAppletMetaDataForMimeType instead + **/ + PLASMA_DEPRECATED KPluginInfo::List listAppletInfoForMimeType(const QString &mimetype); + + /** + * Returns a list of all known applets associated with a certain mimetype. + * + * @return list of applets + **/ + QList listAppletMetaDataForMimeType(const QString &mimetype); + + /** + * Returns a list of all known applets associated with a certain URL. + * + * @return list of applets + * + * @deprecated use listAppletMetaDataForUrl instead **/ - KPluginInfo::List listAppletInfoForMimeType(const QString &mimetype); + PLASMA_DEPRECATED KPluginInfo::List listAppletInfoForUrl(const QUrl &url); /** * Returns a list of all known applets associated with a certain URL. * * @return list of applets **/ - KPluginInfo::List listAppletInfoForUrl(const QUrl &url); + QList listAppletMetaDataForUrl(const QUrl &url); /** * Returns a list of all the categories used by installed applets. * * @param parentApp the application to filter applets on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only applets not specifically * registered to an application. * @return list of categories * @param visibleOnly true if it should only return applets that are marked as visible */ QStringList listAppletCategories(const QString &parentApp = QString(), bool visibleOnly = true); /** * Sets the list of custom categories that are used in addition to the default * set of categories known to libplasma for applets. * @param categories a list of categories * @since 4.3 */ void setCustomAppletCategories(const QStringList &categories); /** * @return the list of custom categories known to libplasma * @since 4.3 */ QStringList customAppletCategories() const; /** * Get the category of the given applet * * @param appletName the name of the applet */ QString appletCategory(const QString &appletName); /** * Returns a list of all known containments. * * @param category Only containments matching this category will be returned. * Useful in conjunction with knownCategories. * If "Miscellaneous" is passed in, then containments without a * Categories= entry are also returned. * If an empty string is passed in, all containments are * returned. * @param parentApp the application to filter containments on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only containments not specifically * registered to an application. * @return list of containments **/ static KPluginInfo::List listContainments(const QString &category = QString(), const QString &parentApp = QString()); /** * Returns a list of all known containments that match the parameters. * * @param type Only containments with this string in X-Plasma-ContainmentType * in their .desktop files will be returned. Common values are panel and * desktop * @param category Only containments matchin this category will be returned. * Useful in conjunction with knownCategories. * If "Miscellaneous" is passed in, then containments without a * Categories= entry are also returned. * If an empty string is passed in, all containments are * returned. * @param parentApp the application to filter containments on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only containments not specifically * registered to an application. * @return list of containments **/ static KPluginInfo::List listContainmentsOfType(const QString &type, const QString &category = QString(), const QString &parentApp = QString()); /** * @return a list of all known types of containments on this system */ static QStringList listContainmentTypes(); /** * Returns a list of all known containments associated with a certain MimeType * * @return list of containments **/ static KPluginInfo::List listContainmentsForMimeType(const QString &mimeType); /** * Returns a list of all known dataengines. * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only dataengines not specifically * registered to an application. * @return list of dataengines **/ KPluginInfo::List listDataEngineInfo(const QString &parentApp = QString()); /** * Returns a list of all known ContainmentActions. * * @param parentApp the application to filter ContainmentActions on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only ContainmentActions not specifically * registered to an application. * @return list of ContainmentActions **/ KPluginInfo::List listContainmentActionsInfo(const QString &parentApp); /** * Set the plugin loader which will be queried for all loads. * * @param loader A subclass of PluginLoader which will be supplied * by the application **/ static void setPluginLoader(PluginLoader *loader); /** * Return the active plugin loader **/ static PluginLoader *self(); protected: /** * A re-implementable method that allows subclasses to override * the default behaviour of loadApplet. If the applet requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadApplet prior to attempting to load an applet using the standard Plasma * plugin mechanisms. * * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param appletId unique ID to assign the applet, or zero to have one * assigned automatically. * @param args to send the applet extra arguments * @return a pointer to the loaded applet, or 0 on load failure **/ virtual Applet *internalLoadApplet(const QString &name, uint appletId = 0, const QVariantList &args = QVariantList()); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadDataEngine. If the engine requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadDataEngine prior to attempting to load a DataEgine using the standard Plasma * plugin mechanisms. * * @param name the name of the engine * @return the data engine that was loaded, or the NullEngine on failure. **/ virtual DataEngine *internalLoadDataEngine(const QString &name); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadService. If the service requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadService prior to attempting to load a Service using the standard Plasma * plugin mechanisms. * * @param name the plugin name of the service to load * @param args a list of arguments to supply to the service plugin when loading it * @param parent the parent object, if any, for the service * * @return a Service object, unlike Plasma::Service::loadService, this can return null. **/ virtual Service *internalLoadService(const QString &name, const QVariantList &args, QObject *parent = 0); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadContainmentActions. If the ContainmentActions requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadService prior to attempting to load a Service using the standard Plasma * plugin mechanisms. * * Returns a pointer to the containmentactions if successful. * The caller takes responsibility for the containmentactions, including * deleting it when no longer needed. * * @param parent the parent containment. @since 4.6 null is allowed. * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param args to send the containmentactions extra arguments * @return a ContaimentActions object **/ virtual ContainmentActions *internalLoadContainmentActions(Containment *parent, const QString &containmentActionsName, const QVariantList &args); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadPackage. If the service requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadService prior to attempting to load a Service using the standard Plasma * plugin mechanisms. * * @param name the plugin name of the service to load * @param args a list of arguments to supply to the service plugin when loading it * @param parent the parent object, if any, for the service * * @return a Service object, unlike Plasma::Service::loadService, this can return null. * @deprecated since 5.29 **/ #ifndef PLASMA_NO_DEPRECATED virtual PLASMA_DEPRECATED Package internalLoadPackage(const QString &name, const QString &specialization); #endif /** * A re-implementable method that allows subclasses to provide additional applets * for listAppletInfo. If the application has no applets to give to the application, * then the implementation should return an empty list. * * This method is called by listAppletInfo prior to generating the list of applets installed * on the system using the standard Plasma plugin mechanisms, and will try to find .desktop * files for your applets. * * @param category Only applets matching this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @return list of applets **/ virtual KPluginInfo::List internalAppletInfo(const QString &category) const; /** * A re-implementable method that allows subclasses to provide additional dataengines * for DataEngine::listDataEngines. * * @return list of dataengine info, or an empty list if none **/ virtual KPluginInfo::List internalDataEngineInfo() const; /** * Returns a list of all known Service implementations * * @return list of Service info, or an empty list if none */ virtual KPluginInfo::List internalServiceInfo() const; /** * Returns a list of all known ContainmentActions implementations * * @return list of ContainmentActions info, or an empty list if none */ virtual KPluginInfo::List internalContainmentActionsInfo() const; /** * Standardized mechanism for providing internal applets by install .desktop files * in $APPPDATA/plasma/internal/applets/ * * For applications that do this, internalAppletInfo can be implemented as a one-liner * call to this method. * * @param category Only applets matching this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @return list of applets, or an empty list if none */ KPluginInfo::List standardInternalAppletInfo(const QString &category) const; /** * Standardized mechanism for providing internal dataengines by install .desktop files * in $APPPDATA/plasma/internal/dataengines/ * * For applications that do this, internalDataEngineInfo can be implemented as a one-liner * call to this method. * * @return list of dataengines */ KPluginInfo::List standardInternalDataEngineInfo() const; /** * Standardized mechanism for providing internal services by install .desktop files * in $APPPDATA/plasma/internal/services/ * * For applications that do this, internalServiceInfo can be implemented as a one-liner * call to this method. * * @return list of services */ KPluginInfo::List standardInternalServiceInfo() const; PluginLoader(); virtual ~PluginLoader(); private: bool isPluginVersionCompatible(KPluginLoader &loader); PluginLoaderPrivate *const d; }; } Q_DECLARE_METATYPE(Plasma::PluginLoader *) #endif diff --git a/src/plasmaquick/appletquickitem.cpp b/src/plasmaquick/appletquickitem.cpp index 39facc613..4589f2087 100644 --- a/src/plasmaquick/appletquickitem.cpp +++ b/src/plasmaquick/appletquickitem.cpp @@ -1,814 +1,815 @@ /* * Copyright 2014 Marco Martin * * 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 "appletquickitem.h" #include "private/appletquickitem_p.h" #include #include #include #include #include #include #include #include #include #include #include #include +#include namespace PlasmaQuick { QHash AppletQuickItemPrivate::s_rootObjects = QHash(); AppletQuickItemPrivate::AppletQuickItemPrivate(Plasma::Applet *a, AppletQuickItem *item) : q(item), switchWidth(-1), switchHeight(-1), applet(a), expanded(false), activationTogglesExpanded(false) { } void AppletQuickItemPrivate::init() { if (!applet->pluginMetaData().isValid()) { // This `qmlObject` is used in other parts of the code qmlObject = new KDeclarative::QmlObject(q); return; } qmlObject = new KDeclarative::QmlObjectSharedEngine(q); if (!qmlObject->engine()->urlInterceptor()) { - PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(qmlObject->engine(), Plasma::Package()); + PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(qmlObject->engine(), KPackage::Package()); qmlObject->engine()->setUrlInterceptor(interceptor); } } void AppletQuickItemPrivate::connectLayoutAttached(QObject *item) { QObject *layout = 0; //Extract the representation's Layout, if any //No Item? if (!item) { return; } //Search a child that has the needed Layout properties //HACK: here we are not type safe, but is the only way to access to a pointer of Layout foreach (QObject *child, item->children()) { //find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid() && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid() && child->property("fillWidth").isValid() && child->property("fillHeight").isValid() ) { layout = child; break; } } //if the compact repr doesn't export a Layout.* attached property, //reset our own with default values if (!layout) { if (ownLayout) { ownLayout->setProperty("minimumWidth", 0); ownLayout->setProperty("minimumHeight", 0); ownLayout->setProperty("preferredWidth", -1); ownLayout->setProperty("preferredHeight", -1); ownLayout->setProperty("maximumWidth", std::numeric_limits::infinity()); ownLayout->setProperty("maximumHeight", std::numeric_limits::infinity()); ownLayout->setProperty("fillWidth", false); ownLayout->setProperty("fillHeight", false); } return; } //propagate all the size hints propagateSizeHint("minimumWidth"); propagateSizeHint("minimumHeight"); propagateSizeHint("preferredWidth"); propagateSizeHint("preferredHeight"); propagateSizeHint("maximumWidth"); propagateSizeHint("maximumHeight"); propagateSizeHint("fillWidth"); propagateSizeHint("fillHeight"); QObject *ownLayout = 0; foreach (QObject *child, q->children()) { //find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid() && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid() && child->property("fillWidth").isValid() && child->property("fillHeight").isValid() ) { ownLayout = child; break; } } //this should never happen, since we ask to create it if doesn't exists if (!ownLayout) { return; } //if the representation didn't change, don't do anything if (representationLayout == layout) { return; } if (representationLayout) { QObject::disconnect(representationLayout, 0, q, 0); } //Here we can't use the new connect syntax because we can't link against QtQuick layouts QObject::connect(layout, SIGNAL(minimumWidthChanged()), q, SLOT(minimumWidthChanged())); QObject::connect(layout, SIGNAL(minimumHeightChanged()), q, SLOT(minimumHeightChanged())); QObject::connect(layout, SIGNAL(preferredWidthChanged()), q, SLOT(preferredWidthChanged())); QObject::connect(layout, SIGNAL(preferredHeightChanged()), q, SLOT(preferredHeightChanged())); QObject::connect(layout, SIGNAL(maximumWidthChanged()), q, SLOT(maximumWidthChanged())); QObject::connect(layout, SIGNAL(maximumHeightChanged()), q, SLOT(maximumHeightChanged())); QObject::connect(layout, SIGNAL(fillWidthChanged()), q, SLOT(fillWidthChanged())); QObject::connect(layout, SIGNAL(fillHeightChanged()), q, SLOT(fillHeightChanged())); representationLayout = layout; AppletQuickItemPrivate::ownLayout = ownLayout; propagateSizeHint("minimumWidth"); propagateSizeHint("minimumHeight"); propagateSizeHint("preferredWidth"); propagateSizeHint("preferredHeight"); propagateSizeHint("maximumWidth"); propagateSizeHint("maximumHeight"); propagateSizeHint("fillWidth"); propagateSizeHint("fillHeight"); } void AppletQuickItemPrivate::propagateSizeHint(const QByteArray &layoutProperty) { if (ownLayout && representationLayout) { ownLayout->setProperty(layoutProperty, representationLayout->property(layoutProperty)); } } QQuickItem *AppletQuickItemPrivate::createCompactRepresentationItem() { if (!compactRepresentation) { return 0; } if (compactRepresentationItem) { return compactRepresentationItem; } QVariantHash initialProperties; initialProperties[QStringLiteral("parent")] = QVariant::fromValue(q); compactRepresentationItem = qobject_cast(qmlObject->createObjectFromComponent(compactRepresentation, QtQml::qmlContext(qmlObject->rootObject()), initialProperties)); emit q->compactRepresentationItemChanged(compactRepresentationItem); return compactRepresentationItem; } QQuickItem *AppletQuickItemPrivate::createFullRepresentationItem() { if (fullRepresentationItem) { return fullRepresentationItem; } if (fullRepresentation && fullRepresentation != qmlObject->mainComponent()) { QVariantHash initialProperties; initialProperties[QStringLiteral("parent")] = QVariant::fromValue(q); fullRepresentationItem = qobject_cast(qmlObject->createObjectFromComponent(fullRepresentation, QtQml::qmlContext(qmlObject->rootObject()), initialProperties)); } else { fullRepresentation = qmlObject->mainComponent(); fullRepresentationItem = qobject_cast(qmlObject->rootObject()); emit q->fullRepresentationChanged(fullRepresentation); } if (!fullRepresentationItem) { return 0; } emit q->fullRepresentationItemChanged(fullRepresentationItem); return fullRepresentationItem; } QQuickItem *AppletQuickItemPrivate::createCompactRepresentationExpanderItem() { if (!compactRepresentationExpander) { return 0; } if (compactRepresentationExpanderItem) { return compactRepresentationExpanderItem; } compactRepresentationExpanderItem = qobject_cast(qmlObject->createObjectFromComponent(compactRepresentationExpander, QtQml::qmlContext(qmlObject->rootObject()))); if (!compactRepresentationExpanderItem) { return 0; } compactRepresentationExpanderItem->setProperty("compactRepresentation", QVariant::fromValue(createCompactRepresentationItem())); return compactRepresentationExpanderItem; } void AppletQuickItemPrivate::compactRepresentationCheck() { if (!qmlObject->rootObject()) { return; } //ignore 0 sizes; if (q->width() <= 0 || q->height() <= 0) { return; } bool full = false; if (applet->isContainment()) { full = true; } else { if (switchWidth > 0 && switchHeight > 0) { full = q->width() > switchWidth && q->height() > switchHeight; //if a size to switch wasn't set, determine what representation to always chose } else { //preferred representation set? if (preferredRepresentation) { full = preferredRepresentation == fullRepresentation; //Otherwise, base on FormFactor } else { full = (applet->formFactor() != Plasma::Types::Horizontal && applet->formFactor() != Plasma::Types::Vertical); } } if ((full && fullRepresentationItem && fullRepresentationItem == currentRepresentationItem) || (!full && compactRepresentationItem && compactRepresentationItem == currentRepresentationItem) ) { return; } } //Expanded if (full) { QQuickItem *item = createFullRepresentationItem(); if (item) { //unwire with the expander if (compactRepresentationExpanderItem) { compactRepresentationExpanderItem->setProperty("fullRepresentation", QVariant()); compactRepresentationExpanderItem->setProperty("compactRepresentation", QVariant()); compactRepresentationExpanderItem->setVisible(false); } item->setParentItem(q); { //set anchors QQmlExpression expr(QtQml::qmlContext(qmlObject->rootObject()), item, QStringLiteral("parent")); QQmlProperty prop(item, QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); } if (compactRepresentationItem) { compactRepresentationItem->setVisible(false); } currentRepresentationItem = item; connectLayoutAttached(item); expanded = true; emit q->expandedChanged(true); } //Icon } else { QQuickItem *compactItem = createCompactRepresentationItem(); QQuickItem *compactExpanderItem = createCompactRepresentationExpanderItem(); if (compactItem && compactExpanderItem) { //set the root item as the main visible item compactItem->setVisible(true); compactExpanderItem->setParentItem(q); compactExpanderItem->setVisible(true); { //set anchors QQmlExpression expr(QtQml::qmlContext(qmlObject->rootObject()), compactExpanderItem, QStringLiteral("parent")); QQmlProperty prop(compactExpanderItem, QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); } if (fullRepresentationItem) { fullRepresentationItem->setProperty("parent", QVariant()); } compactExpanderItem->setProperty("compactRepresentation", QVariant::fromValue(compactItem)); //The actual full representation will be connected when created compactExpanderItem->setProperty("fullRepresentation", QVariant()); currentRepresentationItem = compactItem; connectLayoutAttached(compactItem); expanded = false; emit q->expandedChanged(false); } } } void AppletQuickItemPrivate::minimumWidthChanged() { propagateSizeHint("minimumWidth"); } void AppletQuickItemPrivate::minimumHeightChanged() { propagateSizeHint("minimumHeight"); } void AppletQuickItemPrivate::preferredWidthChanged() { propagateSizeHint("preferredWidth"); } void AppletQuickItemPrivate::preferredHeightChanged() { propagateSizeHint("preferredHeight"); } void AppletQuickItemPrivate::maximumWidthChanged() { propagateSizeHint("maximumWidth"); } void AppletQuickItemPrivate::maximumHeightChanged() { propagateSizeHint("maximumHeight"); } void AppletQuickItemPrivate::fillWidthChanged() { propagateSizeHint("fillWidth"); } void AppletQuickItemPrivate::fillHeightChanged() { propagateSizeHint("fillHeight"); } AppletQuickItem::AppletQuickItem(Plasma::Applet *applet, QQuickItem *parent) : QQuickItem(parent), d(new AppletQuickItemPrivate(applet, this)) { d->init(); if (d->applet) { - d->appletPackage = d->applet->package(); + d->appletPackage = d->applet->kPackage(); if (d->applet->containment()) { if (d->applet->containment()->corona()) { - d->coronaPackage = d->applet->containment()->corona()->package(); + d->coronaPackage = d->applet->containment()->corona()->kPackage(); } - d->containmentPackage = d->applet->containment()->package(); + d->containmentPackage = d->applet->containment()->kPackage(); } if (d->applet->pluginMetaData().isValid()) { const QString rootPath = d->applet->pluginMetaData().value(QStringLiteral("X-Plasma-RootPath")); if (!rootPath.isEmpty()) { d->qmlObject->setTranslationDomain(QLatin1String("plasma_applet_") + rootPath); } else { d->qmlObject->setTranslationDomain(QLatin1String("plasma_applet_") + d->applet->pluginMetaData().pluginId()); } } // set the graphicObject dynamic property on applet d->applet->setProperty("_plasma_graphicObject", QVariant::fromValue(this)); } d->qmlObject->setInitializationDelayed(true); setProperty("_plasma_applet", QVariant::fromValue(d->applet)); } AppletQuickItem::~AppletQuickItem() { //Here the order is important delete d->compactRepresentationItem; delete d->fullRepresentationItem; delete d->compactRepresentationExpanderItem; AppletQuickItemPrivate::s_rootObjects.remove(d->qmlObject->rootContext()); } AppletQuickItem *AppletQuickItem::qmlAttachedProperties(QObject *object) { QQmlContext *context; //is it using shared engine mode? if (!QtQml::qmlEngine(object)->parent()) { context = QtQml::qmlContext(object); //search the root context of the applet in which the object is in while (context) { //the rootcontext of an applet is a child of the engine root context if (context->parentContext() == QtQml::qmlEngine(object)->rootContext()) { break; } context = context->parentContext(); } //otherwise index by root context } else { context = QtQml::qmlEngine(object)->rootContext(); } //at the moment of the attached object creation, the root item is the only one that hasn't a parent //only way to avoid creation of this attached for everybody but the root item if (!object->parent() && AppletQuickItemPrivate::s_rootObjects.contains(context)) { return AppletQuickItemPrivate::s_rootObjects.value(context); } else { return 0; } } Plasma::Applet *AppletQuickItem::applet() const { return d->applet; } void AppletQuickItem::init() { //FIXME: Plasmoid attached property should be fixed since can't be indexed by engine anymore if (AppletQuickItemPrivate::s_rootObjects.contains(d->qmlObject->rootContext())) { return; } AppletQuickItemPrivate::s_rootObjects[d->qmlObject->rootContext()] = this; Q_ASSERT(d->applet); //Initialize the main QML file QQmlEngine *engine = d->qmlObject->engine(); //if the engine of the qmlObject is different from the static one, then we //are using an old version of the api in which every applet had one engine //so initialize a private url interceptor if (d->applet->kPackage().isValid() && !qobject_cast(d->qmlObject)) { - PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(engine, d->applet->package()); + PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(engine, d->applet->kPackage()); interceptor->addAllowedPath(d->coronaPackage.path()); engine->setUrlInterceptor(interceptor); } //Force QtQuickControls to use the "Plasma" style for this engine. //this way is possible to mix QtQuickControls and plasma components in applets //while still having the desktop style in configuration dialogs QQmlComponent c(engine); c.setData(QByteArrayLiteral("import QtQuick 2.1\n\ import QtQuick.Controls 1.0\n\ import QtQuick.Controls.Private 1.0\n \ Item {\ Component.onCompleted: {\ Settings.styleName = \"Plasma\";\ }\ }"), QUrl()); QObject *o = c.create(); o->deleteLater(); d->qmlObject->setSource(QUrl::fromLocalFile(d->applet->kPackage().filePath("mainscript"))); if (!engine || !engine->rootContext() || !engine->rootContext()->isValid() || !d->qmlObject->mainComponent() || d->qmlObject->mainComponent()->isError() || d->applet->failedToLaunch()) { QString reason; if (d->applet->failedToLaunch()) { reason = d->applet->launchErrorMessage(); } else if (d->applet->kPackage().isValid()) { foreach (QQmlError error, d->qmlObject->mainComponent()->errors()) { reason += error.toString() + '\n'; } reason = i18n("Error loading QML file: %1", reason); } else { reason = i18n("Error loading Applet: package inexistent. %1", applet()->launchErrorMessage()); } d->qmlObject->setSource(QUrl::fromLocalFile(d->coronaPackage.filePath("appleterror"))); d->qmlObject->completeInitialization(); //even the error message QML may fail if (d->qmlObject->mainComponent()->isError()) { return; } else { d->qmlObject->rootObject()->setProperty("reason", reason); } d->applet->setLaunchErrorMessage(reason); } d->qmlObject->rootContext()->setContextProperty(QStringLiteral("plasmoid"), this); //initialize size, so an useless resize less QVariantHash initialProperties; //initialize with our size only if valid if (width() > 0 && height() > 0) { const qreal w = parentItem() ? std::min(parentItem()->width(), width()) : width(); const qreal h = parentItem() ? std::min(parentItem()->height(), height()) : height(); initialProperties[QStringLiteral("width")] = w; initialProperties[QStringLiteral("height")] = h; } d->qmlObject->setInitializationDelayed(false); d->qmlObject->completeInitialization(initialProperties); //otherwise, initialize our size to root object's size if (d->qmlObject->rootObject() && (width() <= 0 || height() <= 0)) { const qreal w = d->qmlObject->rootObject()->property("width").value(); const qreal h = d->qmlObject->rootObject()->property("height").value(); setSize(parentItem() ? QSizeF(std::min(parentItem()->width(), w), std::min(parentItem()->height(), h)) : QSizeF(w, h)); } //default fullrepresentation is our root main component, if none specified if (!d->fullRepresentation) { d->fullRepresentation = d->qmlObject->mainComponent(); d->fullRepresentationItem = qobject_cast(d->qmlObject->rootObject()); emit fullRepresentationChanged(d->fullRepresentation); } //default compactRepresentation is a simple icon provided by the shell package if (!d->compactRepresentation) { d->compactRepresentation = new QQmlComponent(engine, this); d->compactRepresentation->loadUrl(QUrl::fromLocalFile(d->coronaPackage.filePath("defaultcompactrepresentation"))); emit compactRepresentationChanged(d->compactRepresentation); } //default compactRepresentationExpander is the popup in which fullRepresentation goes if (!d->compactRepresentationExpander) { d->compactRepresentationExpander = new QQmlComponent(engine, this); QString compactExpanderPath = d->containmentPackage.filePath("compactapplet"); if (compactExpanderPath.isEmpty()) { compactExpanderPath = d->coronaPackage.filePath("compactapplet"); } d->compactRepresentationExpander->loadUrl(QUrl::fromLocalFile(compactExpanderPath)); } d->compactRepresentationCheck(); qmlObject()->engine()->rootContext()->setBaseUrl(qmlObject()->source()); qmlObject()->engine()->setContextForObject(this, qmlObject()->engine()->rootContext()); } Plasma::Package AppletQuickItem::appletPackage() const { - return d->appletPackage; + return Plasma::Package(d->appletPackage); } void AppletQuickItem::setAppletPackage(const Plasma::Package &package) { - d->appletPackage = package; + d->appletPackage = package.kPackage(); } Plasma::Package AppletQuickItem::coronaPackage() const { - return d->coronaPackage; + return Plasma::Package(d->coronaPackage); } void AppletQuickItem::setCoronaPackage(const Plasma::Package &package) { - d->coronaPackage = package; + d->coronaPackage = package.kPackage(); } int AppletQuickItem::switchWidth() const { return d->switchWidth; } void AppletQuickItem::setSwitchWidth(int width) { if (d->switchWidth == width) { return; } d->switchWidth = width; emit switchWidthChanged(width); } int AppletQuickItem::switchHeight() const { return d->switchHeight; } void AppletQuickItem::setSwitchHeight(int height) { if (d->switchHeight == height) { return; } d->switchHeight = height; emit switchHeightChanged(height); } QQmlComponent *AppletQuickItem::compactRepresentation() { return d->compactRepresentation; } void AppletQuickItem::setCompactRepresentation(QQmlComponent *component) { if (d->compactRepresentation == component) { return; } d->compactRepresentation = component; emit compactRepresentationChanged(component); } QQmlComponent *AppletQuickItem::fullRepresentation() { return d->fullRepresentation; } QObject *AppletQuickItem::testItem() { if (!d->testItem) { const QUrl url = QUrl::fromLocalFile(d->appletPackage.filePath("test")); if (url.isEmpty()) { return nullptr; } d->testItem = d->qmlObject->createObjectFromSource(url, QtQml::qmlContext(rootItem())); d->testItem->setProperty("plasmoidItem", QVariant::fromValue(this)); } return d->testItem; } void AppletQuickItem::setFullRepresentation(QQmlComponent *component) { if (d->fullRepresentation == component) { return; } d->fullRepresentation = component; emit fullRepresentationChanged(component); } QQmlComponent *AppletQuickItem::preferredRepresentation() { return d->preferredRepresentation; } void AppletQuickItem::setPreferredRepresentation(QQmlComponent *component) { if (d->preferredRepresentation == component) { return; } d->preferredRepresentation = component; emit preferredRepresentationChanged(component); d->compactRepresentationCheck(); } bool AppletQuickItem::isExpanded() const { return d->expanded; } void AppletQuickItem::setExpanded(bool expanded) { if (d->expanded == expanded) { return; } if (expanded) { d->createFullRepresentationItem(); if (!d->applet->isContainment() && (!d->preferredRepresentation || d->preferredRepresentation != d->fullRepresentation)) { d->createCompactRepresentationExpanderItem(); } if (d->compactRepresentationExpanderItem) { d->compactRepresentationExpanderItem->setProperty("fullRepresentation", QVariant::fromValue(d->createFullRepresentationItem())); } else { d->fullRepresentationItem->setProperty("parent", QVariant::fromValue(this)); } } d->expanded = expanded; emit expandedChanged(expanded); } bool AppletQuickItem::isActivationTogglesExpanded() const { return d->activationTogglesExpanded; } void AppletQuickItem::setActivationTogglesExpanded(bool activationTogglesExpanded) { if (d->activationTogglesExpanded == activationTogglesExpanded) { return; } d->activationTogglesExpanded = activationTogglesExpanded; emit activationTogglesExpandedChanged(activationTogglesExpanded); } ////////////Internals KDeclarative::QmlObject *AppletQuickItem::qmlObject() { return d->qmlObject; } QQuickItem *AppletQuickItem::compactRepresentationItem() { return d->compactRepresentationItem; } QQuickItem *AppletQuickItem::fullRepresentationItem() { return d->fullRepresentationItem; } QObject *AppletQuickItem::rootItem() { return d->qmlObject->rootObject(); } void AppletQuickItem::childEvent(QChildEvent *event) { // Added child may be QQuickLayoutAttached if (event->added() && !d->ownLayout && d->currentRepresentationItem) { // Child has not yet finished initialization at this point QTimer::singleShot(0, this, [this]() { if (!d->ownLayout) { d->connectLayoutAttached(d->currentRepresentationItem); } }); } QQuickItem::childEvent(event); } void AppletQuickItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_UNUSED(oldGeometry) QQuickItem::geometryChanged(newGeometry, oldGeometry); d->compactRepresentationCheck(); } void AppletQuickItem::itemChange(ItemChange change, const ItemChangeData &value) { if (change == QQuickItem::ItemSceneChange) { //we have a window: create the representations if needed if (value.window) { init(); } } QQuickItem::itemChange(change, value); } } #include "moc_appletquickitem.cpp" diff --git a/src/plasmaquick/appletquickitem.h b/src/plasmaquick/appletquickitem.h index 7df364daf..d545bc964 100644 --- a/src/plasmaquick/appletquickitem.h +++ b/src/plasmaquick/appletquickitem.h @@ -1,181 +1,181 @@ /* * Copyright 2014 Marco Martin * * 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 APPLETQUICKITEM_H #define APPLETQUICKITEM_H #include #include #include #include #include #include #include // // W A R N I N G // ------------- // // This file is not part of the public Plasma API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // namespace Plasma { class Applet; } namespace KDeclarative { class QmlObject; } namespace PlasmaQuick { class AppletQuickItemPrivate; class PLASMAQUICK_EXPORT AppletQuickItem : public QQuickItem { Q_OBJECT Q_PROPERTY(int switchWidth READ switchWidth WRITE setSwitchWidth NOTIFY switchWidthChanged) Q_PROPERTY(int switchHeight READ switchHeight WRITE setSwitchHeight NOTIFY switchHeightChanged) Q_PROPERTY(QQmlComponent *compactRepresentation READ compactRepresentation WRITE setCompactRepresentation NOTIFY compactRepresentationChanged) Q_PROPERTY(QQuickItem *compactRepresentationItem READ compactRepresentationItem NOTIFY compactRepresentationItemChanged) Q_PROPERTY(QQmlComponent *fullRepresentation READ fullRepresentation WRITE setFullRepresentation NOTIFY fullRepresentationChanged) Q_PROPERTY(QQuickItem *fullRepresentationItem READ fullRepresentationItem NOTIFY fullRepresentationItemChanged) /** * this is supposed to be either one between compactRepresentation or fullRepresentation */ Q_PROPERTY(QQmlComponent *preferredRepresentation READ preferredRepresentation WRITE setPreferredRepresentation NOTIFY preferredRepresentationChanged) /** * True when the applet is showing its full representation. either as the main only view, or in a popup. * Setting it will open or close the popup if the plasmoid is iconified, however it won't have effect if the applet is open */ Q_PROPERTY(bool expanded WRITE setExpanded READ isExpanded NOTIFY expandedChanged) /** * True when the applet wants the activation signal act in toggle mode, i.e. while being expanded * the signal shrinks the applet to its not exanded state instead of reexpanding it. */ Q_PROPERTY(bool activationTogglesExpanded WRITE setActivationTogglesExpanded READ isActivationTogglesExpanded NOTIFY activationTogglesExpandedChanged) /** * the applet root QML item: sometimes is the same as fullRepresentationItem * if a fullrepresentation was not declared explicitly */ Q_PROPERTY(QObject *rootItem READ rootItem CONSTANT) public: AppletQuickItem(Plasma::Applet *applet, QQuickItem *parent = 0); ~AppletQuickItem(); ////API NOT SUPPOSED TO BE USED BY QML Plasma::Applet *applet() const; //Make the constructor lighter and delay the actual instantiation of the qml in the applet virtual void init(); - Plasma::Package appletPackage() const; - void setAppletPackage(const Plasma::Package &package); + PLASMA_DEPRECATED Plasma::Package appletPackage() const; + PLASMA_DEPRECATED void setAppletPackage(const Plasma::Package &package); - Plasma::Package coronaPackage() const; - void setCoronaPackage(const Plasma::Package &package); + PLASMA_DEPRECATED Plasma::Package coronaPackage() const; + PLASMA_DEPRECATED void setCoronaPackage(const Plasma::Package &package); QQuickItem *compactRepresentationItem(); QQuickItem *fullRepresentationItem(); QObject *rootItem(); QObject *testItem(); ////PROPERTY ACCESSORS int switchWidth() const; void setSwitchWidth(int width); int switchHeight() const; void setSwitchHeight(int width); QQmlComponent *compactRepresentation(); void setCompactRepresentation(QQmlComponent *component); QQmlComponent *fullRepresentation(); void setFullRepresentation(QQmlComponent *component); QQmlComponent *preferredRepresentation(); void setPreferredRepresentation(QQmlComponent *component); bool isExpanded() const; void setExpanded(bool expanded); bool isActivationTogglesExpanded() const; void setActivationTogglesExpanded(bool activationTogglesExpanded); ////NEEDED BY QML TO CREATE ATTACHED PROPERTIES static AppletQuickItem *qmlAttachedProperties(QObject *object); Q_SIGNALS: //Property signals void switchWidthChanged(int width); void switchHeightChanged(int height); void expandedChanged(bool expanded); void activationTogglesExpandedChanged(bool activationTogglesExpanded); void compactRepresentationChanged(QQmlComponent *compactRepresentation); void fullRepresentationChanged(QQmlComponent *fullRepresentation); void preferredRepresentationChanged(QQmlComponent *preferredRepresentation); void compactRepresentationItemChanged(QObject *compactRepresentationItem); void fullRepresentationItemChanged(QObject *fullRepresentationItem); protected: KDeclarative::QmlObject *qmlObject(); //Reimplementation void childEvent(QChildEvent *event) Q_DECL_OVERRIDE; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE; private: AppletQuickItemPrivate *const d; Q_PRIVATE_SLOT(d, void minimumWidthChanged()) Q_PRIVATE_SLOT(d, void minimumHeightChanged()) Q_PRIVATE_SLOT(d, void preferredWidthChanged()) Q_PRIVATE_SLOT(d, void preferredHeightChanged()) Q_PRIVATE_SLOT(d, void maximumWidthChanged()) Q_PRIVATE_SLOT(d, void maximumHeightChanged()) Q_PRIVATE_SLOT(d, void fillWidthChanged()) Q_PRIVATE_SLOT(d, void fillHeightChanged()) }; } QML_DECLARE_TYPEINFO(PlasmaQuick::AppletQuickItem, QML_HAS_ATTACHED_PROPERTIES) #endif diff --git a/src/plasmaquick/configview.cpp b/src/plasmaquick/configview.cpp index 1eba0b63c..8dd4111f2 100644 --- a/src/plasmaquick/configview.cpp +++ b/src/plasmaquick/configview.cpp @@ -1,358 +1,359 @@ /* * Copyright 2013 Marco Martin * * 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 "private/configcategory_p.h" #include "configview.h" #include "configmodel.h" #include "Plasma/Applet" #include "Plasma/Containment" //#include "plasmoid/wallpaperinterface.h" #include "kdeclarative/configpropertymap.h" #include #include #include #include #include #include #include #include #include #include #include #include #include //Unfortunately QWINDOWSIZE_MAX is not exported #define DIALOGSIZE_MAX ((1<<24)-1) namespace PlasmaQuick { //////////////////////////////ConfigView class ConfigViewPrivate { public: ConfigViewPrivate(Plasma::Applet *appl, ConfigView *view); ~ConfigViewPrivate(); void init(); void updateMinimumWidth(); void updateMinimumHeight(); void updateMaximumWidth(); void updateMaximumHeight(); void mainItemLoaded(); ConfigView *q; QWeakPointer applet; ConfigModel *configModel; ConfigModel *kcmConfigModel; Plasma::Corona *corona; //Attached Layout property of mainItem, if any QWeakPointer mainItemLayout; }; ConfigViewPrivate::ConfigViewPrivate(Plasma::Applet *appl, ConfigView *view) : q(view), applet(appl), corona(0) { } void ConfigViewPrivate::init() { if (!applet) { qWarning() << "Null applet passed to constructor"; return; } if (!applet.data()->pluginMetaData().isValid()) { qWarning() << "Invalid applet passed to constructor"; return; } applet.data()->setUserConfiguring(true); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(q->engine()); const QString rootPath = applet.data()->pluginMetaData().value(QStringLiteral("X-Plasma-RootPath")); if (!rootPath.isEmpty()) { kdeclarative.setTranslationDomain("plasma_applet_" + rootPath); } else { kdeclarative.setTranslationDomain("plasma_applet_" + applet.data()->pluginMetaData().pluginId()); } kdeclarative.setupBindings(); //FIXME: problem on nvidia, all windows should be transparent or won't show q->setColor(Qt::transparent); q->setTitle(i18n("%1 Settings", applet.data()->title())); //systray case if (!applet.data()->containment()->corona()) { Plasma::Applet *a = qobject_cast(applet.data()->containment()->parent()); if (a) { corona = a->containment()->corona(); } } else if (!applet.data()->containment()->corona()->kPackage().isValid()) { qWarning() << "Invalid home screen package"; } else { corona = applet.data()->containment()->corona(); } if (!corona) { qWarning() << "Cannot find a Corona, this should never happen!"; return; } - if (corona->kPackage().isValid()) { - PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(q->engine(), corona->package()); + const auto pkg = corona->kPackage(); + if (pkg.isValid()) { + PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(q->engine(), pkg); interceptor->addAllowedPath(applet.data()->kPackage().path()); q->engine()->setUrlInterceptor(interceptor); } q->setResizeMode(QQuickView::SizeViewToRootObject); //config model local of the applet QQmlComponent *component = new QQmlComponent(q->engine(), QUrl::fromLocalFile(applet.data()->kPackage().filePath("configmodel")), q); QObject *object = component->beginCreate(q->engine()->rootContext()); configModel = qobject_cast(object); if (configModel) { configModel->setApplet(applet.data()); } else { delete object; } QStringList kcms = KPluginMetaData::readStringList(applet.data()->pluginMetaData().rawData(), QStringLiteral("X-Plasma-ConfigPlugins")); // filter out non-authorized KCMs // KAuthorized expects KCMs with .desktop suffix, so we can't just pass everything // to KAuthorized::authorizeControlModules verbatim kcms.erase(std::remove_if(kcms.begin(), kcms.end(), [](const QString &kcm) { return !KAuthorized::authorizeControlModule(kcm + QLatin1String(".desktop")); }), kcms.end()); if (!kcms.isEmpty()) { if (!configModel) { configModel = new ConfigModel(q); } foreach (const QString &kcm, kcms) { KPluginLoader loader(KPluginLoader::findPlugin(QLatin1String("kcms/") + kcm)); KPluginMetaData md(loader.fileName()); if (!md.isValid()) { qWarning() << "Could not find" << kcm << "specified in X-Plasma-ConfigPlugins"; continue; } configModel->appendCategory(md.iconName(), md.name(), QString(), loader.fileName()); } } q->engine()->rootContext()->setContextProperty(QStringLiteral("plasmoid"), applet.data()->property("_plasma_graphicObject").value()); q->engine()->rootContext()->setContextProperty(QStringLiteral("configDialog"), q); component->completeCreate(); delete component; } void ConfigViewPrivate::updateMinimumWidth() { if (mainItemLayout) { q->setMinimumWidth(mainItemLayout.data()->property("minimumWidth").toInt()); //Sometimes setMinimumWidth doesn't actually resize: Qt bug? q->setWidth(qMax(q->width(), q->minimumWidth())); } else { q->setMinimumWidth(-1); } } void ConfigViewPrivate::updateMinimumHeight() { if (mainItemLayout) { q->setMinimumHeight(mainItemLayout.data()->property("minimumHeight").toInt()); //Sometimes setMinimumHeight doesn't actually resize: Qt bug? q->setHeight(qMax(q->height(), q->minimumHeight())); } else { q->setMinimumHeight(-1); } } void ConfigViewPrivate::updateMaximumWidth() { if (mainItemLayout) { const int hint = mainItemLayout.data()->property("maximumWidth").toInt(); if (hint > 0) { q->setMaximumWidth(hint); } else { q->setMaximumWidth(DIALOGSIZE_MAX); } } else { q->setMaximumWidth(DIALOGSIZE_MAX); } } void ConfigViewPrivate::updateMaximumHeight() { if (mainItemLayout) { const int hint = mainItemLayout.data()->property("maximumHeight").toInt(); if (hint > 0) { q->setMaximumHeight(hint); } else { q->setMaximumHeight(DIALOGSIZE_MAX); } } else { q->setMaximumHeight(DIALOGSIZE_MAX); } } void ConfigViewPrivate::mainItemLoaded() { if (applet) { KConfigGroup cg = applet.data()->config(); cg = KConfigGroup(&cg, "ConfigDialog"); q->resize(cg.readEntry("DialogWidth", q->width()), cg.readEntry("DialogHeight", q->height())); } //Extract the representation's Layout, if any QObject *layout = 0; //Search a child that has the needed Layout properties //HACK: here we are not type safe, but is the only way to access to a pointer of Layout foreach (QObject *child, q->rootObject()->children()) { //find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid() && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid() && child->property("fillWidth").isValid() && child->property("fillHeight").isValid() ) { layout = child; break; } } mainItemLayout = layout; if (layout) { QObject::connect(layout, SIGNAL(minimumWidthChanged()), q, SLOT(updateMinimumWidth())); QObject::connect(layout, SIGNAL(minimumHeightChanged()), q, SLOT(updateMinimumHeight())); QObject::connect(layout, SIGNAL(maximumWidthChanged()), q, SLOT(updateMaximumWidth())); QObject::connect(layout, SIGNAL(maximumHeightChanged()), q, SLOT(updateMaximumHeight())); updateMinimumWidth(); updateMinimumHeight(); updateMaximumWidth(); updateMaximumHeight(); } } ConfigView::ConfigView(Plasma::Applet *applet, QWindow *parent) : QQuickView(parent), d(new ConfigViewPrivate(applet, this)) { setIcon(QIcon::fromTheme(QStringLiteral("configure"))); qmlRegisterType("org.kde.plasma.configuration", 2, 0, "ConfigModel"); qmlRegisterType("org.kde.plasma.configuration", 2, 0, "ConfigCategory"); d->init(); connect(applet, &QObject::destroyed, this, &ConfigView::close); connect(this, &QQuickView::statusChanged, [=](QQuickView::Status status) { if (status == QQuickView::Ready) { d->mainItemLoaded(); } }); } ConfigView::~ConfigView() { if (d->applet) { d->applet.data()->setUserConfiguring(false); if (d->applet.data()->containment() && d->applet.data()->containment()->corona()) { d->applet.data()->containment()->corona()->requestConfigSync(); } } } void ConfigView::init() { setSource(QUrl::fromLocalFile(d->corona->kPackage().filePath("appletconfigurationui"))); } Plasma::Applet *ConfigView::applet() { return d->applet.data(); } ConfigModel *ConfigView::configModel() const { return d->configModel; } QString ConfigView::appletGlobalShortcut() const { if (!d->applet) { return QString(); } return d->applet.data()->globalShortcut().toString(); } void ConfigView::setAppletGlobalShortcut(const QString &shortcut) { if (!d->applet || d->applet.data()->globalShortcut().toString().toLower() == shortcut.toLower()) { return; } d->applet.data()->setGlobalShortcut(shortcut); emit appletGlobalShortcutChanged(); } //To emulate Qt::WA_DeleteOnClose that QWindow doesn't have void ConfigView::hideEvent(QHideEvent *ev) { QQuickWindow::hideEvent(ev); deleteLater(); } void ConfigView::resizeEvent(QResizeEvent *re) { if (!rootObject()) { return; } rootObject()->setSize(re->size()); if (d->applet) { KConfigGroup cg = d->applet.data()->config(); cg = KConfigGroup(&cg, "ConfigDialog"); cg.writeEntry("DialogWidth", re->size().width()); cg.writeEntry("DialogHeight", re->size().height()); } QQuickWindow::resizeEvent(re); } } #include "moc_configview.cpp" diff --git a/src/plasmaquick/containmentview.cpp b/src/plasmaquick/containmentview.cpp index 309a2cb15..2592614ea 100644 --- a/src/plasmaquick/containmentview.cpp +++ b/src/plasmaquick/containmentview.cpp @@ -1,293 +1,293 @@ /* * Copyright 2013 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, 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 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 "containmentview.h" #include "configview.h" #include #include #include #include #include #include #include "plasma/pluginloader.h" #include #include namespace PlasmaQuick { class ContainmentViewPrivate { public: ContainmentViewPrivate(Plasma::Corona *corona, ContainmentView *view); ~ContainmentViewPrivate(); void setContainment(Plasma::Containment *cont); Plasma::Types::FormFactor formFactor() const; Plasma::Types::Location location() const; void showConfigurationInterface(Plasma::Applet *applet); void updateDestroyed(bool destroyed); ContainmentView *q; friend class ContainmentView; Plasma::Corona *corona; QPointer containment; QPointer configContainmentView; }; ContainmentViewPrivate::ContainmentViewPrivate(Plasma::Corona *cor, ContainmentView *view) : q(view), corona(cor) { } ContainmentViewPrivate::~ContainmentViewPrivate() { } void ContainmentViewPrivate::setContainment(Plasma::Containment *cont) { if (containment == cont) { return; } Plasma::Types::Location oldLoc = location(); Plasma::Types::FormFactor oldForm = formFactor(); if (containment) { QObject::disconnect(containment, 0, q, 0); QObject *oldGraphicObject = containment->property("_plasma_graphicObject").value(); if (oldGraphicObject) { // qDebug() << "Old graphics Object:" << oldGraphicObject << "Old containment" << containment.data(); //make sure the graphic object won't die with us //FIXME:we need a way to reparent to *NO* graphics item, but this makes Qt crash oldGraphicObject->setParent(containment); } containment->reactToScreenChange(); } containment = cont; if (oldLoc != location()) { emit q->locationChanged(location()); } if (oldForm != formFactor()) { emit q->formFactorChanged(formFactor()); } emit q->containmentChanged(); //we are QuickViewSharedEngine::SizeRootObjectToView, but that's not enough, as //the root object isn't immediately resized (done at the resizeEvent handler). //by resising it just before restoring the containment, it removes a chain of resizes at startup if (q->rootObject()) { q->rootObject()->setSize(q->size()); } if (cont) { cont->reactToScreenChange(); QObject::connect(cont, &Plasma::Containment::locationChanged, q, &ContainmentView::locationChanged); QObject::connect(cont, &Plasma::Containment::formFactorChanged, q, &ContainmentView::formFactorChanged); QObject::connect(cont, &Plasma::Containment::configureRequested, q, &ContainmentView::showConfigurationInterface); QObject::connect(cont, SIGNAL(destroyedChanged(bool)), q, SLOT(updateDestroyed(bool))); // Panels are created invisible and the code below ensures they are only // shown once their contents have settled to avoid visual glitches on startup if (cont->containmentType() == Plasma::Types::PanelContainment || cont->containmentType() == Plasma::Types::CustomPanelContainment) { QObject::connect(cont, &Plasma::Containment::uiReadyChanged, q, [this, cont](bool ready) { if (ready && !cont->destroyed()) { q->setVisible(true); } }, Qt::QueuedConnection); q->setVisible(!cont->destroyed() && cont->isUiReady()); } } else { return; } QQuickItem *graphicObject = qobject_cast(containment->property("_plasma_graphicObject").value()); if (graphicObject) { // qDebug() << "using as graphic containment" << graphicObject << containment.data(); //by resizing before adding, it will avoid some resizes in most cases graphicObject->setProperty("width", q->width()); graphicObject->setProperty("height", q->height()); graphicObject->setParentItem(q->rootObject()); if (q->rootObject()) { q->rootObject()->setProperty("containment", QVariant::fromValue(graphicObject)); QObject *wpGraphicObject = containment->property("wallpaperGraphicsObject").value(); if (wpGraphicObject) { q->rootObject()->setProperty("wallpaper", QVariant::fromValue(wpGraphicObject)); } } else { qWarning() << "Could not set containment property on rootObject"; } } else { qWarning() << "Containment graphic object not valid"; } } Plasma::Types::Location ContainmentViewPrivate::location() const { if (!containment) { return Plasma::Types::Desktop; } return containment->location(); } Plasma::Types::FormFactor ContainmentViewPrivate::formFactor() const { if (!containment) { return Plasma::Types::Planar; } return containment->formFactor(); } void ContainmentViewPrivate::showConfigurationInterface(Plasma::Applet *applet) { if (configContainmentView) { if (configContainmentView->applet() != applet) { configContainmentView->hide(); configContainmentView->deleteLater(); } else { configContainmentView->requestActivate(); return; } } if (!applet || !applet->containment()) { return; } configContainmentView = new ConfigView(applet); configContainmentView->init(); configContainmentView->show(); } void ContainmentViewPrivate::updateDestroyed(bool destroyed) { q->setVisible(!destroyed); } ContainmentView::ContainmentView(Plasma::Corona *corona, QWindow *parent) : KQuickAddons::QuickViewSharedEngine(parent), d(new ContainmentViewPrivate(corona, this)) { setColor(Qt::transparent); QObject::connect(screen(), &QScreen::geometryChanged, this, &ContainmentView::screenGeometryChanged); if (corona->kPackage().isValid()) { - KPluginInfo info = corona->package().metadata(); + const auto info = corona->kPackage().metadata(); if (info.isValid()) { - setTranslationDomain("plasma_shell_" + info.pluginName()); + setTranslationDomain("plasma_shell_" + info.pluginId()); } else { qWarning() << "Invalid corona package metadata"; } } else { qWarning() << "Invalid home screen package"; } //Force QtQuickControls to use the "Plasma" style for this engine. //this way is possible to mix QtQuickControls and plasma components in applets //while still having the desktop style in configuration dialogs QQmlComponent c(engine()); c.setData("import QtQuick 2.1\n\ import QtQuick.Controls 1.0\n\ import QtQuick.Controls.Private 1.0\n \ Item {\ Component.onCompleted: {\ Settings.styleName = \"Plasma\";\ }\ }", QUrl()); QObject *o = c.create(); o->deleteLater(); setResizeMode(ContainmentView::SizeRootObjectToView); } ContainmentView::~ContainmentView() { delete d; } Plasma::Corona *ContainmentView::corona() const { return d->corona; } KConfigGroup ContainmentView::config() const { if (!containment()) { return KConfigGroup(); } KConfigGroup views(KSharedConfig::openConfig(), "PlasmaContainmentViews"); return KConfigGroup(&views, QString::number(containment()->screen())); } void ContainmentView::setContainment(Plasma::Containment *cont) { d->setContainment(cont); } Plasma::Containment *ContainmentView::containment() const { return d->containment; } void ContainmentView::setLocation(Plasma::Types::Location location) { d->containment->setLocation(location); } Plasma::Types::Location ContainmentView::location() const { return d->location(); } Plasma::Types::FormFactor ContainmentView::formFactor() const { return d->formFactor(); } QRectF ContainmentView::screenGeometry() { return screen()->geometry(); } void ContainmentView::showConfigurationInterface(Plasma::Applet *applet) { d->showConfigurationInterface(applet); } } #include "moc_containmentview.cpp" diff --git a/src/plasmaquick/packageurlinterceptor.cpp b/src/plasmaquick/packageurlinterceptor.cpp index 4aa5c3ad2..6302c5ab5 100644 --- a/src/plasmaquick/packageurlinterceptor.cpp +++ b/src/plasmaquick/packageurlinterceptor.cpp @@ -1,170 +1,172 @@ /* * Copyright 2013 Marco Martin * * 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 "packageurlinterceptor.h" #include #include #include #include #include #include +#include +#include #include namespace PlasmaQuick { class PackageUrlInterceptorPrivate { public: - PackageUrlInterceptorPrivate(QQmlEngine *engine, const Plasma::Package &p) + PackageUrlInterceptorPrivate(QQmlEngine *engine, const KPackage::Package &p) : package(p), engine(engine) { } - Plasma::Package package; + KPackage::Package package; QStringList allowedPaths; QQmlEngine *engine; //FIXME: those are going to be stuffed here and stay.. // they should probably be removed when the last applet of that type is removed - static QHash s_packages; + static QHash s_packages; }; -QHash PackageUrlInterceptorPrivate::s_packages = QHash(); +QHash PackageUrlInterceptorPrivate::s_packages = QHash(); -PackageUrlInterceptor::PackageUrlInterceptor(QQmlEngine *engine, const Plasma::Package &p) +PackageUrlInterceptor::PackageUrlInterceptor(QQmlEngine *engine, const KPackage::Package &p) : QQmlAbstractUrlInterceptor(), d(new PackageUrlInterceptorPrivate(engine, p)) { //d->allowedPaths << d->engine->importPathList(); } PackageUrlInterceptor::~PackageUrlInterceptor() { delete d; } void PackageUrlInterceptor::addAllowedPath(const QString &path) { d->allowedPaths << path; } void PackageUrlInterceptor::removeAllowedPath(const QString &path) { d->allowedPaths.removeAll(path); } QStringList PackageUrlInterceptor::allowedPaths() const { return d->allowedPaths; } QUrl PackageUrlInterceptor::intercept(const QUrl &path, QQmlAbstractUrlInterceptor::DataType type) { //qDebug() << "Intercepted URL:" << path << type; QString pkgRoot; - Plasma::Package package; + KPackage::Package package; if (d->package.isValid()) { package = d->package; } else { foreach (const QString &base, QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)) { pkgRoot = QFileInfo(base + "/plasma/plasmoids/").canonicalFilePath(); if (!pkgRoot.isEmpty() && path.path().startsWith(pkgRoot)) { const QString pkgName = path.path().mid(pkgRoot.length() + 1).split('/').first(); if (PackageUrlInterceptorPrivate::s_packages.contains(pkgName)) { package = PackageUrlInterceptorPrivate::s_packages.value(pkgName); } else { - package = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/Applet")); + package = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/Applet")).kPackage(); package.setPath(pkgName); PackageUrlInterceptorPrivate::s_packages[pkgName] = package; } break; } } } if (!package.isValid()) { return path; } if (d->package.isValid() && path.scheme() == QStringLiteral("plasmapackage")) { //FIXME: this is incorrect but works around a bug in qml in resolution of urls of qmldir files if (type == QQmlAbstractUrlInterceptor::QmldirFile) { return QUrl(d->package.filePath(0, path.path())); } else { return QUrl::fromLocalFile(d->package.filePath(0, path.path())); } } //TODO: security: permission for remote urls if (!path.isLocalFile()) { return path; } //if is just a normal string, no qml file was asked, allow it if (type == QQmlAbstractUrlInterceptor::UrlString) { return path; } //asked a file inside a package: let's rewrite the url! if (path.path().startsWith(package.path())) { //qDebug() << "Found URL in package" << path; //tries to isolate the relative path asked relative to the contentsPrefixPath: like ui/foo.qml QString relativePath; foreach (const QString &prefix, package.contentsPrefixPaths()) { QString root = package.path() + prefix; if (path.path().startsWith(root)) { //obtain a string in the form ui/foo/bar/baz.qml relativePath = path.path().mid(root.length()); break; } } //should never happen Q_ASSERT(!relativePath.isEmpty()); QStringList components = relativePath.split(QLatin1Char('/')); //a path with less than 2 items should ever happen Q_ASSERT(components.count() >= 2); components.pop_front(); //obtain a string in the form foo/bar/baz.qml: ui/ gets discarded const QString &filename = components.join(QStringLiteral("/")); QUrl ret = QUrl::fromLocalFile(package.filePath(prefixForType(type, filename), filename)); //qDebug() << "Returning" << ret; if (ret.path().isEmpty()) { return path; } return ret; //forbid to load random absolute paths } return path; } } diff --git a/src/plasmaquick/packageurlinterceptor.h b/src/plasmaquick/packageurlinterceptor.h index bbaeb709a..d52c0f443 100644 --- a/src/plasmaquick/packageurlinterceptor.h +++ b/src/plasmaquick/packageurlinterceptor.h @@ -1,95 +1,95 @@ /* * Copyright 2013 Marco Martin * * 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 PACKAGEURLINTERCEPTOR_H #define PACKAGEURLINTERCEPTOR_H #include #include -#include +#include // // W A R N I N G // ------------- // // This file is not part of the public Plasma API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // class QQmlEngine; namespace PlasmaQuick { class PackageUrlInterceptorPrivate; //depends from https://codereview.qt-project.org/#change,65626 class PLASMAQUICK_EXPORT PackageUrlInterceptor: public QQmlAbstractUrlInterceptor { public: - PackageUrlInterceptor(QQmlEngine *engine, const Plasma::Package &p); + PackageUrlInterceptor(QQmlEngine *engine, const KPackage::Package &p); virtual ~PackageUrlInterceptor(); void addAllowedPath(const QString &path); void removeAllowedPath(const QString &path); QStringList allowedPaths() const; QUrl intercept(const QUrl &path, QQmlAbstractUrlInterceptor::DataType type) Q_DECL_OVERRIDE; static inline QByteArray prefixForType(QQmlAbstractUrlInterceptor::DataType type, const QString &fileName) { switch (type) { case QQmlAbstractUrlInterceptor::QmlFile: return QByteArray("ui"); case QQmlAbstractUrlInterceptor::JavaScriptFile: return QByteArray("scripts"); default: break; } //failed by type, let's try by extension const QString &extension = fileName.mid(fileName.lastIndexOf(QLatin1Char('.')) + 1).toLower(); if (extension == QStringLiteral("svg") || extension == QStringLiteral("svgz") || extension == QStringLiteral("png") || extension == QStringLiteral("gif") || extension == QStringLiteral("jpg") || extension == QStringLiteral("jpeg")) { return QByteArray("images"); //FIXME: are those necessary? are they *always* catched by type? } else if (extension == QStringLiteral("js")) { return QByteArray("scripts"); } else if (extension == QStringLiteral("qml")) { return QByteArray("ui"); //everything else, throw it in "data" } else { return QByteArray("data"); } } private: PackageUrlInterceptorPrivate *const d; }; } #endif diff --git a/src/plasmaquick/private/appletquickitem_p.h b/src/plasmaquick/private/appletquickitem_p.h index ffd2bf269..476eb25fe 100644 --- a/src/plasmaquick/private/appletquickitem_p.h +++ b/src/plasmaquick/private/appletquickitem_p.h @@ -1,114 +1,116 @@ /* * Copyright 2014 Marco Martin * * 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 APPLETQUICKITEM_P_H #define APPLETQUICKITEM_P_H #include -#include +#include #include +#include // // W A R N I N G // ------------- // // This file is not part of the public Plasma API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // + namespace Plasma { class Applet; } namespace KDeclarative { class QmlObject; } namespace PlasmaQuick { class AppletQuickItem; class AppletQuickItemPrivate { public: AppletQuickItemPrivate(Plasma::Applet *a, AppletQuickItem *item); void init(); QQuickItem *createCompactRepresentationItem(); QQuickItem *createFullRepresentationItem(); QQuickItem *createCompactRepresentationExpanderItem(); //look into item, and return the Layout attached property, if found void connectLayoutAttached(QObject *item); void propagateSizeHint(const QByteArray &layoutProperty); //handlers of Layout signals, private slots void compactRepresentationCheck(); void minimumWidthChanged(); void minimumHeightChanged(); void preferredWidthChanged(); void preferredHeightChanged(); void maximumWidthChanged(); void maximumHeightChanged(); void fillWidthChanged(); void fillHeightChanged(); AppletQuickItem *q; int switchWidth; int switchHeight; QPointer compactRepresentation; QPointer fullRepresentation; QPointer preferredRepresentation; QPointer compactRepresentationExpander; QPointer compactRepresentationItem; QPointer fullRepresentationItem; QPointer compactRepresentationExpanderItem; QPointer currentRepresentationItem; QPointer testItem; //Attached layout objects: own and the representation's one QPointer representationLayout; QPointer ownLayout; Plasma::Applet *applet; KDeclarative::QmlObject *qmlObject; - Plasma::Package appletPackage; - Plasma::Package coronaPackage; - Plasma::Package containmentPackage; + KPackage::Package appletPackage; + KPackage::Package coronaPackage; + KPackage::Package containmentPackage; bool expanded : 1; bool activationTogglesExpanded : 1; static QHash s_rootObjects; }; } #endif diff --git a/src/plasmaquick/view.cpp b/src/plasmaquick/view.cpp index ca6ae6f2b..108f06ffd 100644 --- a/src/plasmaquick/view.cpp +++ b/src/plasmaquick/view.cpp @@ -1,278 +1,279 @@ /* * Copyright 2013 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, 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 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 "view.h" #include "configview.h" #include #include #include #include #include #include #include "plasma/pluginloader.h" #include #include namespace PlasmaQuick { class ViewPrivate { public: ViewPrivate(Plasma::Corona *corona, View *view); ~ViewPrivate(); void setContainment(Plasma::Containment *cont); Plasma::Types::FormFactor formFactor() const; Plasma::Types::Location location() const; void showConfigurationInterface(Plasma::Applet *applet); void updateDestroyed(bool destroyed); View *q; friend class View; Plasma::Corona *corona; QPointer containment; QPointer configView; }; ViewPrivate::ViewPrivate(Plasma::Corona *cor, View *view) : q(view), corona(cor) { } ViewPrivate::~ViewPrivate() { } void ViewPrivate::setContainment(Plasma::Containment *cont) { if (containment == cont) { return; } Plasma::Types::Location oldLoc = location(); Plasma::Types::FormFactor oldForm = formFactor(); if (containment) { QObject::disconnect(containment, 0, q, 0); QObject *oldGraphicObject = containment->property("_plasma_graphicObject").value(); if (oldGraphicObject) { // qDebug() << "Old graphics Object:" << oldGraphicObject << "Old containment" << containment.data(); //make sure the graphic object won't die with us //FIXME:we need a way to reparent to *NO* graphics item, but this makes Qt crash oldGraphicObject->setParent(containment); } containment->reactToScreenChange(); } containment = cont; if (oldLoc != location()) { emit q->locationChanged(location()); } if (oldForm != formFactor()) { emit q->formFactorChanged(formFactor()); } emit q->containmentChanged(); if (cont) { cont->reactToScreenChange(); QObject::connect(cont, &Plasma::Containment::locationChanged, q, &View::locationChanged); QObject::connect(cont, &Plasma::Containment::formFactorChanged, q, &View::formFactorChanged); QObject::connect(cont, &Plasma::Containment::configureRequested, q, &View::showConfigurationInterface); QObject::connect(cont, SIGNAL(destroyedChanged(bool)), q, SLOT(updateDestroyed(bool))); if (cont->containmentType() == Plasma::Types::PanelContainment || cont->containmentType() == Plasma::Types::CustomPanelContainment) { q->setVisible(!cont->destroyed() && cont->isUiReady()); } } else { return; } QQuickItem *graphicObject = qobject_cast(containment->property("_plasma_graphicObject").value()); if (graphicObject) { // qDebug() << "using as graphic containment" << graphicObject << containment.data(); //by resizing before adding, it will avoid some resizes in most cases graphicObject->setProperty("width", q->width()); graphicObject->setProperty("height", q->height()); graphicObject->setParentItem(q->rootObject()); if (q->rootObject()) { q->rootObject()->setProperty("containment", QVariant::fromValue(graphicObject)); QObject *wpGraphicObject = containment->property("wallpaperGraphicsObject").value(); if (wpGraphicObject) { q->rootObject()->setProperty("wallpaper", QVariant::fromValue(wpGraphicObject)); } } else { qWarning() << "Could not set containment property on rootObject"; } } else { qWarning() << "Containment graphic object not valid"; } } Plasma::Types::Location ViewPrivate::location() const { if (!containment) { return Plasma::Types::Desktop; } return containment->location(); } Plasma::Types::FormFactor ViewPrivate::formFactor() const { if (!containment) { return Plasma::Types::Planar; } return containment->formFactor(); } void ViewPrivate::showConfigurationInterface(Plasma::Applet *applet) { if (configView) { if (configView->applet() != applet) { configView->hide(); configView->deleteLater(); } else { configView->requestActivate(); return; } } if (!applet || !applet->containment()) { return; } configView = new ConfigView(applet); configView->init(); configView->show(); } void ViewPrivate::updateDestroyed(bool destroyed) { q->setVisible(!destroyed); } View::View(Plasma::Corona *corona, QWindow *parent) : QQuickView(parent), d(new ViewPrivate(corona, this)) { setColor(Qt::transparent); QObject::connect(screen(), &QScreen::geometryChanged, this, &View::screenGeometryChanged); - if (corona->kPackage().isValid()) { - PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(engine(), corona->package()); + const auto pkg = corona->kPackage(); + if (pkg.isValid()) { + PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(engine(), pkg); engine()->setUrlInterceptor(interceptor); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); //binds things like kconfig and icons - kdeclarative.setTranslationDomain("plasma_shell_" + corona->kPackage().metadata().pluginId()); + kdeclarative.setTranslationDomain("plasma_shell_" + pkg.metadata().pluginId()); kdeclarative.setupBindings(); } else { qWarning() << "Invalid home screen package"; } //Force QtQuickControls to use the "Plasma" style for this engine. //this way is possible to mix QtQuickControls and plasma components in applets //while still having the desktop style in configuration dialogs QQmlComponent c(engine()); c.setData("import QtQuick 2.1\n\ import QtQuick.Controls 1.0\n\ import QtQuick.Controls.Private 1.0\n \ Item {\ Component.onCompleted: {\ Settings.styleName = \"Plasma\";\ }\ }", QUrl()); QObject *o = c.create(); o->deleteLater(); setResizeMode(View::SizeRootObjectToView); } View::~View() { delete d; } Plasma::Corona *View::corona() const { return d->corona; } KConfigGroup View::config() const { if (!containment()) { return KConfigGroup(); } KConfigGroup views(KSharedConfig::openConfig(), "PlasmaViews"); return KConfigGroup(&views, QString::number(containment()->screen())); } void View::setContainment(Plasma::Containment *cont) { d->setContainment(cont); } Plasma::Containment *View::containment() const { return d->containment; } void View::setLocation(Plasma::Types::Location location) { d->containment->setLocation(location); } Plasma::Types::Location View::location() const { return d->location(); } Plasma::Types::FormFactor View::formFactor() const { return d->formFactor(); } QRectF View::screenGeometry() { return screen()->geometry(); } void View::showConfigurationInterface(Plasma::Applet *applet) { d->showConfigurationInterface(applet); } } #include "moc_view.cpp" diff --git a/src/scriptengines/qml/plasmoid/containmentinterface.cpp b/src/scriptengines/qml/plasmoid/containmentinterface.cpp index 4658ac38a..c024e2596 100644 --- a/src/scriptengines/qml/plasmoid/containmentinterface.cpp +++ b/src/scriptengines/qml/plasmoid/containmentinterface.cpp @@ -1,1222 +1,1220 @@ /* * Copyright 2008 Chani Armitage * Copyright 2008, 2009 Aaron Seigo * Copyright 2010 Marco Martin * * 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 "containmentinterface.h" #include "wallpaperinterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PLASMA_NO_KIO #include "kio/jobclasses.h" // for KIO::JobFlags #include "kio/job.h" #include "kio/scheduler.h" #include #endif #include #include #include #include #include #include #include #include "kdeclarative/configpropertymap.h" #include ContainmentInterface::ContainmentInterface(DeclarativeAppletScript *parent, const QVariantList &args) : AppletInterface(parent, args), m_wallpaperInterface(0), m_activityInfo(0), m_wheelDelta(0), m_editMode(false) { m_containment = static_cast(appletScript()->applet()->containment()); setAcceptedMouseButtons(Qt::AllButtons); connect(m_containment.data(), &Plasma::Containment::appletRemoved, this, &ContainmentInterface::appletRemovedForward); connect(m_containment.data(), &Plasma::Containment::appletAdded, this, &ContainmentInterface::appletAddedForward); if (!m_appletInterfaces.isEmpty()) { emit appletsChanged(); } } void ContainmentInterface::init() { if (qmlObject()->rootObject()) { return; } m_activityInfo = new KActivities::Info(m_containment->activity(), this); connect(m_activityInfo, &KActivities::Info::nameChanged, this, &ContainmentInterface::activityNameChanged); emit activityNameChanged(); AppletInterface::init(); //Create the ToolBox if (m_containment) { KConfigGroup defaults; if (m_containment->containmentType() == Plasma::Types::DesktopContainment) { defaults = KConfigGroup(KSharedConfig::openConfig(m_containment->corona()->kPackage().filePath("defaults")), "Desktop"); } else if (m_containment->containmentType() == Plasma::Types::PanelContainment) { defaults = KConfigGroup(KSharedConfig::openConfig(m_containment->corona()->kPackage().filePath("defaults")), "Panel"); } if (defaults.isValid()) { KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage("KPackage/GenericQML"); pkg.setDefaultPackageRoot("plasma/packages"); if (defaults.isValid()) { pkg.setPath(defaults.readEntry("ToolBox", "org.kde.desktoptoolbox")); } else { pkg.setPath("org.kde.desktoptoolbox"); } PlasmaQuick::PackageUrlInterceptor *interceptor = dynamic_cast(qmlObject()->engine()->urlInterceptor()); if (interceptor) { interceptor->addAllowedPath(pkg.path()); } if (pkg.metadata().isValid() && !pkg.metadata().isHidden()) { if (pkg.isValid()) { QObject *containmentGraphicObject = qmlObject()->rootObject(); QVariantHash toolboxProperties; toolboxProperties["parent"] = QVariant::fromValue(this); QObject *toolBoxObject = qmlObject()->createObjectFromSource(QUrl::fromLocalFile(pkg.filePath("mainscript")), 0, toolboxProperties); if (toolBoxObject && containmentGraphicObject) { containmentGraphicObject->setProperty("toolBox", QVariant::fromValue(toolBoxObject)); } } else { qWarning() << "Could not load toolbox package." << pkg.path(); } } else { qWarning() << "Toolbox not loading, toolbox package is either invalid or disabled."; } } } //set parent, both as object hierarchically and visually //do this only for containments, applets will do it in compactrepresentationcheck if (qmlObject()->rootObject()) { qmlObject()->rootObject()->setProperty("parent", QVariant::fromValue(this)); //set anchors QQmlExpression expr(qmlObject()->engine()->rootContext(), qmlObject()->rootObject(), QStringLiteral("parent")); QQmlProperty prop(qmlObject()->rootObject(), QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); } if (!m_containment->wallpaper().isEmpty()) { loadWallpaper(); } connect(m_containment.data(), &Plasma::Containment::activityChanged, this, &ContainmentInterface::activityChanged); connect(m_containment.data(), &Plasma::Containment::activityChanged, [ = ]() { delete m_activityInfo; m_activityInfo = new KActivities::Info(m_containment->activity(), this); connect(m_activityInfo, &KActivities::Info::nameChanged, this, &ContainmentInterface::activityNameChanged); emit activityNameChanged(); }); connect(m_containment.data(), &Plasma::Containment::wallpaperChanged, this, &ContainmentInterface::loadWallpaper); connect(m_containment.data(), &Plasma::Containment::containmentTypeChanged, this, &ContainmentInterface::containmentTypeChanged); connect(m_containment.data()->actions(), &KActionCollection::inserted, this, &ContainmentInterface::actionsChanged); connect(m_containment.data()->actions(), &KActionCollection::removed, this, &ContainmentInterface::actionsChanged); if (m_containment->corona()) { connect(m_containment->corona(), &Plasma::Corona::availableScreenRegionChanged, this, &ContainmentInterface::availableScreenRegionChanged); connect(m_containment->corona(), &Plasma::Corona::availableScreenRectChanged, this, &ContainmentInterface::availableScreenRectChanged); } } QList ContainmentInterface::applets() { return m_appletInterfaces; } Plasma::Types::ContainmentType ContainmentInterface::containmentType() const { return appletScript()->containmentType(); } void ContainmentInterface::setContainmentType(Plasma::Types::ContainmentType type) { appletScript()->setContainmentType(type); } Plasma::Applet *ContainmentInterface::createApplet(const QString &plugin, const QVariantList &args, const QPoint &pos) { return createApplet(plugin, args, QRectF(pos, QSize())); } Plasma::Applet *ContainmentInterface::createApplet(const QString &plugin, const QVariantList &args, const QRectF &geom) { //HACK //This is necessary to delay the appletAdded signal (of containmentInterface) AFTER the applet graphics object has been created blockSignals(true); Plasma::Applet *applet = m_containment->createApplet(plugin, args); if (applet) { QQuickItem *appletGraphicObject = applet->property("_plasma_graphicObject").value(); //invalid applet? if (!appletGraphicObject) { blockSignals(false); return applet; } if (geom.width() > 0 && geom.height() > 0) { appletGraphicObject->setSize(geom.size()); } blockSignals(false); emit appletAdded(appletGraphicObject, geom.x(), geom.y()); emit appletsChanged(); } else { blockSignals(false); } return applet; } void ContainmentInterface::setAppletArgs(Plasma::Applet *applet, const QString &mimetype, const QString &data) { if (!applet) { return; } AppletInterface *appletInterface = applet->property("_plasma_graphicObject").value(); if (appletInterface) { emit appletInterface->externalData(mimetype, data); } } QObject *ContainmentInterface::containmentAt(int x, int y) { QObject *desktop = nullptr; foreach (Plasma::Containment *c, m_containment->corona()->containments()) { ContainmentInterface *contInterface = c->property("_plasma_graphicObject").value(); if (contInterface && contInterface->isVisible()) { QWindow *w = contInterface->window(); if (w && w->geometry().contains(QPoint(window()->x(), window()->y()) + QPoint(x, y))) { if (c->containmentType() == Plasma::Types::CustomEmbeddedContainment) { continue; } if (c->containmentType() == Plasma::Types::DesktopContainment) { desktop = contInterface; } else { return contInterface; } } } } return desktop; } void ContainmentInterface::addApplet(AppletInterface *applet, int x, int y) { if (!applet || applet->applet()->containment() == m_containment) { return; } blockSignals(true); m_containment->addApplet(applet->applet()); blockSignals(false); emit appletAdded(applet, x, y); } QPointF ContainmentInterface::mapFromApplet(AppletInterface *applet, int x, int y) { if (!applet->window() || !window()) { return QPointF(); } //x,y in absolute screen coordinates of current view QPointF pos = applet->mapToScene(QPointF(x, y)); pos = QPointF(pos + applet->window()->geometry().topLeft()); //return the coordinate in the relative view's coords return pos - window()->geometry().topLeft(); } QPointF ContainmentInterface::mapToApplet(AppletInterface *applet, int x, int y) { if (!applet->window() || !window()) { return QPointF(); } //x,y in absolute screen coordinates of current view QPointF pos(x, y); pos = QPointF(pos + window()->geometry().topLeft()); //the coordinate in the relative view's coords pos = pos - applet->window()->geometry().topLeft(); //make it relative to applet coords return pos - applet->mapToScene(QPointF(0, 0)); } QPointF ContainmentInterface::adjustToAvailableScreenRegion(int x, int y, int w, int h) const { QRegion reg; int screenId = screen(); if (screenId > -1 && m_containment->corona()) { reg = m_containment->corona()->availableScreenRegion(screenId); } if (!reg.isEmpty()) { //make it relative QRect geometry = m_containment->corona()->screenGeometry(screenId); reg.translate(- geometry.topLeft()); } else { reg = QRect(0, 0, width(), height()); } const QRect rect(qBound(reg.boundingRect().left(), x, reg.boundingRect().right() + 1 - w), qBound(reg.boundingRect().top(), y, reg.boundingRect().bottom() + 1 - h), w, h); const QRectF ar = availableScreenRect(); QRect tempRect(rect); // in the case we are in the topleft quadrant // * see if the passed rect is completely in the region, if yes, return // * otherwise, try to move it horizontally to the screenrect x // * if now fits, return // * if fail, move vertically // * as last resort, move horizontally and vertically // top left corner if (rect.center().x() <= ar.center().x() && rect.center().y() <= ar.center().y()) { //QRegion::contains doesn't do what it would suggest, so do reg.intersected(rect) != rect instead if (reg.intersected(rect) != rect) { tempRect = QRect(qMax(rect.left(), (int)ar.left()), rect.top(), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(rect.left(), qMax(rect.top(), (int)ar.top()), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(qMax(rect.left(), (int)ar.left()), qMax(rect.top(), (int)ar.top()), w, h); return tempRect.topLeft(); } else { return rect.topLeft(); } //bottom left corner } else if (rect.center().x() <= ar.center().x() && rect.center().y() > ar.center().y()) { if (reg.intersected(rect) != rect) { tempRect = QRect(qMax(rect.left(), (int)ar.left()), rect.top(), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(rect.left(), qMin(rect.top(), (int)(ar.bottom() + 1 - h)), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(qMax(rect.left(), (int)ar.left()), qMin(rect.top(), (int)(ar.bottom() + 1 - h)), w, h); return tempRect.topLeft(); } else { return rect.topLeft(); } //top right corner } else if (rect.center().x() > ar.center().x() && rect.center().y() <= ar.center().y()) { if (reg.intersected(rect) != rect) { tempRect = QRect(qMin(rect.left(), (int)(ar.right() + 1 - w)), rect.top(), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(rect.left(), qMax(rect.top(), (int)ar.top()), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(qMin(rect.left(), (int)(ar.right() + 1 - w)), qMax(rect.top(), (int)ar.top()), w, h); return tempRect.topLeft(); } else { return rect.topLeft(); } //bottom right corner } else if (rect.center().x() > ar.center().x() && rect.center().y() > ar.center().y()) { if (reg.intersected(rect) != rect) { tempRect = QRect(qMin(rect.left(), (int)(ar.right() + 1 - w)), rect.top(), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(rect.left(), qMin(rect.top(), (int)(ar.bottom() + 1 - h)), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(qMin(rect.left(), (int)(ar.right() + 1 - w)), qMin(rect.top(), (int)(ar.bottom() + 1 - h)), w, h); return tempRect.topLeft(); } else { return rect.topLeft(); } } return rect.topLeft(); } bool ContainmentInterface::isEditMode() const { return m_editMode; } void ContainmentInterface::setEditMode(bool edit) { if (edit == m_editMode) { return; } m_editMode = edit; emit editModeChanged(); } void ContainmentInterface::processMimeData(QObject *mimeDataProxy, int x, int y, KIO::DropJob *dropJob) { QMimeData* mime = qobject_cast(mimeDataProxy); if (mime) { processMimeData(mime, x, y, dropJob); } else { processMimeData(mimeDataProxy->property("mimeData").value(), x, y, dropJob); } } void ContainmentInterface::processMimeData(QMimeData *mimeData, int x, int y, KIO::DropJob *dropJob) { if (!mimeData) { return; } //const QMimeData *mimeData = data; qDebug() << "Arrived mimeData" << mimeData->urls() << mimeData->formats() << "at" << x << ", " << y; if (mimeData->hasFormat(QStringLiteral("text/x-plasmoidservicename"))) { QString data = mimeData->data(QStringLiteral("text/x-plasmoidservicename")); const QStringList appletNames = data.split('\n', QString::SkipEmptyParts); foreach (const QString &appletName, appletNames) { qDebug() << "adding" << appletName; metaObject()->invokeMethod(this, "createApplet", Qt::QueuedConnection, Q_ARG(QString, appletName), Q_ARG(QVariantList, QVariantList()), Q_ARG(QRectF, QRectF(x, y, -1, -1))); } } else if (mimeData->hasUrls()) { //TODO: collect the mimetypes of available script engines and offer // to create widgets out of the matching URLs, if any const QList urls = KUrlMimeData::urlsFromMimeData(mimeData); foreach (const QUrl &url, urls) { #ifndef PLASMA_NO_KIO QMimeDatabase db; const QMimeType &mime = db.mimeTypeForUrl(url); QString mimeName = mime.name(); QVariantList args; args << url.url(); qDebug() << "can decode" << mimeName << args; // It may be a directory or a file, let's stat KIO::JobFlags flags = KIO::HideProgressInfo; KIO::MimetypeJob *job = KIO::mimetype(url, flags); m_dropPoints[job] = QPoint(x, y); QObject::connect(job, SIGNAL(result(KJob*)), this, SLOT(dropJobResult(KJob*))); QObject::connect(job, SIGNAL(mimetype(KIO::Job*,QString)), this, SLOT(mimeTypeRetrieved(KIO::Job*,QString))); if (dropJob) { m_dropJobs[job] = dropJob; } else { QMenu *choices = new QMenu(i18n("Content dropped")); if (choices->winId()) { choices->windowHandle()->setTransientParent(window()); } choices->addAction(QIcon::fromTheme(QStringLiteral("process-working")), i18n("Fetching file type...")); choices->popup(window() ? window()->mapToGlobal(QPoint(x, y)) : QPoint(x, y)); m_dropMenus[job] = choices; } #endif } } else { QStringList formats = mimeData->formats(); - QHash seenPlugins; + QHash seenPlugins; QHash pluginFormats; foreach (const QString &format, formats) { - KPluginInfo::List plugins = Plasma::PluginLoader::self()->listAppletInfoForMimeType(format); + const auto plugins = Plasma::PluginLoader::self()->listAppletMetaDataForMimeType(format); - foreach (const KPluginInfo &plugin, plugins) { - if (seenPlugins.contains(plugin.pluginName())) { + foreach (const auto &plugin, plugins) { + if (seenPlugins.contains(plugin.pluginId())) { continue; } - seenPlugins.insert(plugin.pluginName(), plugin); - pluginFormats.insert(plugin.pluginName(), format); + seenPlugins.insert(plugin.pluginId(), plugin); + pluginFormats.insert(plugin.pluginId(), format); } } //qDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values(); QString selectedPlugin; if (seenPlugins.isEmpty()) { // do nothing //directly create if only one offer only if the conteinment didn't pass an existing plugin } else if (seenPlugins.count() == 1) { selectedPlugin = seenPlugins.constBegin().key(); Plasma::Applet *applet = createApplet(selectedPlugin, QVariantList(), QRect(x, y, -1, -1)); setAppletArgs(applet, pluginFormats[selectedPlugin], mimeData->data(pluginFormats[selectedPlugin])); } else { QMenu *choices = nullptr; if (!dropJob) { choices = new QMenu(); if (choices->winId()) { choices->windowHandle()->setTransientParent(window()); } } QList extraActions; QHash actionsToPlugins; - foreach (const KPluginInfo &info, seenPlugins) { + foreach (const auto &info, seenPlugins) { QAction *action; - if (!info.icon().isEmpty()) { - action = new QAction(QIcon::fromTheme(info.icon()), info.name(), nullptr); + if (!info.iconName().isEmpty()) { + action = new QAction(QIcon::fromTheme(info.iconName()), info.name(), nullptr); } else { action = new QAction(info.name(), nullptr); } extraActions << action; if (choices) { choices->addAction(action); } - action->setData(info.pluginName()); + action->setData(info.pluginId()); connect(action, &QAction::triggered, this, [this, x, y, mimeData, action]() { const QString selectedPlugin = action->data().toString(); Plasma::Applet *applet = createApplet(selectedPlugin, QVariantList(), QRect(x, y, -1, -1)); setAppletArgs(applet, selectedPlugin, mimeData->data(selectedPlugin)); }); - actionsToPlugins.insert(action, info.pluginName()); + actionsToPlugins.insert(action, info.pluginId()); } //if the menu was created by ourselves, delete it if (choices) { QAction *choice = choices->exec(window() ? window()->mapToGlobal(QPoint(x, y)) : QPoint(x, y)); delete choices; } else { Q_ASSERT(dropJob); dropJob->setApplicationActions(extraActions); } } } } void ContainmentInterface::clearDataForMimeJob(KIO::Job *job) { #ifndef PLASMA_NO_KIO QObject::disconnect(job, 0, this, 0); m_dropPoints.remove(job); QMenu *choices = m_dropMenus.take(job); m_dropJobs.remove(job); job->kill(); #endif // PLASMA_NO_KIO } void ContainmentInterface::dropJobResult(KJob *job) { #ifndef PLASMA_NO_KIO if (job->error()) { qDebug() << "ERROR" << job->error() << ' ' << job->errorString(); } #endif // PLASMA_NO_KIO } void ContainmentInterface::mimeTypeRetrieved(KIO::Job *job, const QString &mimetype) { #ifndef PLASMA_NO_KIO qDebug() << "Mimetype Job returns." << mimetype; KIO::TransferJob *tjob = dynamic_cast(job); if (!tjob) { qDebug() << "job should be a TransferJob, but isn't"; clearDataForMimeJob(job); return; } - KPluginInfo::List appletList = Plasma::PluginLoader::self()->listAppletInfoForUrl(tjob->url()); + QList appletList = Plasma::PluginLoader::self()->listAppletMetaDataForUrl(tjob->url()); if (mimetype.isEmpty() && appletList.isEmpty()) { clearDataForMimeJob(job); qDebug() << "No applets found matching the url (" << tjob->url() << ") or the mimetype (" << mimetype << ")"; return; } else { QPoint posi; // will be overwritten with the event's position if (m_dropPoints.contains(tjob)) { posi = m_dropPoints.value(tjob); qDebug() << "Received a suitable dropEvent at" << posi; } else { qDebug() << "Bailing out. Cannot find associated dropEvent related to the TransferJob"; clearDataForMimeJob(job); return; } QMenu *choices = m_dropMenus.value(tjob); QList dropActions; KIO::DropJob *dropJob = m_dropJobs.value(tjob); if (!choices && !dropJob) { qDebug() << "Bailing out. No QMenu or drop job found for this job."; clearDataForMimeJob(job); return; } qDebug() << "Creating menu for:" << mimetype << posi; - appletList << Plasma::PluginLoader::self()->listAppletInfoForMimeType(mimetype); + appletList << Plasma::PluginLoader::self()->listAppletMetaDataForMimeType(mimetype); - KPluginInfo::List wallpaperList; + QList wallpaperList; if (m_containment->containmentType() != Plasma::Types::PanelContainment && m_containment->containmentType() != Plasma::Types::CustomPanelContainment) { if (m_wallpaperInterface && m_wallpaperInterface->supportsMimetype(mimetype)) { - wallpaperList << m_wallpaperInterface->package().metadata(); + wallpaperList << m_wallpaperInterface->kPackage().metadata(); } else { - wallpaperList = WallpaperInterface::listWallpaperInfoForMimetype(mimetype); + wallpaperList = WallpaperInterface::listWallpaperMetadataForMimetype(mimetype); } } const bool isPlasmaPackage = (mimetype == QLatin1String("application/x-plasma")); if (!appletList.isEmpty() || !wallpaperList.isEmpty() || isPlasmaPackage) { QAction *installPlasmaPackageAction = nullptr; if (isPlasmaPackage) { if (choices) { choices->addSection(i18n("Plasma Package")); installPlasmaPackageAction = choices->addAction(QIcon::fromTheme(QStringLiteral("application-x-plasma")), i18n("Install")); } else { QAction *action = new QAction(i18n("Plasma Package"), nullptr); action->setSeparator(true); dropActions << action; installPlasmaPackageAction = new QAction(QIcon::fromTheme(QStringLiteral("application-x-plasma")), i18n("Install"), nullptr); Q_ASSERT(dropJob); dropActions << installPlasmaPackageAction; dropJob->setApplicationActions(dropActions); } const QString &packagePath = tjob->url().toLocalFile(); connect(installPlasmaPackageAction, &QAction::triggered, this, [this, tjob, posi, packagePath]() { using namespace KPackage; PackageStructure *structure = PackageLoader::self()->loadPackageStructure(QStringLiteral("Plasma/Applet")); Package package(structure); KJob *installJob = package.update(packagePath); connect(installJob, &KJob::result, this, [this, packagePath, structure, posi](KJob *job) { auto fail = [job](const QString &text) { KNotification::event(QStringLiteral("plasmoidInstallationFailed"), i18n("Package Installation Failed"), text, QStringLiteral("dialog-error"), 0, KNotification::CloseOnTimeout, QStringLiteral("plasma_workspace")); }; // if the applet is already installed, just add it to the containment if (job->error() != KJob::NoError && job->error() != Package::PackageAlreadyInstalledError && job->error() != Package::NewerVersionAlreadyInstalledError) { fail(job->errorText()); return; } using namespace KPackage; Package package(structure); // TODO how can I get the path of the actual package? package.setPath(packagePath); // TODO how can I get the plugin id? Package::metadata() is deprecated if (!package.isValid() || !package.metadata().isValid()) { fail(i18n("The package you just dropped is invalid.")); return; } createApplet(package.metadata().pluginId(), QVariantList(), QRect(posi, QSize(-1,-1))); }); }); } if (choices) { choices->addSection(i18n("Widgets")); } else { QAction *action = new QAction(i18n("Widgets"), nullptr); action->setSeparator(true); dropActions << action; } - foreach (const KPluginInfo &info, appletList) { - qDebug() << info.name(); + foreach (const auto &info, appletList) { QAction *action; - if (!info.icon().isEmpty()) { - action = new QAction(QIcon::fromTheme(info.icon()), info.name(), nullptr); + if (!info.iconName().isEmpty()) { + action = new QAction(QIcon::fromTheme(info.iconName()), info.name(), nullptr); } else { action = new QAction(info.name(), nullptr); } if (choices) { choices->addAction(action); } dropActions << action; - action->setData(info.pluginName()); - qDebug() << info.pluginName(); + action->setData(info.pluginId()); const QUrl url = tjob->url(); connect(action, &QAction::triggered, this, [this, action, posi, mimetype, url]() { Plasma::Applet *applet = createApplet(action->data().toString(), QVariantList(), QRect(posi, QSize(-1,-1))); setAppletArgs(applet, mimetype, url.toString()); }); } { QAction *action = new QAction(i18n("Icon"), nullptr); if (choices) { choices->addAction(action); } dropActions << action; action->setData(QStringLiteral("org.kde.plasma.icon")); const QUrl url = tjob->url(); connect(action, &QAction::triggered, this, [this, action, posi, mimetype, url](){ Plasma::Applet *applet = createApplet(action->data().toString(), QVariantList(), QRect(posi, QSize(-1,-1))); setAppletArgs(applet, mimetype, url.toString()); }); } QHash actionsToWallpapers; if (!wallpaperList.isEmpty()) { if (choices) { choices->addSection(i18n("Wallpaper")); } else { QAction *action = new QAction(i18n("Wallpaper"), nullptr); action->setSeparator(true); dropActions << action; } - QMap sorted; - foreach (const KPluginInfo &info, appletList) { + QMap sorted; + foreach (const auto &info, appletList) { sorted.insert(info.name(), info); } - foreach (const KPluginInfo &info, wallpaperList) { + foreach (const KPluginMetaData &info, wallpaperList) { QAction *action; - if (!info.icon().isEmpty()) { - action = new QAction(QIcon::fromTheme(info.icon()), info.name(), nullptr); + if (!info.iconName().isEmpty()) { + action = new QAction(QIcon::fromTheme(info.iconName()), info.name(), nullptr); } else { action = new QAction(info.name(), nullptr); } if (choices) { choices->addAction(action); } dropActions << action; - actionsToWallpapers.insert(action, info.pluginName()); + actionsToWallpapers.insert(action, info.pluginId()); const QUrl url = tjob->url(); connect(action, &QAction::triggered, this, [this, action, url]() { //set wallpapery stuff if (m_wallpaperInterface && url.isValid()) { m_wallpaperInterface->setUrl(url); } }); } } } else { //case in which we created the menu ourselves, just the "fetching type entry, directly create the icon applet if (choices) { Plasma::Applet *applet = createApplet(QStringLiteral("org.kde.plasma.icon"), QVariantList(), QRect(posi, QSize(-1,-1))); setAppletArgs(applet, mimetype, tjob->url().toString()); } else { QAction *action; if (choices) { choices->addSection(i18n("Widgets")); action = choices->addAction(i18n("Icon")); } else { QAction *sep = new QAction(i18n("Widgets"), nullptr); sep->setSeparator(true); dropActions << sep; // we can at least create an icon as a link to the URL action = new QAction(i18n("Icon"), nullptr); dropActions << action; } const QUrl url = tjob->url(); connect(action, &QAction::triggered, this, [this, posi, mimetype, url](){ Plasma::Applet *applet = createApplet(QStringLiteral("org.kde.plasma.icon"), QVariantList(), QRect(posi, QSize(-1,-1))); setAppletArgs(applet, mimetype, url.toString()); }); } } if (choices) { // HACK If the QMenu becomes empty at any point after the "determining mimetype" // popup was shown, it self-destructs, does not matter if we call clear() or remove // the action manually, hence we remove the aforementioned item after we populated the menu choices->removeAction(choices->actions().at(0)); choices->exec(); } else { dropJob->setApplicationActions(dropActions); } clearDataForMimeJob(tjob); } #endif // PLASMA_NO_KIO } void ContainmentInterface::appletAddedForward(Plasma::Applet *applet) { if (!applet) { return; } AppletInterface *appletGraphicObject = applet->property("_plasma_graphicObject").value(); AppletInterface *contGraphicObject = m_containment->property("_plasma_graphicObject").value(); // qDebug() << "Applet added on containment:" << m_containment->title() << contGraphicObject // << "Applet: " << applet << applet->title() << appletGraphicObject; //applets can not have a graphic object if they don't have a script engine loaded //this can happen if they were loaded with an invalid metadata if (!appletGraphicObject) { return; } if (contGraphicObject) { appletGraphicObject->setProperty("visible", false); appletGraphicObject->setProperty("parent", QVariant::fromValue(contGraphicObject)); } m_appletInterfaces << appletGraphicObject; connect(appletGraphicObject, &QObject::destroyed, this, [this](QObject *obj) { m_appletInterfaces.removeAll(obj); }); emit appletAdded(appletGraphicObject, appletGraphicObject->m_positionBeforeRemoval.x(), appletGraphicObject->m_positionBeforeRemoval.y()); emit appletsChanged(); } void ContainmentInterface::appletRemovedForward(Plasma::Applet *applet) { AppletInterface *appletGraphicObject = applet->property("_plasma_graphicObject").value(); if (appletGraphicObject) { m_appletInterfaces.removeAll(appletGraphicObject); appletGraphicObject->m_positionBeforeRemoval = appletGraphicObject->mapToItem(this, QPointF()); } emit appletRemoved(appletGraphicObject); emit appletsChanged(); } void ContainmentInterface::loadWallpaper() { if (m_containment->containmentType() != Plasma::Types::DesktopContainment && m_containment->containmentType() != Plasma::Types::CustomContainment) { return; } if (!m_containment->wallpaper().isEmpty()) { delete m_wallpaperInterface; m_wallpaperInterface = new WallpaperInterface(this); m_wallpaperInterface->setZ(-1000); //Qml seems happier if the parent gets set in this way m_wallpaperInterface->setProperty("parent", QVariant::fromValue(this)); //set anchors QQmlExpression expr(qmlObject()->engine()->rootContext(), m_wallpaperInterface, QStringLiteral("parent")); QQmlProperty prop(m_wallpaperInterface, QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); m_containment->setProperty("wallpaperGraphicsObject", QVariant::fromValue(m_wallpaperInterface)); } else { if (m_wallpaperInterface) { m_wallpaperInterface->deleteLater(); m_wallpaperInterface = 0; } } } QString ContainmentInterface::activity() const { return m_containment->activity(); } QString ContainmentInterface::activityName() const { if (!m_activityInfo) { return QString(); } return m_activityInfo->name(); } QList ContainmentInterface::actions() const { //FIXME: giving directly a QList crashes QStringList actionOrder; actionOrder << QStringLiteral("add widgets") << QStringLiteral("manage activities") << QStringLiteral("remove") << QStringLiteral("lock widgets") << QStringLiteral("run associated application") << QStringLiteral("configure"); QHash orderedActions; //use a multimap to sort by action type QMultiMap actions; int i = 0; foreach (QAction *a, m_containment->actions()->actions()) { if (!actionOrder.contains(a->objectName())) { //FIXME QML visualizations don't support menus for now, *and* there is no way to //distinguish them on QML side if (!a->menu()) { actions.insert(a->data().toInt()*100 + i, a); ++i; } } else { orderedActions[a->objectName()] = a; } } i = 0; foreach (QAction *a, m_containment->corona()->actions()->actions()) { if (a->objectName() == QStringLiteral("lock widgets") || a->menu()) { //It is up to the Containment to decide if the user is allowed or not //to lock/unluck the widgets, so corona should not add one when there is none //(user is not allow) and it shouldn't add another one when there is already //one continue; } if (!actionOrder.contains(a->objectName())) { actions.insert(a->data().toInt()*100 + i, a); } else { orderedActions[a->objectName()] = a; } ++i; } QList actionList = actions.values(); foreach (const QString &name, actionOrder) { QAction *a = orderedActions.value(name); if (a && !a->menu()) { actionList << a; } ++i; } return actionList; } //PROTECTED-------------------- void ContainmentInterface::mouseReleaseEvent(QMouseEvent *event) { event->setAccepted(m_containment->containmentActions().contains(Plasma::ContainmentActions::eventToString(event))); } void ContainmentInterface::mousePressEvent(QMouseEvent *event) { //even if the menu is executed synchronously, other events may be processed //by the qml incubator when plasma is loading, so we need to guard there if (m_contextMenu) { m_contextMenu.data()->close(); return; } const QString trigger = Plasma::ContainmentActions::eventToString(event); Plasma::ContainmentActions *plugin = m_containment->containmentActions().value(trigger); if (!plugin || plugin->contextualActions().isEmpty()) { event->setAccepted(false); return; } //the plugin can be a single action or a context menu //Don't have an action list? execute as single action //and set the event position as action data if (plugin->contextualActions().length() == 1) { QAction *action = plugin->contextualActions().at(0); action->setData(event->pos()); action->trigger(); event->accept(); return; } //FIXME: very inefficient appletAt() implementation Plasma::Applet *applet = 0; foreach (QObject *appletObject, m_appletInterfaces) { if (AppletInterface *ai = qobject_cast(appletObject)) { if (ai->isVisible() && ai->contains(ai->mapFromItem(this, event->posF()))) { applet = ai->applet(); break; } else { ai = 0; } } } //qDebug() << "Invoking menu for applet" << applet; QMenu *desktopMenu = new QMenu; if (desktopMenu->winId()) { desktopMenu->windowHandle()->setTransientParent(window()); } desktopMenu->setAttribute(Qt::WA_DeleteOnClose); m_contextMenu = desktopMenu; if (applet) { emit applet->contextualActionsAboutToShow(); addAppletActions(desktopMenu, applet, event); } else { emit m_containment->contextualActionsAboutToShow(); addContainmentActions(desktopMenu, event); } //this is a workaround where Qt now creates the menu widget //in .exec before oxygen can polish it and set the following attribute desktopMenu->setAttribute(Qt::WA_TranslucentBackground); //end workaround //this is a workaround where Qt will fail to realise a mouse has been released // this happens if a window which does not accept focus spawns a new window that takes focus and X grab // whilst the mouse is depressed // https://bugreports.qt.io/browse/QTBUG-59044 // this causes the next click to go missing //by releasing manually we avoid that situation auto ungrabMouseHack = [this]() { if (window() && window()->mouseGrabberItem()) { window()->mouseGrabberItem()->ungrabMouse(); } }; //pre 5.8.0 QQuickWindow code is "item->grabMouse(); sendEvent(item, mouseEvent)" //post 5.8.0 QQuickWindow code is sendEvent(item, mouseEvent); item->grabMouse() if (QVersionNumber::fromString(qVersion()) > QVersionNumber(5, 8, 0)) { QTimer::singleShot(0, this, ungrabMouseHack); } else { ungrabMouseHack(); } //end workaround QPoint pos = event->globalPos(); if (window() && m_containment->containmentType() == Plasma::Types::PanelContainment) { desktopMenu->adjustSize(); if (QScreen *screen = window()->screen()) { const QRect geo = screen->availableGeometry(); pos = QPoint(qBound(geo.left(), pos.x(), geo.right() + 1 - desktopMenu->width()), qBound(geo.top(), pos.y(), geo.bottom() + 1 - desktopMenu->height())); } } if (desktopMenu->isEmpty()) { delete desktopMenu; event->accept(); return; } KAcceleratorManager::manage(desktopMenu); desktopMenu->popup(pos); event->setAccepted(true); } void ContainmentInterface::wheelEvent(QWheelEvent *event) { const QString trigger = Plasma::ContainmentActions::eventToString(event); Plasma::ContainmentActions *plugin = m_containment->containmentActions().value(trigger); if (!plugin) { event->setAccepted(false); return; } m_wheelDelta += event->delta(); // Angle delta 120 for common "one click" // See: http://qt-project.org/doc/qt-5/qml-qtquick-wheelevent.html#angleDelta-prop while (m_wheelDelta >= 120) { m_wheelDelta -= 120; plugin->performPreviousAction(); } while (m_wheelDelta <= -120) { m_wheelDelta += 120; plugin->performNextAction(); } } void ContainmentInterface::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Menu) { QMouseEvent me(QEvent::MouseButtonRelease, QPoint(), Qt::RightButton, Qt::RightButton, event->modifiers()); mousePressEvent(&me); event->accept(); } AppletInterface::keyPressEvent(event); } void ContainmentInterface::addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event) { foreach (QAction *action, applet->contextualActions()) { if (action) { desktopMenu->addAction(action); } } if (!applet->failedToLaunch()) { QAction *runAssociatedApplication = applet->actions()->action(QStringLiteral("run associated application")); if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { desktopMenu->addAction(runAssociatedApplication); } QAction *configureApplet = applet->actions()->action(QStringLiteral("configure")); if (configureApplet && configureApplet->isEnabled()) { desktopMenu->addAction(configureApplet); } QAction *appletAlternatives = applet->actions()->action(QStringLiteral("alternatives")); if (appletAlternatives && appletAlternatives->isEnabled()) { desktopMenu->addAction(appletAlternatives); } } QMenu *containmentMenu = new QMenu(i18nc("%1 is the name of the containment", "%1 Options", m_containment->title()), desktopMenu); addContainmentActions(containmentMenu, event); if (!containmentMenu->isEmpty()) { int enabled = 0; //count number of real actions QListIterator actionsIt(containmentMenu->actions()); while (enabled < 3 && actionsIt.hasNext()) { QAction *action = actionsIt.next(); if (action->isVisible() && !action->isSeparator()) { ++enabled; } } if (enabled) { //if there is only one, don't create a submenu if (enabled < 2) { foreach (QAction *action, containmentMenu->actions()) { if (action->isVisible() && !action->isSeparator()) { desktopMenu->addAction(action); } } } else { desktopMenu->addMenu(containmentMenu); } } } if (m_containment->immutability() == Plasma::Types::Mutable && (m_containment->containmentType() != Plasma::Types::PanelContainment || m_containment->isUserConfiguring())) { QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); //qDebug() << "checking for removal" << closeApplet; if (closeApplet) { if (!desktopMenu->isEmpty()) { desktopMenu->addSeparator(); } //qDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible(); desktopMenu->addAction(closeApplet); } } } void ContainmentInterface::addContainmentActions(QMenu *desktopMenu, QEvent *event) { if (m_containment->corona()->immutability() != Plasma::Types::Mutable && !KAuthorized::authorizeKAction(QStringLiteral("plasma/containment_actions"))) { //qDebug() << "immutability"; return; } //this is what ContainmentPrivate::prepareContainmentActions was const QString trigger = Plasma::ContainmentActions::eventToString(event); Plasma::ContainmentActions *plugin = m_containment->containmentActions().value(trigger); if (!plugin) { return; } if (plugin->containment() != m_containment) { plugin->setContainment(m_containment); // now configure it KConfigGroup cfg(m_containment->corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(m_containment->containmentType())); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } QList actions = plugin->contextualActions(); if (actions.isEmpty()) { //it probably didn't bother implementing the function. give the user a chance to set //a better plugin. note that if the user sets no-plugin this won't happen... if ((m_containment->containmentType() != Plasma::Types::PanelContainment && m_containment->containmentType() != Plasma::Types::CustomPanelContainment) && m_containment->actions()->action(QStringLiteral("configure"))) { desktopMenu->addAction(m_containment->actions()->action(QStringLiteral("configure"))); } } else { desktopMenu->addActions(actions); } return; } #include "moc_containmentinterface.cpp" diff --git a/src/scriptengines/qml/plasmoid/wallpaperinterface.cpp b/src/scriptengines/qml/plasmoid/wallpaperinterface.cpp index 2487a3c61..5bd5d8c11 100644 --- a/src/scriptengines/qml/plasmoid/wallpaperinterface.cpp +++ b/src/scriptengines/qml/plasmoid/wallpaperinterface.cpp @@ -1,279 +1,279 @@ /* * Copyright 2013 Marco Martin * * 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 "wallpaperinterface.h" #include "containmentinterface.h" #include #include #include #include #include #include #include #include #include #include #include #include QHash WallpaperInterface::s_rootObjects = QHash(); WallpaperInterface::WallpaperInterface(ContainmentInterface *parent) : QQuickItem(parent), m_containmentInterface(parent), m_qmlObject(0), m_configuration(0), m_configLoader(0), m_actionSignals(0) { m_actions = new KActionCollection(this); //resize at the beginning to avoid as much resize events as possible if (parent) { setSize(QSizeF(parent->width(), parent->height())); } if (!m_containmentInterface->containment()->wallpaper().isEmpty()) { syncWallpaperPackage(); } connect(m_containmentInterface->containment(), &Plasma::Containment::wallpaperChanged, this, &WallpaperInterface::syncWallpaperPackage); } WallpaperInterface::~WallpaperInterface() { if (m_qmlObject) { s_rootObjects.remove(m_qmlObject->engine()); } } -KPluginInfo::List WallpaperInterface::listWallpaperInfoForMimetype(const QString &mimetype, const QString &formFactor) +QList WallpaperInterface::listWallpaperMetadataForMimetype(const QString &mimetype, const QString &formFactor) { auto filter = [&mimetype, &formFactor](const KPluginMetaData &md) -> bool { if (!formFactor.isEmpty() && !md.value(QStringLiteral("X-Plasma-FormFactors")).contains(formFactor)) { return false; } return KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimetype); }; - return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Wallpaper"), QString(), filter).toVector()); + return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Wallpaper"), QString(), filter); } -Plasma::Package WallpaperInterface::package() const +KPackage::Package WallpaperInterface::kPackage() const { return m_pkg; } QString WallpaperInterface::pluginName() const { return m_wallpaperPlugin; } KDeclarative::ConfigPropertyMap *WallpaperInterface::configuration() const { return m_configuration; } KConfigLoader *WallpaperInterface::configScheme() { if (!m_configLoader) { //FIXME: do we need "mainconfigxml" in wallpaper packagestructures? const QString xmlPath = m_pkg.filePath("config", QStringLiteral("main.xml")); KConfigGroup cfg = m_containmentInterface->containment()->config(); cfg = KConfigGroup(&cfg, "Wallpaper"); cfg = KConfigGroup(&cfg, m_wallpaperPlugin); if (xmlPath.isEmpty()) { m_configLoader = new KConfigLoader(cfg, 0, this); } else { QFile file(xmlPath); m_configLoader = new KConfigLoader(cfg, &file, this); } } return m_configLoader; } void WallpaperInterface::syncWallpaperPackage() { if (m_wallpaperPlugin == m_containmentInterface->containment()->wallpaper() && m_qmlObject->rootObject()) { return; } m_wallpaperPlugin = m_containmentInterface->containment()->wallpaper(); if (!m_qmlObject) { m_qmlObject = new KDeclarative::QmlObjectSharedEngine(this); s_rootObjects[m_qmlObject->engine()] = this; m_qmlObject->setInitializationDelayed(true); connect(m_qmlObject, &KDeclarative::QmlObject::finished, this, &WallpaperInterface::loadFinished); } m_actions->clear(); - m_pkg = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/Wallpaper")); + m_pkg = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/Wallpaper")).kPackage(); m_pkg.setPath(m_wallpaperPlugin); if (!m_pkg.isValid()) { qWarning() << "Error loading the wallpaper, no valid package loaded"; return; } if (m_configLoader) m_configLoader->deleteLater(); if (m_configuration) m_configuration->deleteLater(); m_configLoader = 0; m_configuration = 0; if (configScheme()) { m_configuration = new KDeclarative::ConfigPropertyMap(configScheme(), this); } m_qmlObject->rootContext()->setContextProperty(QStringLiteral("wallpaper"), this); m_qmlObject->setSource(QUrl::fromLocalFile(m_pkg.filePath("mainscript"))); - const QString rootPath = m_pkg.metadata().property(QStringLiteral("X-Plasma-RootPath")).toString(); + const QString rootPath = m_pkg.metadata().value(QStringLiteral("X-Plasma-RootPath")); if (!rootPath.isEmpty()) { m_qmlObject->setTranslationDomain(QLatin1String("plasma_wallpaper_") + rootPath); } else { - m_qmlObject->setTranslationDomain(QLatin1String("plasma_wallpaper_") + m_pkg.metadata().pluginName()); + m_qmlObject->setTranslationDomain(QLatin1String("plasma_wallpaper_") + m_pkg.metadata().pluginId()); } //initialize with our size to avoid as much resize events as possible QVariantHash props; props[QStringLiteral("width")] = width(); props[QStringLiteral("height")] = height(); m_qmlObject->completeInitialization(props); } void WallpaperInterface::loadFinished() { if (m_qmlObject->mainComponent() && m_qmlObject->rootObject() && !m_qmlObject->mainComponent()->isError()) { m_qmlObject->rootObject()->setProperty("z", -1000); m_qmlObject->rootObject()->setProperty("parent", QVariant::fromValue(this)); //set anchors QQmlExpression expr(m_qmlObject->engine()->rootContext(), m_qmlObject->rootObject(), QStringLiteral("parent")); QQmlProperty prop(m_qmlObject->rootObject(), QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); } else if (m_qmlObject->mainComponent()) { qWarning() << "Error loading the wallpaper" << m_qmlObject->mainComponent()->errors(); s_rootObjects.remove(m_qmlObject->engine()); m_qmlObject->deleteLater(); m_qmlObject = 0; } else { qWarning() << "Error loading the wallpaper, package not found"; } emit packageChanged(); emit configurationChanged(); } QList WallpaperInterface::contextualActions() const { return m_actions->actions(); } bool WallpaperInterface::supportsMimetype(const QString &mimetype) const { - return KPluginMetaData::readStringList(m_pkg.kPackage().metadata().rawData(), "X-Plasma-DropMimeTypes").contains(mimetype); + return KPluginMetaData::readStringList(m_pkg.metadata().rawData(), "X-Plasma-DropMimeTypes").contains(mimetype); } void WallpaperInterface::setUrl(const QUrl &url) { if (m_qmlObject->rootObject()) { QMetaObject::invokeMethod(m_qmlObject->rootObject(), QStringLiteral("setUrl").toLatin1(), Qt::DirectConnection, Q_ARG(QVariant, QVariant::fromValue(url))); } } void WallpaperInterface::setAction(const QString &name, const QString &text, const QString &icon, const QString &shortcut) { QAction *action = m_actions->action(name); if (action) { action->setText(text); } else { Q_ASSERT(!m_actions->action(name)); action = new QAction(text, this); m_actions->addAction(name, action); if (!m_actionSignals) { m_actionSignals = new QSignalMapper(this); connect(m_actionSignals, SIGNAL(mapped(QString)), this, SLOT(executeAction(QString))); } connect(action, SIGNAL(triggered()), m_actionSignals, SLOT(map())); m_actionSignals->setMapping(action, name); } if (!icon.isEmpty()) { action->setIcon(QIcon::fromTheme(icon)); } if (!shortcut.isEmpty()) { action->setShortcut(shortcut); } action->setObjectName(name); setProperty("contextualActions", QVariant::fromValue(contextualActions())); } void WallpaperInterface::removeAction(const QString &name) { QAction *action = m_actions->action(name); if (action) { if (m_actionSignals) { m_actionSignals->removeMappings(action); } m_actions->removeAction(action); delete action; } setProperty("contextualActions", QVariant::fromValue(contextualActions())); } QAction *WallpaperInterface::action(QString name) const { return m_actions->action(name); } void WallpaperInterface::executeAction(const QString &name) { if (m_qmlObject->rootObject()) { QMetaObject::invokeMethod(m_qmlObject->rootObject(), QString("action_" + name).toLatin1(), Qt::DirectConnection); } } WallpaperInterface * WallpaperInterface::qmlAttachedProperties(QObject* object) { //at the moment of the attached object creation, the root item is the only one that hasn't a parent //only way to avoid creation of this attached for everybody but the root item return object->parent() ? nullptr : s_rootObjects.value(QtQml::qmlEngine(object)); } #include "moc_wallpaperinterface.cpp" diff --git a/src/scriptengines/qml/plasmoid/wallpaperinterface.h b/src/scriptengines/qml/plasmoid/wallpaperinterface.h index 08909e717..4445b6308 100644 --- a/src/scriptengines/qml/plasmoid/wallpaperinterface.h +++ b/src/scriptengines/qml/plasmoid/wallpaperinterface.h @@ -1,114 +1,114 @@ /* * Copyright 2013 Marco Martin * * 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 WALLPAPERINTERFACE_H #define WALLPAPERINTERFACE_H #include #include -#include +#include class KConfigLoader; class KActionCollection; class ContainmentInterface; class QSignalMapper; namespace KDeclarative { class ConfigPropertyMap; class QmlObject; } /** * @class WallpaperInterface * * @brief This class is exposed to wallpapers as the attached property "wallpaper" * * */ class WallpaperInterface : public QQuickItem { Q_OBJECT Q_PROPERTY(QString pluginName READ pluginName NOTIFY packageChanged) Q_PROPERTY(KDeclarative::ConfigPropertyMap *configuration READ configuration NOTIFY configurationChanged) public: WallpaperInterface(ContainmentInterface *parent = 0); ~WallpaperInterface(); /** - * Returns a list of all known wallpapers that can accept the given mimetype - * @param mimetype the mimetype to search for - * @param formFactor the format of the wallpaper being search for (e.g. desktop) - * @return list of wallpapers - */ - static KPluginInfo::List listWallpaperInfoForMimetype(const QString &mimetype, + * Returns a list of all known wallpapers that can accept the given mimetype + * @param mimetype the mimetype to search for + * @param formFactor the format of the wallpaper being search for (e.g. desktop) + * @return list of wallpapers + */ + static QList listWallpaperMetadataForMimetype(const QString &mimetype, const QString &formFactor = QString()); - Plasma::Package package() const; + KPackage::Package kPackage() const; QString pluginName() const; KDeclarative::ConfigPropertyMap *configuration() const; KConfigLoader *configScheme(); QList contextualActions() const; bool supportsMimetype(const QString &mimetype) const; void setUrl(const QUrl &urls); Q_INVOKABLE void setAction(const QString &name, const QString &text, const QString &icon = QString(), const QString &shortcut = QString()); Q_INVOKABLE void removeAction(const QString &name); Q_INVOKABLE QAction *action(QString name) const; static WallpaperInterface *qmlAttachedProperties(QObject *object); Q_SIGNALS: void packageChanged(); void configurationChanged(); private Q_SLOTS: void syncWallpaperPackage(); void executeAction(const QString &name); void loadFinished(); private: QString m_wallpaperPlugin; ContainmentInterface *m_containmentInterface; KDeclarative::QmlObject *m_qmlObject; - Plasma::Package m_pkg; + KPackage::Package m_pkg; KDeclarative::ConfigPropertyMap *m_configuration; KConfigLoader *m_configLoader; KActionCollection *m_actions; QSignalMapper *m_actionSignals; static QHash s_rootObjects; }; QML_DECLARE_TYPEINFO(WallpaperInterface, QML_HAS_ATTACHED_PROPERTIES) #endif