diff --git a/CMakeLists.txt b/CMakeLists.txt index ea567441e..e52edec54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,229 +1,230 @@ cmake_minimum_required(VERSION 3.5) set(KF5_VERSION "5.66.0") # handled by release scripts set(KF5_DEP_VERSION "5.65.0") # handled by release scripts project(Plasma VERSION ${KF5_VERSION}) # ECM setup include(FeatureSummary) find_package(ECM 5.65.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMGenerateExportHeader) include(ECMGenerateHeaders) include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(ECMQtDeclareLoggingCategory) include(ECMAddQch) include(KDEPackageAppTemplates) include(ECMGenerateQmlTypes) include(ECMSetupQtPluginMacroNames) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") ecm_setup_version(PROJECT VARIABLE_PREFIX PLASMA VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/plasma_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaConfigVersion.cmake" SOVERSION 5) if(KDE_PLATFORM_FEATURE_DISABLE_DEPRECATED) set(KDE_NO_DEPRECATED TRUE) set(CMAKE_AUTOMOC_MOC_OPTIONS "-DKDE_NO_DEPRECATED") endif() ################# now find all used packages ################# set (REQUIRED_QT_VERSION 5.11.0) find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS Quick Gui Sql Qml Svg QuickControls2) find_package(KF5 ${KF5_DEP_VERSION} REQUIRED COMPONENTS Activities Archive Config ConfigWidgets CoreAddons DBusAddons Declarative GlobalAccel GuiAddons I18n IconThemes KIO Service WindowSystem XmlGui Notifications Package Kirigami2 OPTIONAL_COMPONENTS Wayland DocTools ) set_package_properties(KF5Wayland PROPERTIES DESCRIPTION "Integration with the Wayland compositor" TYPE OPTIONAL ) if(KF5Wayland_FOUND) set(HAVE_KWAYLAND 1) endif() set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Tools to generate documentation" TYPE OPTIONAL ) #optional features find_package(X11 MODULE) set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "https://www.x.org/" TYPE OPTIONAL ) find_package(XCB MODULE COMPONENTS XCB COMPOSITE DAMAGE SHAPE XFIXES RENDER) set_package_properties(XCB PROPERTIES DESCRIPTION "X protocol C-language Binding" URL "https://xcb.freedesktop.org/" TYPE OPTIONAL ) if(X11_FOUND AND XCB_XCB_FOUND) set(HAVE_X11 1) find_package(Qt5 REQUIRED NO_MODULE COMPONENTS X11Extras) #X11_Xrender discovery is done by FindX11 #add_feature_info("X Rendering Extension (libXrender)" X11_Xrender_FOUND "Support for compositing, rendering operations, and alpha-blending. STRONGLY RECOMMENDED") endif() find_package(OpenGL) set_package_properties(OpenGL PROPERTIES DESCRIPTION "The OpenGL libraries" URL "https://www.opengl.org/" TYPE OPTIONAL ) find_package(EGL) set_package_properties(EGL PROPERTIES PURPOSE "Support for Window Thumbnail on EGL platform" TYPE OPTIONAL ) set(HAVE_EGL ${EGL_FOUND}) if(OPENGL_FOUND AND (${Qt5Gui_OPENGL_IMPLEMENTATION} STREQUAL "GL")) set(HAVE_GLX ${HAVE_X11}) else() set(HAVE_GLX 0) endif() ######################################################################### ecm_setup_qtplugin_macro_names( JSON_NONE "K_EXPORT_PLASMA_SERVICE" "K_EXPORT_PLASMA_APPLET" "K_EXPORT_PLASMA_PACKAGE" "K_EXPORT_PLASMA_APPLETSCRIPTENGINE" "K_EXPORT_PLASMA_DATAENGINESCRIPTENGINE" "K_EXPORT_PLASMA_DATAENGINE" JSON_ARG2 "K_EXPORT_PLASMA_PACKAGE_WITH_JSON" JSON_ARG3 "K_EXPORT_PLASMA_SERVICE_WITH_JSON" "K_EXPORT_PLASMA_APPLET_WITH_JSON" "K_EXPORT_PLASMA_APPLETSCRIPTENGINE_WITH_JSON" "K_EXPORT_PLASMA_DATAENGINESCRIPTENGINE_WITH_JSON" "K_EXPORT_PLASMA_CONTAINMENTACTIONS_WITH_JSON" "K_EXPORT_PLASMA_DATAENGINE_WITH_JSON" CONFIG_CODE_VARIABLE PACKAGE_SETUP_AUTOMOC_VARIABLES ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050d00) +add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x054000) #add_definitions(-Wno-deprecated) include(KF5PlasmaMacros.cmake) ######################################################################### option(BUILD_EXAMPLES "Build and install Plasma examples." OFF) option(BUILD_COVERAGE "Build Plasma Frameworks with gcov support" OFF) if(BUILD_COVERAGE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov") endif() # make plasma_version.h available include_directories(${CMAKE_CURRENT_BINARY_DIR}) ################# list the subdirectories ################# if (KF5DocTools_FOUND) add_subdirectory(docs) endif() add_definitions(-DTRANSLATION_DOMAIN=\"libplasma5\") if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install(po) if (KF5DocTools_FOUND) kdoctools_install(po) endif() endif() add_subdirectory(src) if (BUILD_EXAMPLES) add_subdirectory(examples) endif() if (BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() add_subdirectory(templates) ################ create PlasmaConfig.cmake and install it ########################### # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Plasma") if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5Plasma_QCH FILE KF5PlasmaQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5PlasmaQchTargets.cmake\")") endif() configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5PlasmaConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} PATH_VARS KF5_INCLUDE_INSTALL_DIR CMAKE_INSTALL_PREFIX ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaConfigVersion.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/KF5PlasmaMacros.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5PlasmaTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5PlasmaTargets.cmake NAMESPACE KF5:: COMPONENT Devel) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/plasma_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) install(FILES plasma-framework.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/declarativeimports/core/corebindingsplugin.cpp b/src/declarativeimports/core/corebindingsplugin.cpp index 052f5d3ee..a8d8e42a8 100644 --- a/src/declarativeimports/core/corebindingsplugin.cpp +++ b/src/declarativeimports/core/corebindingsplugin.cpp @@ -1,114 +1,115 @@ /* * Copyright 2009 by Alan Alpert * Copyright 2010 by Ménard Alexis * Copyright 2010 by Marco Martin * Copyright 2013 by Sebastian Kügler * 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 "corebindingsplugin.h" #include #include #include #include #include "datasource.h" #include "datamodel.h" #include "framesvgitem.h" #include "svgitem.h" #include "theme.h" #include "dialog.h" #include "iconitem.h" #include "serviceoperationstatus.h" #include "colorscope.h" #include "quicktheme.h" #include "tooltip.h" #include "units.h" #include "windowthumbnail.h" #include // #include "dataenginebindings_p.h" #include #include void CoreBindingsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { QQmlExtensionPlugin::initializeEngine(engine, uri); QQmlContext *context = engine->rootContext(); Plasma::QuickTheme *theme = new Plasma::QuickTheme(engine); context->setContextProperty(QStringLiteral("theme"), theme); context->setContextProperty(QStringLiteral("units"), &Units::instance()); if (!engine->rootContext()->contextObject()) { KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine); - kdeclarative.setupBindings(); + kdeclarative.setupContext(); + KDeclarative::KDeclarative::setupEngine(engine); } } void CoreBindingsPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QByteArray("org.kde.plasma.core")); qmlRegisterUncreatableType(uri, 2, 0, "Types", {}); qmlRegisterUncreatableType(uri, 2, 0, "Units", {}); qmlRegisterType(uri, 2, 0, "Svg"); qmlRegisterType(uri, 2, 0, "FrameSvg"); qmlRegisterType(uri, 2, 0, "SvgItem"); qmlRegisterType(uri, 2, 0, "FrameSvgItem"); //qmlRegisterType(uri, 2, 0, "Theme"); qmlRegisterUncreatableType(uri, 2, 0, "Theme", QStringLiteral("It is not possible to instantiate Theme directly.")); qmlRegisterType(uri, 2, 0, "ColorScope"); qmlRegisterType(uri, 2, 0, "DataSource"); qmlRegisterType(uri, 2, 0, "DataModel"); qmlRegisterType(uri, 2, 0, "SortFilterModel"); qmlRegisterType(uri, 2, 1, "SortFilterModel"); qmlRegisterType(uri, 2, 0, "Dialog"); // HACK make properties like "opacity" work that are in REVISION 1 of QWindow qmlRegisterRevision(uri, 2, 0); qmlRegisterRevision(uri, 2, 0); qmlRegisterType(uri, 2, 0, "ToolTipArea"); qmlRegisterInterface("Service"); qRegisterMetaType("Service"); qmlRegisterInterface("ServiceJob"); qRegisterMetaType("ServiceJob"); qmlRegisterType(uri, 2, 0, "ServiceOperationStatus"); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(uri, 2, 0, "IconItem"); qmlRegisterInterface("DataSource"); qRegisterMetaType("DataSource"); qmlRegisterType(uri, 2, 0, "WindowThumbnail"); } diff --git a/src/plasma/package.cpp b/src/plasma/package.cpp index 396abe730..db97a501b 100644 --- a/src/plasma/package.cpp +++ b/src/plasma/package.cpp @@ -1,317 +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(nullptr), fallbackPackage(nullptr), structure(nullptr) { } 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(); + return QString::fromLocal8Bit(d->internalPackage->cryptographicHash(QCryptographicHash::Sha1)); } 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/plasmaquick/configview.cpp b/src/plasmaquick/configview.cpp index 122907390..b410e9593 100644 --- a/src/plasmaquick/configview.cpp +++ b/src/plasmaquick/configview.cpp @@ -1,360 +1,361 @@ /* * 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; QPointer applet; ConfigModel *configModel; ConfigModel *kcmConfigModel; Plasma::Corona *corona; //Attached Layout property of mainItem, if any QPointer mainItemLayout; }; ConfigViewPrivate::ConfigViewPrivate(Plasma::Applet *appl, ConfigView *view) : q(view), applet(appl), corona(nullptr) { } 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(QStringLiteral("plasma_applet_") + rootPath); } else { kdeclarative.setTranslationDomain(QStringLiteral("plasma_applet_") + applet.data()->pluginMetaData().pluginId()); } - kdeclarative.setupBindings(); + kdeclarative.setupContext(); + KDeclarative::KDeclarative::setupEngine(q->engine()); // ### how to make sure to do this only once per engine? //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; } 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); new QQmlFileSelector(q->engine(), q->engine()); } q->setResizeMode(QQuickView::SizeViewToRootObject); //config model local of the applet QQmlComponent *component = new QQmlComponent(q->engine(), applet.data()->kPackage().fileUrl("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 = nullptr; //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(d->corona->kPackage().fileUrl("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/view.cpp b/src/plasmaquick/view.cpp index f448fa0c1..4b9c0159a 100644 --- a/src/plasmaquick/view.cpp +++ b/src/plasmaquick/view.cpp @@ -1,280 +1,281 @@ /* * 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, nullptr, q, nullptr); 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->setSize(q->size()); 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->raise(); 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); const auto pkg = corona->kPackage(); if (pkg.isValid()) { PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(engine(), pkg); interceptor->setForcePlasmaStyle(true); engine()->setUrlInterceptor(interceptor); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); //binds things like kconfig and icons kdeclarative.setTranslationDomain(QStringLiteral("plasma_shell_") + pkg.metadata().pluginId()); - kdeclarative.setupBindings(); + kdeclarative.setupContext(); + KDeclarative::KDeclarative::setupEngine(engine()); // ### how to make sure to do this only once per engine? } 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 f73f3d8db..1b5de193f 100644 --- a/src/scriptengines/qml/plasmoid/containmentinterface.cpp +++ b/src/scriptengines/qml/plasmoid/containmentinterface.cpp @@ -1,1202 +1,1200 @@ /* * 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 #include #include #include #include #include #include #include #include #include #include #include #include "kdeclarative/configpropertymap.h" #include ContainmentInterface::ContainmentInterface(DeclarativeAppletScript *parent, const QVariantList &args) : AppletInterface(parent, args), m_wallpaperInterface(nullptr), m_activityInfo(nullptr), m_wheelDelta(0) { 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); connect(m_containment->corona(), &Plasma::Corona::editModeChanged, this, &ContainmentInterface::editModeChanged); 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(); if (!m_containment->wallpaper().isEmpty()) { loadWallpaper(); } 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(QStringLiteral("KPackage/GenericQML")); pkg.setDefaultPackageRoot(QStringLiteral("plasma/packages")); if (defaults.isValid()) { pkg.setPath(defaults.readEntry("ToolBox", "org.kde.desktoptoolbox")); } else { pkg.setPath(QStringLiteral("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[QStringLiteral("parent")] = QVariant::fromValue(this); QObject *toolBoxObject = qmlObject()->createObjectFromSource(pkg.fileUrl("mainscript"), nullptr, 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()); } connect(m_containment.data(), &Plasma::Containment::activityChanged, this, &ContainmentInterface::activityChanged); connect(m_containment.data(), &Plasma::Containment::activityChanged, this, [ = ]() { 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, + connect(m_containment.data()->actions(), &KActionCollection::changed, this, &ContainmentInterface::actionsChanged); } 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(); } QAction *ContainmentInterface::globalAction(QString name) const { return m_containment->corona()->actions()->action(name); } bool ContainmentInterface::isEditMode() const { return m_containment->corona()->isEditMode(); } void ContainmentInterface::setEditMode(bool edit) { m_containment->corona()->setEditMode(edit); } 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; // Catch drops from a Task Manager and convert to usable URL. if (!mimeData->hasUrls() && mimeData->hasFormat(QStringLiteral("text/x-orgkdeplasmataskmanager_taskurl"))) { QList urls = {QUrl(QString::fromUtf8(mimeData->data(QStringLiteral("text/x-orgkdeplasmataskmanager_taskurl"))))}; mimeData->setUrls(urls); } if (mimeData->hasFormat(QStringLiteral("text/x-plasmoidservicename"))) { QString data = QString::fromUtf8( mimeData->data(QStringLiteral("text/x-plasmoidservicename")) ); const QStringList appletNames = data.split(QLatin1Char('\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) { 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, &KJob::result, this, &ContainmentInterface::dropJobResult); 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; } } } else { QStringList formats = mimeData->formats(); QHash seenPlugins; QHash pluginFormats; foreach (const QString &format, formats) { const auto plugins = Plasma::PluginLoader::self()->listAppletMetaDataForMimeType(format); foreach (const auto &plugin, plugins) { if (seenPlugins.contains(plugin.pluginId())) { continue; } 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 containment 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], QString::fromUtf8(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 auto &info, seenPlugins) { QAction *action; 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.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, QString::fromUtf8(mimeData->data(selectedPlugin))); }); 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) { QObject::disconnect(job, nullptr, this, nullptr); m_dropPoints.remove(job); QMenu *choices = m_dropMenus.take(job); m_dropJobs.remove(job); job->kill(); } void ContainmentInterface::dropJobResult(KJob *job) { if (job->error()) { qDebug() << "ERROR" << job->error() << ' ' << job->errorString(); } } void ContainmentInterface::mimeTypeRetrieved(KIO::Job *job, const QString &mimetype) { 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; } 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()->listAppletMetaDataForMimeType(mimetype); 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->kPackage().metadata(); } else { 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, 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 = [](const QString &text) { KNotification::event(QStringLiteral("plasmoidInstallationFailed"), i18n("Package Installation Failed"), text, QStringLiteral("dialog-error"), nullptr, 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 auto &info, appletList) { const QString actionText = i18nc("Add widget", "Add %1", info.name()); QAction *action = new QAction(actionText, nullptr); if (!info.iconName().isEmpty()) { action->setIcon(QIcon::fromTheme(info.iconName())); } if (choices) { choices->addAction(action); } dropActions << action; 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(i18nc("Add icon widget", "Add 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 auto &info, appletList) { sorted.insert(info.name(), info); } foreach (const KPluginMetaData &info, wallpaperList) { const QString actionText = i18nc("Set wallpaper", "Set %1", info.name()); QAction *action = new QAction(actionText, nullptr); if (!info.iconName().isEmpty()) { action->setIcon(QIcon::fromTheme(info.iconName())); } if (choices) { choices->addAction(action); } dropActions << action; actionsToWallpapers.insert(action, info.pluginId()); const QUrl url = tjob->url(); connect(action, &QAction::triggered, this, [this, 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(i18nc("Add icon widget", "Add 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(i18nc("Add icon widget", "Add 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); } } 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_wallpaperInterface && !m_containment->wallpaper().isEmpty()) { 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)); connect(m_wallpaperInterface, &WallpaperInterface::isLoadingChanged, this, &AppletInterface::updateUiReadyConstraint); //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_containment->wallpaper().isEmpty()) { m_wallpaperInterface->deleteLater(); m_wallpaperInterface = nullptr; } } 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() == QLatin1String("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 = nullptr; foreach (QObject *appletObject, m_appletInterfaces) { if (AppletInterface *ai = qobject_cast(appletObject)) { if (ai->isVisible() && ai->contains(ai->mapFromItem(this, event->localPos()))) { applet = ai->applet(); break; } else { ai = nullptr; } } } //qDebug() << "Invoking menu for applet" << applet; QMenu *desktopMenu = new QMenu; //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 if (desktopMenu->winId()) { desktopMenu->windowHandle()->setTransientParent(window()); } desktopMenu->setAttribute(Qt::WA_DeleteOnClose); m_contextMenu = desktopMenu; emit m_containment->contextualActionsAboutToShow(); if (applet) { emit applet->contextualActionsAboutToShow(); addAppletActions(desktopMenu, applet, event); } else { addContainmentActions(desktopMenu, event); } //this is a workaround where Qt will fail to realize 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(QLatin1String(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; } // Bug 344205 keep panel visible while menu is open const auto oldStatus = m_containment->status(); m_containment->setStatus(Plasma::Types::RequiresAttentionStatus); connect(desktopMenu, &QMenu::aboutToHide, m_containment, [this, oldStatus] { m_containment->setStatus(oldStatus); }); 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->angleDelta().y(); // Angle delta 120 for common "one click" // See: https://doc.qt.io/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); } } if (m_containment->containmentType() != Plasma::Types::DesktopContainment) { desktopMenu->addSeparator(); addContainmentActions(desktopMenu, event); } 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::authorizeAction(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; } bool ContainmentInterface::isLoading() const { bool loading = AppletInterface::isLoading(); if (m_wallpaperInterface) { loading |= m_wallpaperInterface->isLoading(); } return loading; } #include "moc_containmentinterface.cpp"