diff --git a/src/plasma/CMakeLists.txt b/src/plasma/CMakeLists.txt index 41edf273f..98b2e1b98 100644 --- a/src/plasma/CMakeLists.txt +++ b/src/plasma/CMakeLists.txt @@ -1,243 +1,243 @@ add_subdirectory(packagestructure) # This option should be removed, or moved down as far as possible. # That means porting the existing frameworks to the CMake automoc # feature. Porting is mostly removing explicit moc includes, and # leaving the ones which are truly needed (ie, if you remove # them, the build fails). set(CMAKE_AUTOMOC_RELAXED_MODE ON) if(KDE_PLATFORM_FEATURE_BINARY_COMPATIBLE_FEATURE_REDUCTION) set(PLASMA_NO_PACKAGE_EXTRADATA TRUE) endif() #find_package(KdepimLibs 4.5.60) #find_package(Gpgme) #set_package_properties(KDEPIMLIBS PROPERTIES DESCRIPTION "KDE PIM libraries" # URL "https://www.kde.org" TYPE OPTIONAL # PURPOSE "Needed for building several Plasma DataEngines") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-plasma.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-plasma.h) #FIXME: gpgme++ is in kdepimlibs, must move somewhere else! include_directories(${KDEPIMLIBS_INCLUDE_DIRS} ${GPGME_INCLUDES}) add_definitions(-DKDE_DEFAULT_DEBUG_AREA=1209) ########### next target ############### set(Plasma_LIB_SRCS #global plasma.cpp pluginloader.cpp version.cpp #applets,containments,corona applet.cpp containment.cpp containmentactions.cpp corona.cpp private/applet_p.cpp private/associatedapplicationmanager.cpp private/containment_p.cpp private/timetracker.cpp #Dataengines, services datacontainer.cpp dataengine.cpp dataengineconsumer.cpp service.cpp servicejob.cpp private/datacontainer_p.cpp private/dataenginemanager.cpp private/storage.cpp private/storagethread.cpp #packages package.cpp packagestructure.cpp #graphics framesvg.cpp svg.cpp theme.cpp private/theme_p.cpp #scripting scripting/appletscript.cpp scripting/dataenginescript.cpp scripting/scriptengine.cpp ) if(HAVE_X11) set(Plasma_LIB_SRCS ${Plasma_LIB_SRCS} private/effectwatcher.cpp) endif() kconfig_add_kcfg_files(Plasma_LIB_SRCS data/kconfigxt/libplasma-theme-global.kcfgc) #NEPOMUK_GENERATE_FROM_ONTOLOGY( # nwc.nrl # ${metadata_test_BINARY_DIR} # TEST_HEADERS # TEST_SOURCES # TEST_INCLUDES #) ecm_qt_declare_logging_category(Plasma_LIB_SRCS HEADER debug_p.h IDENTIFIER LOG_PLASMA CATEGORY_NAME org.kde.plasma) add_library(KF5Plasma ${Plasma_LIB_SRCS}) add_library(KF5::Plasma ALIAS KF5Plasma) ecm_generate_export_header(KF5Plasma EXPORT_FILE_NAME plasma/plasma_export.h BASE_NAME Plasma GROUP_BASE_NAME KF VERSION ${KF5_VERSION} DEPRECATED_BASE_VERSION 0 - DEPRECATION_VERSIONS 5.6 5.19 5.28 5.30 5.36 5.46 + DEPRECATION_VERSIONS 5.6 5.19 5.28 5.30 5.36 5.46 5.67 ) # TODO: add support for EXCLUDE_DEPRECATED_BEFORE_AND_AT to all Plasma libs # needs fixing of undeprecated API being still implemented using own deprecated API if(HAVE_X11) set(PLASMA_EXTRA_LIBS ${PLASMA_EXTRA_LIBS} Qt5::X11Extras ${X11_LIBRARIES} XCB::XCB) endif() if(DL_LIBRARY) set(PLASMA_EXTRA_LIBS ${PLASMA_EXTRA_LIBS} ${DL_LIBRARY}) endif() target_link_libraries(KF5Plasma PUBLIC KF5::Service # For kplugininfo.h and kservice.h Qt5::Gui KF5::Package PRIVATE Qt5::Sql Qt5::Svg Qt5::DBus KF5::Archive KF5::GuiAddons #kimagecache KF5::I18n KF5::KIOCore #ServiceJob KF5::KIOWidgets #KRun KF5::WindowSystem #compositingActive KF5::Declarative #runtimePlatform KF5::XmlGui #KActionCollection KF5::GlobalAccel #Applet::setGlobalShortcut KF5::Notifications KF5::IconThemes ${PLASMA_EXTRA_LIBS} ) set(Plasma_BUILD_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/Plasma ) target_include_directories(KF5Plasma PUBLIC "$" ) target_include_directories(KF5Plasma INTERFACE "$" ) set_target_properties(KF5Plasma PROPERTIES VERSION ${PLASMA_VERSION_STRING} SOVERSION ${PLASMA_SOVERSION} EXPORT_NAME Plasma ) ########### install files ############### ecm_generate_headers(Plasma_CamelCase_HEADERS HEADER_NAMES Applet Containment ContainmentActions Corona DataContainer DataEngine DataEngineConsumer PluginLoader FrameSvg Package PackageStructure Service ServiceJob Svg Theme Plasma REQUIRED_HEADERS Plasma_HEADERS PREFIX Plasma ) set(Plasma_HEADERS ${Plasma_HEADERS} version.h ) set(PlasmaScripting_HEADERS scripting/appletscript.h scripting/dataenginescript.h scripting/scriptengine.h ) install(FILES ${Plasma_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/plasma/plasma_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/plasma COMPONENT Devel) install(FILES ${Plasma_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/Plasma COMPONENT Devel) install(FILES ${PlasmaScripting_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/plasma/scripting COMPONENT Devel) install(FILES data/servicetypes/plasma-applet.desktop data/servicetypes/plasma-containment.desktop data/servicetypes/plasma-containmentactions.desktop data/servicetypes/plasma-dataengine.desktop data/servicetypes/plasma-generic.desktop data/servicetypes/plasma-packagestructure.desktop data/servicetypes/plasma-scriptengine.desktop data/servicetypes/plasma-service.desktop data/servicetypes/plasma-shell.desktop data/servicetypes/plasma-lookandfeel.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR}) install(FILES data/operations/dataengineservice.operations DESTINATION ${PLASMA_DATA_INSTALL_DIR}/services) install(FILES data/operations/plasmoidservice.operations DESTINATION ${PLASMA_DATA_INSTALL_DIR}/services) install(FILES data/operations/storage.operations DESTINATION ${PLASMA_DATA_INSTALL_DIR}/services) install(TARGETS KF5Plasma EXPORT KF5PlasmaTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) if(BUILD_QCH) ecm_add_qch( KF5Plasma_QCH NAME Plasma BASE_NAME KF5Plasma VERSION ${KF5_VERSION} ORG_DOMAIN org.kde SOURCES # using only public headers, to cover only public API ${Plasma_HEADERS} ${PlasmaScripting_HEADERS} Mainpage.dox MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" LINK_QCHS Qt5Gui_QCH KF5Service_QCH KF5Package_QCH INCLUDE_DIRS ${Plasma_BUILD_INCLUDE_DIRS} BLANK_MACROS PLASMA_EXPORT PLASMA_DEPRECATED PLASMA_DEPRECATED_EXPORT "PLASMA_DEPRECATED_VERSION(x, y, t)" TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} COMPONENT Devel ) endif() diff --git a/src/plasma/containment.cpp b/src/plasma/containment.cpp index 955c606e7..8557d27ba 100644 --- a/src/plasma/containment.cpp +++ b/src/plasma/containment.cpp @@ -1,603 +1,603 @@ /* * Copyright 2007 by Aaron Seigo * Copyright 2008 by Ménard Alexis * Copyright 2009 Chani Armitage * Copyright 2012 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 "containment.h" #include "private/containment_p.h" #include "config-plasma.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "containmentactions.h" #include "corona.h" #include "pluginloader.h" #include "debug_p.h" #include "private/applet_p.h" #include "plasma/plasma.h" namespace Plasma { Containment::Containment(QObject *parent, const QString &serviceId, uint containmentId) : Applet(parent, serviceId, containmentId), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setContainmentType(Types::CustomContainment); setHasConfigurationInterface(true); } Containment::Containment(QObject *parent, const QVariantList &args) : Applet(parent, args), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setHasConfigurationInterface(true); } Containment::Containment(const KPluginMetaData &md, uint appletId) : Applet(md, nullptr, appletId), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setHasConfigurationInterface(true); } Containment::~Containment() { qDeleteAll(d->localActionPlugins); delete d; } void Containment::init() { Applet::init(); static_cast(this)->d->setupScripting(); if (d->type == Types::NoContainmentType) { //setContainmentType(Plasma::Types::DesktopContainment); //Try to determine the containment type. It must be done as soon as possible QString type = pluginMetaData().value(QStringLiteral("X-Plasma-ContainmentType")); if (type == QLatin1String("Panel")) { setContainmentType(Plasma::Types::PanelContainment); } else if (type == QLatin1String("Custom")) { setContainmentType(Plasma::Types::CustomContainment); } else if (type == QLatin1String("CustomPanel")) { setContainmentType(Plasma::Types::CustomPanelContainment); //default to desktop } else { setContainmentType(Plasma::Types::DesktopContainment); } } //connect actions ContainmentPrivate::addDefaultActions(actions(), this); bool unlocked = immutability() == Types::Mutable; //fix the text of the actions that need title() //btw, do we really want to use title() when it's a desktopcontainment? QAction *closeApplet = actions()->action(QStringLiteral("remove")); if (closeApplet) { closeApplet->setText(i18nc("%1 is the name of the applet", "Remove %1", title())); } QAction *configAction = actions()->action(QStringLiteral("configure")); if (configAction) { if (d->type == Types::PanelContainment || d->type == Types::CustomPanelContainment) { configAction->setText(i18nc("%1 is the name of the containment", "Edit %1...", title())); configAction->setIcon(QIcon::fromTheme(QStringLiteral("document-edit"))); } else { configAction->setText(i18nc("%1 is the name of the applet", "Configure %1...", title())); } } QAction *appletBrowserAction = actions()->action(QStringLiteral("add widgets")); if (appletBrowserAction) { appletBrowserAction->setVisible(unlocked); appletBrowserAction->setEnabled(unlocked); connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets())); } if (immutability() != Types::SystemImmutable && corona()) { QAction *lockDesktopAction = corona()->actions()->action(QStringLiteral("lock widgets")); //keep a pointer so nobody notices it moved to corona if (lockDesktopAction) { actions()->addAction(QStringLiteral("lock widgets"), lockDesktopAction); } } //HACK: this is valid only in the systray case connect(this, &Containment::configureRequested, this, [=] (Plasma::Applet *a) { if (Plasma::Applet *p = qobject_cast(parent())) { emit p->containment()->configureRequested(a); } }); } // helper function for sorting the list of applets bool appletConfigLessThan(const KConfigGroup &c1, const KConfigGroup &c2) { int i1 = c1.readEntry("id", 0); int i2 = c2.readEntry("id", 0); return (i1 < i2); } void Containment::restore(KConfigGroup &group) { /* #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "!!!!!!!!!!!!initConstraints" << group.name() << d->type; // qCDebug(LOG_PLASMA) << " location:" << group.readEntry("location", (int)d->location); // qCDebug(LOG_PLASMA) << " geom:" << group.readEntry("geometry", geometry()); // qCDebug(LOG_PLASMA) << " formfactor:" << group.readEntry("formfactor", (int)d->formFactor); // qCDebug(LOG_PLASMA) << " screen:" << group.readEntry("screen", d->screen); #endif */ setLocation((Plasma::Types::Location)group.readEntry("location", (int)d->location)); setFormFactor((Plasma::Types::FormFactor)group.readEntry("formfactor", (int)d->formFactor)); d->lastScreen = group.readEntry("lastScreen", d->lastScreen); setWallpaper(group.readEntry("wallpaperplugin", ContainmentPrivate::defaultWallpaper)); d->activityId = group.readEntry("activityId", QString()); flushPendingConstraintsEvents(); restoreContents(group); setImmutability((Types::ImmutabilityType)group.readEntry("immutability", (int)Types::Mutable)); if (isContainment() && KAuthorized::authorize(QStringLiteral("plasma/containment_actions"))) { KConfigGroup cfg = KConfigGroup(corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(containmentType())); //qCDebug(LOG_PLASMA) << cfg.keyList(); if (cfg.exists()) { foreach (const QString &key, cfg.keyList()) { //qCDebug(LOG_PLASMA) << "loading" << key; setContainmentActions(key, cfg.readEntry(key, QString())); } } else { //shell defaults KConfigGroup defaultActionsCfg; switch (d->type) { case Plasma::Types::PanelContainment: /* fall through*/ case Plasma::Types::CustomPanelContainment: defaultActionsCfg = KConfigGroup(KSharedConfig::openConfig(corona()->kPackage().filePath("defaults")), "Panel"); break; case Plasma::Types::DesktopContainment: defaultActionsCfg = KConfigGroup(KSharedConfig::openConfig(corona()->kPackage().filePath("defaults")), "Desktop"); break; default: //for any other type of containment, there are no defaults break; } if (defaultActionsCfg.isValid()) { defaultActionsCfg = KConfigGroup(&defaultActionsCfg, "ContainmentActions"); foreach (const QString &key, defaultActionsCfg.keyList()) { setContainmentActions(key, defaultActionsCfg.readEntry(key, QString())); } } } } /* #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Containment" << id() << #endif "screen" << screen() << "geometry is" << geometry() << "config entries" << group.entryMap(); */ } void Containment::save(KConfigGroup &g) const { if (Applet::d->transient) { return; } KConfigGroup group = g; if (!group.isValid()) { group = config(); } // locking is saved in Applet::save Applet::save(group); // group.writeEntry("screen", d->screen); group.writeEntry("lastScreen", d->lastScreen); group.writeEntry("formfactor", (int)d->formFactor); group.writeEntry("location", (int)d->location); group.writeEntry("activityId", d->activityId); group.writeEntry("wallpaperplugin", d->wallpaper); saveContents(group); } void Containment::saveContents(KConfigGroup &group) const { KConfigGroup applets(&group, "Applets"); foreach (const Applet *applet, d->applets) { KConfigGroup appletConfig(&applets, QString::number(applet->id())); applet->save(appletConfig); } } void Containment::restoreContents(KConfigGroup &group) { KConfigGroup applets(&group, "Applets"); //restore the applets ordered by id QStringList groups = applets.groupList(); std::sort(groups.begin(), groups.end()); // Sort the applet configs in order of geometry to ensure that applets // are added from left to right or top to bottom for a panel containment QList appletConfigs; foreach (const QString &appletGroup, groups) { //qCDebug(LOG_PLASMA) << "reading from applet group" << appletGroup; KConfigGroup appletConfig(&applets, appletGroup); appletConfigs.append(appletConfig); } std::stable_sort(appletConfigs.begin(), appletConfigs.end(), appletConfigLessThan); QMutableListIterator it(appletConfigs); while (it.hasNext()) { KConfigGroup &appletConfig = it.next(); int appId = appletConfig.name().toUInt(); QString plugin = appletConfig.readEntry("plugin", QString()); if (plugin.isEmpty()) { continue; } d->createApplet(plugin, QVariantList(), appId); } //if there are no applets, none of them is "loading" if (Containment::applets().isEmpty()) { d->appletsUiReady = true; } foreach (Applet *applet, Containment::applets()) { if (!applet->pluginMetaData().isValid()) { applet->updateConstraints(Plasma::Types::UiReadyConstraint); } } } Plasma::Types::ContainmentType Containment::containmentType() const { return d->type; } void Containment::setContainmentType(Plasma::Types::ContainmentType type) { if (d->type == type) { return; } d->type = type; emit containmentTypeChanged(); } Corona *Containment::corona() const { if(Plasma::Corona* corona = qobject_cast(parent())) { return corona; //case in which this containment is child of an applet, hello systray :) } else { Plasma::Applet *parentApplet = qobject_cast(parent()); if (parentApplet && parentApplet->containment()) { return parentApplet->containment()->corona(); } } return nullptr; } void Containment::setFormFactor(Types::FormFactor formFactor) { if (d->formFactor == formFactor) { return; } //qCDebug(LOG_PLASMA) << "switching FF to " << formFactor; d->formFactor = formFactor; updateConstraints(Plasma::Types::FormFactorConstraint); KConfigGroup c = config(); c.writeEntry("formfactor", (int)formFactor); emit configNeedsSaving(); emit formFactorChanged(formFactor); } void Containment::setLocation(Types::Location location) { if (d->location == location) { return; } d->location = location; foreach (Applet *applet, d->applets) { applet->updateConstraints(Plasma::Types::LocationConstraint); } updateConstraints(Plasma::Types::LocationConstraint); KConfigGroup c = config(); c.writeEntry("location", (int)location); emit configNeedsSaving(); emit locationChanged(location); } Applet *Containment::createApplet(const QString &name, const QVariantList &args) { Plasma::Applet *applet = d->createApplet(name, args); if (applet) { emit appletCreated(applet); } return applet; } void Containment::addApplet(Applet *applet) { if (!applet) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "adding null applet!?!"; #endif return; } if (immutability() != Types::Mutable && !applet->property("org.kde.plasma:force-create").toBool()) { return; } #ifndef NDEBUG if (d->applets.contains(applet)) { // qCDebug(LOG_PLASMA) << "already have this applet!"; } #endif Containment *currentContainment = applet->containment(); if (currentContainment && currentContainment != this) { emit currentContainment->appletRemoved(applet); disconnect(applet, nullptr, currentContainment, nullptr); KConfigGroup oldConfig = applet->config(); currentContainment->d->applets.removeAll(applet); applet->setParent(this); // now move the old config to the new location //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc KConfigGroup c = config().group("Applets").group(QString::number(applet->id())); oldConfig.reparent(&c); applet->d->resetConfigurationObject(); disconnect(applet, &Applet::activated, currentContainment, &Applet::activated); //change the group to its configloader, if any //FIXME: this is very, very brutal if (applet->configScheme()) { const QString oldGroupPrefix = QStringLiteral("Containments") + QString::number(currentContainment->id()) + QStringLiteral("Applets"); const QString newGroupPrefix = QStringLiteral("Containments") + QString::number(id()) + QStringLiteral("Applets"); applet->configScheme()->setCurrentGroup(applet->configScheme()->currentGroup().replace(0, oldGroupPrefix.length(), newGroupPrefix)); foreach (KConfigSkeletonItem *item, applet->configScheme()->items()) { item->setGroup(item->group().replace(0, oldGroupPrefix.length(), newGroupPrefix)); } } } else { applet->setParent(this); } //make sure the applets are sorted by id auto position = std::lower_bound(d->applets.begin(), d->applets.end(), applet, [](Plasma::Applet *a1, Plasma::Applet *a2) { return a1->id() < a2->id(); }); d->applets.insert(position, applet); if (!d->uiReady) { d->loadingApplets << applet; } connect(applet, &Applet::configNeedsSaving, this, &Applet::configNeedsSaving); connect(applet, SIGNAL(appletDeleted(Plasma::Applet*)), this, SLOT(appletDeleted(Plasma::Applet*))); connect(applet, SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(checkStatus(Plasma::Types::ItemStatus))); connect(applet, &Applet::activated, this, &Applet::activated); if (!currentContainment) { const bool isNew = applet->d->mainConfigGroup()->entryMap().isEmpty(); if (!isNew) { applet->restore(*applet->d->mainConfigGroup()); } applet->init(); applet->d->setupScripting(); if (isNew) { applet->save(*applet->d->mainConfigGroup()); emit configNeedsSaving(); } //FIXME: an on-appear animation would be nice to have again } applet->updateConstraints(Plasma::Types::AllConstraints); applet->flushPendingConstraintsEvents(); emit appletAdded(applet); if (!currentContainment) { applet->updateConstraints(Plasma::Types::StartupCompletedConstraint); applet->flushPendingConstraintsEvents(); } applet->d->scheduleModificationNotification(); } QList Containment::applets() const { return d->applets; } int Containment::screen() const { Q_ASSERT(corona()); if (Corona* c = corona()) { return c->screenForContainment(this); } else { return -1; } } int Containment::lastScreen() const { return d->lastScreen; } void Containment::setWallpaper(const QString &pluginName) { if (pluginName != d->wallpaper) { d->wallpaper = pluginName; KConfigGroup cfg = config(); cfg.writeEntry("wallpaperplugin", d->wallpaper); emit configNeedsSaving(); emit wallpaperChanged(); } } QString Containment::wallpaper() const { return d->wallpaper; } void Containment::setContainmentActions(const QString &trigger, const QString &pluginName) { KConfigGroup cfg = d->containmentActionsConfig(); ContainmentActions *plugin = nullptr; plugin = containmentActions().value(trigger); - if (plugin && plugin->pluginInfo().pluginName() != pluginName) { + if (plugin && plugin->metadata().pluginId() != pluginName) { containmentActions().remove(trigger); delete plugin; plugin = nullptr; } if (pluginName.isEmpty()) { cfg.deleteEntry(trigger); } else if (plugin) { //it already existed, just reload config plugin->setContainment(this); //to be safe //FIXME make a truly unique config group KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } else { plugin = PluginLoader::self()->loadContainmentActions(this, pluginName); if (plugin) { cfg.writeEntry(trigger, pluginName); containmentActions().insert(trigger, plugin); plugin->setContainment(this); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } else { //bad plugin... gets removed. is this a feature or a bug? cfg.deleteEntry(trigger); } } emit configNeedsSaving(); } QHash &Containment::containmentActions() { return d->localActionPlugins; } bool Containment::isUiReady() const { return d->uiReady && d->appletsUiReady && Applet::d->started; } void Containment::setActivity(const QString &activityId) { if (activityId.isEmpty() || d->activityId == activityId) { return; } d->activityId = activityId; KConfigGroup c = config(); c.writeEntry("activityId", activityId); emit configNeedsSaving(); emit activityChanged(activityId); } QString Containment::activity() const { return d->activityId; } void Containment::reactToScreenChange() { int newScreen = screen(); if (newScreen >= 0) { d->lastScreen = newScreen; KConfigGroup c = config(); c.writeEntry("lastScreen", d->lastScreen); emit configNeedsSaving(); } emit screenChanged(newScreen); } } // Plasma namespace #include "moc_containment.cpp" diff --git a/src/plasma/containmentactions.cpp b/src/plasma/containmentactions.cpp index a80331cbe..468fe01a5 100644 --- a/src/plasma/containmentactions.cpp +++ b/src/plasma/containmentactions.cpp @@ -1,169 +1,174 @@ /* * Copyright (c) 2009 Chani Armitage * * 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 "containmentactions.h" #include "containment.h" #include "private/containmentactions_p.h" #include "private/containment_p.h" #include #include #include #include #include #include #include "version.h" namespace Plasma { ContainmentActions::ContainmentActions(QObject *parentObject) : d(new ContainmentActionsPrivate({}, this)) { setParent(parentObject); } ContainmentActions::ContainmentActions(QObject *parentObject, const QVariantList &args) : d(new ContainmentActionsPrivate(args.value(0), this)) { setParent(parentObject); // now remove first item since those are managed by Wallpaper and subclasses shouldn't // need to worry about them. yes, it violates the constness of this var, but it lets us add // or remove items later while applets can just pretend that their args always start at 0 QVariantList &mutableArgs = const_cast(args); if (!mutableArgs.isEmpty()) { mutableArgs.removeFirst(); } } ContainmentActions::~ContainmentActions() { delete d; } KPluginInfo ContainmentActions::pluginInfo() const { return KPluginInfo(d->containmentActionsDescription); } +KPluginMetaData ContainmentActions::metadata() const +{ + return d->containmentActionsDescription; +} + Containment *ContainmentActions::containment() { if (d->containment) { return d->containment; } return qobject_cast(parent()); } void ContainmentActions::restore(const KConfigGroup &config) { Q_UNUSED(config); } void ContainmentActions::save(KConfigGroup &config) { Q_UNUSED(config); } QWidget *ContainmentActions::createConfigurationInterface(QWidget *parent) { Q_UNUSED(parent); return nullptr; } void ContainmentActions::configurationAccepted() { //do nothing by default } void ContainmentActions::performNextAction() { //do nothing by default, implement in subclasses } void ContainmentActions::performPreviousAction() { //do nothing by default, implement in subclasses } QList ContainmentActions::contextualActions() { return QList(); } QString ContainmentActions::eventToString(QEvent *event) { QString trigger; Qt::KeyboardModifiers modifiers; switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: { QMouseEvent *e = static_cast(event); int m = QObject::staticQtMetaObject.indexOfEnumerator("MouseButtons"); QMetaEnum mouse = QObject::staticQtMetaObject.enumerator(m); trigger += QString::fromLatin1(mouse.valueToKey(e->button())); modifiers = e->modifiers(); break; } case QEvent::Wheel: { QWheelEvent *e = static_cast(event); int o = QObject::staticQtMetaObject.indexOfEnumerator("Orientations"); QMetaEnum orient = QObject::staticQtMetaObject.enumerator(o); trigger = QStringLiteral("wheel:"); #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) trigger += QString::fromLatin1(orient.valueToKey(e->orientation())); #else // ContainmentInterface::wheelEvent uses angleDelta.y() // To support both, should we just remove this orientation string? trigger += QStringLiteral("Vertical"); #endif modifiers = e->modifiers(); break; } case QEvent::ContextMenu: { int m = QObject::staticQtMetaObject.indexOfEnumerator("MouseButtons"); QMetaEnum mouse = QObject::staticQtMetaObject.enumerator(m); trigger = QString::fromLatin1(mouse.valueToKey(Qt::RightButton)); modifiers = Qt::NoModifier; break; } default: return QString(); } int k = QObject::staticQtMetaObject.indexOfEnumerator("KeyboardModifiers"); QMetaEnum kbd = QObject::staticQtMetaObject.enumerator(k); trigger += QLatin1Char(';') + QString::fromLatin1(kbd.valueToKeys(modifiers)); return trigger; } void ContainmentActions::setContainment(Containment *newContainment) { d->containment = newContainment; } } // Plasma namespace #include "moc_containmentactions.cpp" diff --git a/src/plasma/containmentactions.h b/src/plasma/containmentactions.h index 61a4c7b52..e331992e5 100644 --- a/src/plasma/containmentactions.h +++ b/src/plasma/containmentactions.h @@ -1,164 +1,177 @@ /* * Copyright (c) 2009 Chani Armitage * 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 PLASMA_CONTAINMENTACTIONS_H #define PLASMA_CONTAINMENTACTIONS_H #include #include #include #include class QAction; +class KPluginMetaData; namespace Plasma { class DataEngine; class Containment; class ContainmentActionsPrivate; /** * @class ContainmentActions plasma/containmentactions.h * * @short The base ContainmentActions class * * "ContainmentActions" are components that provide actions (usually displaying a contextmenu) in * response to an event with a position (usually a mouse event). * * ContainmentActions plugins are registered using .desktop files. These files should be * named using the following naming scheme: * * plasma-containmentactions-\.desktop * */ class PLASMA_EXPORT ContainmentActions : public QObject { Q_OBJECT public: /** * Default constructor for an empty or null containmentactions */ explicit ContainmentActions(QObject *parent = nullptr); ~ContainmentActions(); +#if PLASMA_ENABLE_DEPRECATED_SINCE(5, 64) /** * @return the plugin info for this ContainmentActions instance, * including name, pluginName and icon * @since 5.0 + * + * @deprecated since 5.67, use metadata */ + PLASMA_DEPRECATED_VERSION(5, 67, "use metadata()") KPluginInfo pluginInfo() const; +#endif + + /** + * @return metadata for this ContainmentActions instance + * including name, pluginName and icon + * @since 5.62 + */ + KPluginMetaData metadata() const; /** * This method should be called once the plugin is loaded or settings are changed. * @param config Config group to load settings * @see init **/ virtual void restore(const KConfigGroup &config); /** * This method is called when settings need to be saved. * @param config Config group to save settings **/ virtual void save(KConfigGroup &config); /** * Returns the widget used in the configuration dialog. * Add the configuration interface of the containmentactions to this widget. */ virtual QWidget *createConfigurationInterface(QWidget *parent); /** * This method is called when the user's configuration changes are accepted */ virtual void configurationAccepted(); /** * Called when a "next" action is triggered (e.g. by mouse wheel scroll). This * can be used to scroll through a list of items this plugin manages such as * windows, virtual desktops, activities, etc. * @see performPrevious */ virtual void performNextAction(); /** * Called when a "previous" action is triggered (e.g. by mouse wheel scroll). This * can be used to scroll through a list of items this plugin manages such as * windows, virtual desktops, activities, etc. * @see performNext */ virtual void performPreviousAction(); /** * Implement this to provide a list of actions that can be added to another menu * for example, when right-clicking an applet, the "Activity Options" submenu is populated * with this. */ virtual QList contextualActions(); /** * Turns a mouse or wheel event into a string suitable for a ContainmentActions * @return the string representation of the event */ static QString eventToString(QEvent *event); /** * @p newContainment the containment the plugin should be associated with. * @since 4.6 */ void setContainment(Containment *newContainment); /** * @return the containment the plugin is associated with. */ Containment *containment(); protected: /** * This constructor is to be used with the plugin loading systems * found in KPluginInfo and KService. The argument list is expected * to have one element: the KService service ID for the desktop entry. * * @param parent a QObject parent; you probably want to pass in 0 * @param args a list of strings containing one entry: the service id */ ContainmentActions(QObject *parent, const QVariantList &args); private: ContainmentActionsPrivate *const d; }; } // Plasma namespace /** * Register a containmentactions when it is contained in a loadable module */ #define K_EXPORT_PLASMA_CONTAINMENTACTIONS_WITH_JSON(libname, classname, jsonFile) \ K_PLUGIN_FACTORY_WITH_JSON(factory, jsonFile, registerPlugin();) \ K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION) Q_DECLARE_METATYPE(Plasma::ContainmentActions *) #endif // PLASMA_CONTAINMENTACTIONS_H diff --git a/src/plasma/dataengine.cpp b/src/plasma/dataengine.cpp index ee86354b3..310bfae23 100644 --- a/src/plasma/dataengine.cpp +++ b/src/plasma/dataengine.cpp @@ -1,644 +1,653 @@ /* * Copyright 2006-2007 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dataengine.h" #include "private/dataengine_p.h" #include "private/datacontainer_p.h" #include #include #include #include #include #include #include #include #include #include #include #include "datacontainer.h" #include "package.h" #include "pluginloader.h" #include "service.h" #include "scripting/dataenginescript.h" #include "private/service_p.h" #include "private/storage_p.h" #include "config-plasma.h" namespace Plasma { DataEngine::DataEngine(const KPluginInfo &plugin, QObject *parent) + : DataEngine(plugin.toMetaData(), parent) +{} + +DataEngine::DataEngine(const KPluginMetaData &plugin, QObject *parent) : QObject(parent), d(new DataEnginePrivate(this, plugin)) { if (d->script) { d->setupScriptSupport(); d->script->init(); } else { // default implementation does nothing. this is for engines that have to // start things in motion external to themselves before they can work } } DataEngine::DataEngine(QObject *parent, const QVariantList &args) : QObject(parent), - d(new DataEnginePrivate(this, KPluginInfo(args), args)) + d(new DataEnginePrivate(this, KPluginInfo(args).toMetaData(), args)) { if (d->script) { d->setupScriptSupport(); d->script->init(); } } DataEngine::~DataEngine() { //qCDebug(LOG_PLASMA) << objectName() << ": bye bye birdy! "; delete d; } QStringList DataEngine::sources() const { if (d->script) { return d->script->sources(); } else { return d->sources.keys(); } } Service *DataEngine::serviceForSource(const QString &source) { if (d->script) { Service *s = d->script->serviceForSource(source); if (s) { return s; } } return new NullService(source, this); } KPluginInfo DataEngine::pluginInfo() const +{ + return KPluginInfo(d->dataEngineDescription); +} + +KPluginMetaData DataEngine::metadata() const { return d->dataEngineDescription; } void DataEngine::connectSource(const QString &source, QObject *visualization, uint pollingInterval, Plasma::Types::IntervalAlignment intervalAlignment) const { //qCDebug(LOG_PLASMA) << "connectSource" << source; bool newSource; DataContainer *s = d->requestSource(source, &newSource); if (s) { // we suppress the immediate invocation of dataUpdated here if the // source was prexisting and they don't request delayed updates // (we want to do an immediate update in that case so they don't // have to wait for the first time out) if (newSource && !s->data().isEmpty()) { newSource = false; } d->connectSource(s, visualization, pollingInterval, intervalAlignment, !newSource || pollingInterval > 0); //qCDebug(LOG_PLASMA) << " ==> source connected"; } } void DataEngine::connectAllSources(QObject *visualization, uint pollingInterval, Plasma::Types::IntervalAlignment intervalAlignment) const { foreach (DataContainer *s, d->sources) { d->connectSource(s, visualization, pollingInterval, intervalAlignment); } } void DataEngine::disconnectSource(const QString &source, QObject *visualization) const { DataContainer *s = d->source(source, false); if (s) { s->disconnectVisualization(visualization); } } DataContainer *DataEngine::containerForSource(const QString &source) { return d->source(source, false); } bool DataEngine::sourceRequestEvent(const QString &name) { if (d->script) { return d->script->sourceRequestEvent(name); } else { return false; } } bool DataEngine::updateSourceEvent(const QString &source) { if (d->script) { return d->script->updateSourceEvent(source); } else { //qCDebug(LOG_PLASMA) << source; return false; //TODO: should this be true to trigger, even needless, updates on every tick? } } void DataEngine::setData(const QString &source, const QVariant &value) { setData(source, source, value); } void DataEngine::setData(const QString &source, const QString &key, const QVariant &value) { DataContainer *s = d->source(source, false); bool isNew = !s; if (isNew) { s = d->source(source); } s->setData(key, value); if (isNew && source != d->waitingSourceRequest) { emit sourceAdded(source); } d->scheduleSourcesUpdated(); } void DataEngine::setData(const QString &source, const QVariantMap &data) { DataContainer *s = d->source(source, false); bool isNew = !s; if (isNew) { s = d->source(source); } Data::const_iterator it = data.constBegin(); while (it != data.constEnd()) { s->setData(it.key(), it.value()); ++it; } if (isNew && source != d->waitingSourceRequest) { emit sourceAdded(source); } d->scheduleSourcesUpdated(); } void DataEngine::removeAllData(const QString &source) { DataContainer *s = d->source(source, false); if (s) { s->removeAllData(); d->scheduleSourcesUpdated(); } } void DataEngine::removeData(const QString &source, const QString &key) { DataContainer *s = d->source(source, false); if (s) { s->setData(key, QVariant()); d->scheduleSourcesUpdated(); } } void DataEngine::setModel(const QString &source, QAbstractItemModel *model) { if (model) { setData(source, QStringLiteral("HasModel"), true); } else { removeData(source, QStringLiteral("HasModel")); } Plasma::DataContainer *s = containerForSource(source); if (s) { s->setModel(model); } } QAbstractItemModel *DataEngine::modelForSource(const QString &source) { Plasma::DataContainer *s = containerForSource(source); if (s) { return s->model(); } else { return nullptr; } } void DataEngine::addSource(DataContainer *source) { if (d->sources.contains(source->objectName())) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "source named \"" << source->objectName() << "\" already exists."; #endif return; } QObject::connect(source, SIGNAL(updateRequested(DataContainer*)), this, SLOT(internalUpdateSource(DataContainer*))); QObject::connect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); d->sources.insert(source->objectName(), source); emit sourceAdded(source->objectName()); d->scheduleSourcesUpdated(); } void DataEngine::setMinimumPollingInterval(int minimumMs) { d->minPollingInterval = minimumMs; } int DataEngine::minimumPollingInterval() const { return d->minPollingInterval; } void DataEngine::setPollingInterval(uint frequency) { killTimer(d->updateTimerId); d->updateTimerId = 0; if (frequency > 0) { d->updateTimerId = startTimer(frequency); } } void DataEngine::removeSource(const QString &source) { QHash::iterator it = d->sources.find(source); if (it != d->sources.end()) { DataContainer *s = it.value(); s->d->store(); d->sources.erase(it); s->disconnect(this); s->deleteLater(); emit sourceRemoved(source); } } void DataEngine::removeAllSources() { QMutableHashIterator it(d->sources); while (it.hasNext()) { it.next(); Plasma::DataContainer *s = it.value(); const QString source = it.key(); it.remove(); s->disconnect(this); s->deleteLater(); emit sourceRemoved(source); } } bool DataEngine::isValid() const { return d->valid; } bool DataEngine::isEmpty() const { return d->sources.isEmpty(); } void DataEngine::setValid(bool valid) { d->valid = valid; } QHash DataEngine::containerDict() const { return d->sources; } void DataEngine::timerEvent(QTimerEvent *event) { //qCDebug(LOG_PLASMA); if (event->timerId() == d->updateTimerId) { // if the freq update is less than 0, don't bother if (d->minPollingInterval < 0) { //qCDebug(LOG_PLASMA) << "uh oh.. no polling allowed!"; return; } // minPollingInterval if (d->updateTimer.elapsed() < d->minPollingInterval) { //qCDebug(LOG_PLASMA) << "hey now.. slow down!"; return; } d->updateTimer.start(); updateAllSources(); } else if (event->timerId() == d->checkSourcesTimerId) { killTimer(d->checkSourcesTimerId); d->checkSourcesTimerId = 0; QHashIterator it(d->sources); while (it.hasNext()) { it.next(); it.value()->checkForUpdate(); } } else { QObject::timerEvent(event); } } void DataEngine::updateAllSources() { QHashIterator it(d->sources); while (it.hasNext()) { it.next(); //qCDebug(LOG_PLASMA) << "updating" << it.key(); if (it.value()->isUsed()) { updateSourceEvent(it.key()); } } d->scheduleSourcesUpdated(); } void DataEngine::forceImmediateUpdateOfAllVisualizations() { foreach (DataContainer *source, d->sources) { if (source->isUsed()) { source->forceImmediateUpdate(); } } } Package DataEngine::package() const { return d->package ? *d->package : Package(); } void DataEngine::setStorageEnabled(const QString &source, bool store) { DataContainer *s = d->source(source, false); if (s) { s->setStorageEnabled(store); } } // Private class implementations -DataEnginePrivate::DataEnginePrivate(DataEngine *e, const KPluginInfo &info, const QVariantList &args) +DataEnginePrivate::DataEnginePrivate(DataEngine *e, const KPluginMetaData &md, const QVariantList &args) : q(e), - dataEngineDescription(info), + dataEngineDescription(md), refCount(-1), // first ref checkSourcesTimerId(0), updateTimerId(0), minPollingInterval(-1), valid(false), script(nullptr), package(nullptr) { updateTimer.start(); if (dataEngineDescription.isValid()) { valid = true; e->setObjectName(dataEngineDescription.name()); } if (dataEngineDescription.isValid()) { - QString api = dataEngineDescription.property(QStringLiteral("X-Plasma-API")).toString(); + QString api = dataEngineDescription.value(QStringLiteral("X-Plasma-API")); if (!api.isEmpty()) { const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, - QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/dataengines/") + dataEngineDescription.pluginName(), + QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/dataengines/") + dataEngineDescription.pluginId(), QStandardPaths::LocateDirectory); package = new Package(PluginLoader::self()->loadPackage(QStringLiteral("Plasma/DataEngine"), api)); package->setPath(path); if (package->isValid()) { script = Plasma::loadScriptEngine(api, q, args); } if (!script) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Could not create a" << api << "ScriptEngine for the" // << dataEngineDescription.name() << "DataEngine."; #endif delete package; package = nullptr; } } } } DataEnginePrivate::~DataEnginePrivate() { delete script; script = nullptr; delete package; package = nullptr; } void DataEnginePrivate::internalUpdateSource(DataContainer *source) { if (minPollingInterval > 0 && source->timeSinceLastUpdate() < (uint)minPollingInterval) { // skip updating this source; it's been too soon //qCDebug(LOG_PLASMA) << "internal update source is delaying" << source->timeSinceLastUpdate() << minPollingInterval; //but fake an update so that the signalrelay that triggered this gets the data from the //recent update. this way we don't have to worry about queuing - the relay will send a //signal immediately and everyone else is undisturbed. source->setNeedsUpdate(); return; } if (q->updateSourceEvent(source->objectName())) { //qCDebug(LOG_PLASMA) << "queuing an update"; scheduleSourcesUpdated(); }/* else { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "no update"; #endif }*/ } void DataEnginePrivate::ref() { --refCount; } void DataEnginePrivate::deref() { ++refCount; } bool DataEnginePrivate::isUsed() const { return refCount != 0; } DataContainer *DataEnginePrivate::source(const QString &sourceName, bool createWhenMissing) { QHash::const_iterator it = sources.constFind(sourceName); if (it != sources.constEnd()) { DataContainer *s = it.value(); return s; } if (!createWhenMissing) { return nullptr; } //qCDebug(LOG_PLASMA) << "DataEngine " << q->objectName() << ": could not find DataContainer " << sourceName << ", creating"; DataContainer *s = new DataContainer(q); s->setObjectName(sourceName); sources.insert(sourceName, s); QObject::connect(s, SIGNAL(destroyed(QObject*)), q, SLOT(sourceDestroyed(QObject*))); QObject::connect(s, SIGNAL(updateRequested(DataContainer*)), q, SLOT(internalUpdateSource(DataContainer*))); return s; } void DataEnginePrivate::connectSource(DataContainer *s, QObject *visualization, uint pollingInterval, Plasma::Types::IntervalAlignment align, bool immediateCall) { //qCDebug(LOG_PLASMA) << "connect source called" << s->objectName() << "with interval" << pollingInterval; if (pollingInterval > 0) { // never more frequently than allowed, never more than 20 times per second uint min = qMax(50, minPollingInterval); // for qMax below pollingInterval = qMax(min, pollingInterval); // align on the 50ms pollingInterval = pollingInterval - (pollingInterval % 50); } if (immediateCall) { // we don't want to do an immediate call if we are simply // reconnecting //qCDebug(LOG_PLASMA) << "immediate call requested, we have:" << s->visualizationIsConnected(visualization); immediateCall = !s->data().isEmpty() && !s->visualizationIsConnected(visualization); } s->connectVisualization(visualization, pollingInterval, align); if (immediateCall) { QMetaObject::invokeMethod(visualization, "dataUpdated", Q_ARG(QString, s->objectName()), Q_ARG(Plasma::DataEngine::Data, s->data())); if (s->d->model) { QMetaObject::invokeMethod(visualization, "modelChanged", Q_ARG(QString, s->objectName()), Q_ARG(QAbstractItemModel*, s->d->model.data())); } s->d->dirty = false; } } void DataEnginePrivate::sourceDestroyed(QObject *object) { QHash::iterator it = sources.begin(); while (it != sources.end()) { if (it.value() == object) { sources.erase(it); emit q->sourceRemoved(object->objectName()); break; } ++it; } } DataContainer *DataEnginePrivate::requestSource(const QString &sourceName, bool *newSource) { if (newSource) { *newSource = false; } //qCDebug(LOG_PLASMA) << "requesting source " << sourceName; DataContainer *s = source(sourceName, false); if (!s) { // we didn't find a data source, so give the engine an opportunity to make one /*// qCDebug(LOG_PLASMA) << "DataEngine " << q->objectName() << ": could not find DataContainer " << sourceName << " will create on request" << endl;*/ waitingSourceRequest = sourceName; if (q->sourceRequestEvent(sourceName)) { s = source(sourceName, false); if (s) { // now we have a source; since it was created on demand, assume // it should be removed when not used if (newSource) { *newSource = true; } QObject::connect(s, &DataContainer::becameUnused, q, &DataEngine::removeSource); emit q->sourceAdded(sourceName); } } waitingSourceRequest.clear(); } return s; } // put all setup routines for script here. at this point we can assume that // package exists and that we have a script engine void DataEnginePrivate::setupScriptSupport() { if (!package) { return; } /* #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "sletting up script support, package is in" << package->path() #endif << "which is a" << package->structure()->type() << "package" << ", main script is" << package->filePath("mainscript"); */ // FIXME: Replace with ki18n functionality once semantics is clear. // const QString translationsPath = package->filePath("translations"); // if (!translationsPath.isEmpty()) { // KGlobal::dirs()->addResourceDir("locale", translationsPath); // } } void DataEnginePrivate::scheduleSourcesUpdated() { if (checkSourcesTimerId) { return; } checkSourcesTimerId = q->startTimer(0); } } #include "moc_dataengine.cpp" diff --git a/src/plasma/dataengine.h b/src/plasma/dataengine.h index cc1f42a4d..d7db23478 100644 --- a/src/plasma/dataengine.h +++ b/src/plasma/dataengine.h @@ -1,465 +1,492 @@ /* * Copyright 2006-2007 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PLASMA_DATAENGINE_H #define PLASMA_DATAENGINE_H #include #include #include #include #include #include #include #include class QAbstractItemModel; namespace Plasma { class DataContainer; class DataEngineScript; class Package; class Service; class DataEnginePrivate; /** * @class DataEngine plasma/dataengine.h * * @short Data provider for plasmoids (Plasma plugins) * * This is the base class for DataEngines, which provide access to bodies of * data via a common and consistent interface. The common use of a DataEngine * is to provide data to a widget for display. This allows a user interface * element to show all sorts of data: as long as there is a DataEngine, the * data is retrievable. * * DataEngines are loaded as plugins on demand and provide zero, one or more * data sources which are identified by name. For instance, a network * DataEngine might provide a data source for each network interface. **/ class PLASMA_EXPORT DataEngine : public QObject { Q_OBJECT public: typedef QHash Dict; typedef QMap Data; typedef QMapIterator DataIterator; typedef QHash SourceDict; +#if PLASMA_ENABLE_DEPRECATED_SINCE(5, 64) /** * Constructor. * * @param parent The parent object. * @param plugin plugin info that describes the engine + * + * @deprecated since 5.67 **/ + PLASMA_DEPRECATED_VERSION(5, 67, "Use KPluginMetaData") explicit DataEngine(const KPluginInfo &plugin, QObject *parent = nullptr); +#endif + + /** + * Constructor. + * + * @param parent The parent object. + * @param plugin metadata that describes the engine + * + * @since 5.62 + */ + explicit DataEngine(const KPluginMetaData &plugin, QObject *parent = nullptr); explicit DataEngine(QObject *parent = nullptr, const QVariantList &args = QVariantList()); ~DataEngine() override; /** * @return a list of all the data sources available via this DataEngine * Whether these sources are currently available (which is what * the default implementation provides) or not is up to the * DataEngine to decide. **/ virtual QStringList sources() const; /** * @param source the source to target the Service at * @return a Service that has the source as a destination. The service * is parented to the DataEngine, but should be deleted by the * caller when finished with it */ Q_INVOKABLE virtual Service *serviceForSource(const QString &source); +#if PLASMA_ENABLE_DEPRECATED_SINCE(5, 64) /** * @return description of the plugin that implements this DataEngine + * + * @deprecated since 5.67, use metadata */ + PLASMA_DEPRECATED_VERSION(5, 67, "Use metadata()") KPluginInfo pluginInfo() const; +#endif + + /** + * @return description of the plugin that implements this DataEngine + * + * @since 5.62 + */ + KPluginMetaData metadata() const; /** * Connects a source to an object for data updates. The object must * have a slot with the following signature: * @code * void dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data); * @endcode * * The data is a QHash of QVariants keyed by QString names, allowing * one data source to provide sets of related data. * * @param source the name of the data source * @param visualization the object to connect the data source to * @param pollingInterval the frequency, in milliseconds, with which to check for updates; * a value of 0 (the default) means to update only * when there is new data spontaneously generated * (e.g. by the engine); any other value results in * periodic updates from this source. This value is * per-visualization and can be handy for items that require * constant updates such as scrolling graphs or clocks. * If the data has not changed, no update will be sent. * @param intervalAlignment the number of ms to align the interval to **/ Q_INVOKABLE void connectSource( const QString &source, QObject *visualization, uint pollingInterval = 0, Plasma::Types::IntervalAlignment intervalAlignment = Types::NoAlignment) const; /** * Connects all currently existing sources to an object for data updates. * The object must have a slot with the following signature: * @code * void dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data); * @endcode * * The data is a QHash of QVariants keyed by QString names, allowing * one data source to provide sets of related data. * * This method may be called multiple times for the same visualization * without side-effects. This can be useful to change the pollingInterval. * * Note that this method does not automatically connect sources that * may appear later on. Connecting and responding to the sourceAdded signal * is still required to achieve that. * * @param visualization the object to connect the data source to * @param pollingInterval the frequency, in milliseconds, with which to check for updates; * a value of 0 (the default) means to update only * when there is new data spontaneously generated * (e.g. by the engine); any other value results in * periodic updates from this source. This value is * per-visualization and can be handy for items that require * constant updates such as scrolling graphs or clocks. * If the data has not changed, no update will be sent. * @param intervalAlignment the number of ms to align the interval to **/ Q_INVOKABLE void connectAllSources(QObject *visualization, uint pollingInterval = 0, Plasma::Types::IntervalAlignment intervalAlignment = Types::NoAlignment) const; /** * Disconnects a source from an object that was receiving data updates. * * @param source the name of the data source * @param visualization the object to connect the data source to **/ Q_INVOKABLE void disconnectSource(const QString &source, QObject *visualization) const; /** * Retrieves a pointer to the DataContainer for a given source. This method * should not be used if possible. An exception is for script engines that * can not provide a QMetaObject as required by connectSource for the initial * call to dataUpdated. Using this method, such engines can provide their own * connectSource API. * * @param source the name of the source. * @return pointer to a DataContainer, or zero on failure **/ Q_INVOKABLE DataContainer *containerForSource(const QString &source); /** * @return The model associated to a source if any. The ownership of the model stays with the DataContainer. * Returns 0 if there isn't any model associated or if the source doesn't exists. */ QAbstractItemModel *modelForSource(const QString &source); /** * Returns true if this engine is valid, otherwise returns false * * @return true if the engine is valid **/ bool isValid() const; /** * Returns true if the data engine is empty, which is to say that it has no * data sources currently. * * @return true if the engine has no sources currently */ bool isEmpty() const; /** * @return the name of the icon for this data engine; and empty string * is returned if there is no associated icon. **/ QString icon() const; /** * Accessor for the associated Package object if any. * * @return the Package object, or 0 if none **/ Package package() const; Q_SIGNALS: /** * Emitted when a new data source is created * * Note that you do not need to emit this yourself unless * you are reimplementing sources() and want to advertise * that a new source is available (but hasn't been created * yet). * * @param source the name of the new data source **/ void sourceAdded(const QString &source); /** * Emitted when a data source is removed. * * Note that you do not need to emit this yourself unless * you have reimplemented sources() and want to signal that * a source that was available but was never created is no * longer available. * * @param source the name of the data source that was removed **/ void sourceRemoved(const QString &source); protected: /** * When a source that does not currently exist is requested by the * consumer, this method is called to give the DataEngine the * opportunity to create one. * * The name of the data source (e.g. the source parameter passed into * setData) must be the same as the name passed to sourceRequestEvent * otherwise the requesting visualization may not receive notice of a * data update. * * If the source can not be populated with data immediately (e.g. due to * an asynchronous data acquisition method such as an HTTP request) * the source must still be created, even if it is empty. This can * be accomplished in these cases with the follow line: * * setData(name, DataEngine::Data()); * * @param source the name of the source that has been requested * @return true if a DataContainer was set up, false otherwise */ virtual bool sourceRequestEvent(const QString &source); /** * Called by internal updating mechanisms to trigger the engine * to refresh the data contained in a given source. Reimplement this * method when using facilities such as setPollingInterval. * @see setPollingInterval * * @param source the name of the source that should be updated * @return true if the data was changed, or false if there was no * change or if the change will occur later **/ virtual bool updateSourceEvent(const QString &source); /** * Sets a value for a data source. If the source * doesn't exist then it is created. * * @param source the name of the data source * @param value the data to associated with the source **/ void setData(const QString &source, const QVariant &value); /** * Sets a value for a data source. If the source * doesn't exist then it is created. * * @param source the name of the data source * @param key the key to use for the data * @param value the data to associated with the source **/ void setData(const QString &source, const QString &key, const QVariant &value); /** * Adds a set of data to a data source. If the source * doesn't exist then it is created. * * @param source the name of the data source * @param data the data to add to the source **/ void setData(const QString &source, const QVariantMap &data); /** * Removes all the data associated with a data source. * * @param source the name of the data source **/ void removeAllData(const QString &source); /** * Removes a data entry from a source * * @param source the name of the data source * @param key the data entry to remove **/ void removeData(const QString &source, const QString &key); /** * Associates a model to a data source. If the source * doesn't exist then it is created. The source will have the key "HasModel" to easily indicate there is a model present. * * The ownership of the model is transferred to the DataContainer, * so the model will be deleted when a new one is set or when the * DataContainer itself is deleted. As the DataContainer, it will be * deleted when there won't be any * visualization associated to this source. * * @param source the name of the data source * @param model the model instance */ void setModel(const QString &source, QAbstractItemModel *model); /** * Adds an already constructed data source. The DataEngine takes * ownership of the DataContainer object. The objectName of the source * is used for the source name. * * @param source the DataContainer to add to the DataEngine **/ void addSource(DataContainer *source); /** * Sets the minimum amount of time, in milliseconds, that must pass between * successive updates of data. This can help prevent too many updates happening * due to multiple update requests coming in, which can be useful for * expensive (time- or resource-wise) update mechanisms. * * The default minimumPollingInterval is -1, or "never perform automatic updates" * * @param minimumMs the minimum time lapse, in milliseconds, between updates. * A value less than 0 means to never perform automatic updates, * a value of 0 means update immediately on every update request, * a value >0 will result in a minimum time lapse being enforced. **/ void setMinimumPollingInterval(int minimumMs); /** * @return the minimum time between updates. @see setMinimumPollingInterval **/ int minimumPollingInterval() const; /** * Sets up an internal update tick for all data sources. On every update, * updateSourceEvent will be called for each applicable source. * @see updateSourceEvent * * @param frequency the time, in milliseconds, between updates. A value of 0 * will stop internally triggered updates. **/ void setPollingInterval(uint frequency); /** * Removes all data sources **/ void removeAllSources(); /** * Sets whether or not this engine is valid, e.g. can be used. * In practice, only the internal fall-back engine, the NullEngine * should have need for this. * * @param valid whether or not the engine is valid **/ void setValid(bool valid); /** * @return the list of active DataContainers. */ QHash containerDict() const; /** * Reimplemented from QObject **/ void timerEvent(QTimerEvent *event) override; /** * Sets the icon for this data engine **/ void setIcon(const QString &icon); /** * Sets a source to be stored for easy retrieval * when the real source of the data (usually a network connection) * is unavailable. * @param source the name of the source * @param store if source should be stored * @since 4.6 */ void setStorageEnabled(const QString &source, bool store); protected Q_SLOTS: /** * Removes a data source. * @param source the name of the data source to remove **/ void removeSource(const QString &source); /** * Immediately updates all existing sources when called */ void updateAllSources(); /** * Forces an immediate update to all connected sources, even those with * timeouts that haven't yet expired. This should _only_ be used when * there was no data available, e.g. due to network non-availability, * and then it becomes available. Normal changes in data values due to * calls to updateSource or in the natural progression of the monitored * object (e.g. CPU heat) should not result in a call to this method! * * @since 4.4 */ void forceImmediateUpdateOfAllVisualizations(); private: friend class DataEnginePrivate; friend class DataEngineScript; friend class DataEngineManager; friend class PlasmoidServiceJob; friend class NullEngine; Q_PRIVATE_SLOT(d, void internalUpdateSource(DataContainer *source)) Q_PRIVATE_SLOT(d, void sourceDestroyed(QObject *object)) Q_PRIVATE_SLOT(d, void scheduleSourcesUpdated()) DataEnginePrivate *const d; }; } // Plasma namespace /** * Register a data engine when it is contained in a loadable module */ #define K_EXPORT_PLASMA_DATAENGINE(libname, classname) \ K_PLUGIN_FACTORY(factory, registerPlugin();) \ K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION) #define K_EXPORT_PLASMA_DATAENGINE_WITH_JSON(libname, classname, jsonFile) \ K_PLUGIN_FACTORY_WITH_JSON(factory, jsonFile, registerPlugin();) \ K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION) Q_DECLARE_METATYPE(Plasma::DataEngine *) #endif // multiple inclusion guard diff --git a/src/plasma/private/dataengine_p.h b/src/plasma/private/dataengine_p.h index 9a4b22700..5394b405a 100644 --- a/src/plasma/private/dataengine_p.h +++ b/src/plasma/private/dataengine_p.h @@ -1,109 +1,109 @@ /* * Copyright 2006-2007 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DATAENGINE_P_H #define DATAENGINE_P_H #include -#include +#include namespace Plasma { class Service; class DataEnginePrivate { public: - DataEnginePrivate(DataEngine *e, const KPluginInfo &info, const QVariantList &args = QVariantList()); + DataEnginePrivate(DataEngine *e, const KPluginMetaData &info, const QVariantList &args = QVariantList()); ~DataEnginePrivate(); DataContainer *source(const QString &sourceName, bool createWhenMissing = true); void connectSource(DataContainer *s, QObject *visualization, uint pollingInterval, Plasma::Types::IntervalAlignment align, bool immediateCall = true); DataContainer *requestSource(const QString &sourceName, bool *newSource = nullptr); void internalUpdateSource(DataContainer *); void setupScriptSupport(); /** * Reference counting method. Calling this method increases the count * by one. **/ void ref(); /** * Reference counting method. Calling this method decreases the count * by one. **/ void deref(); /** * Reference counting method. Used to determine if this DataEngine is * used. * @return true if the reference count is non-zero **/ bool isUsed() const; /** * a datacontainer has been destroyed, clean up stuff */ void sourceDestroyed(QObject *object); /** * stores the source * @param sourceName the name of the source to store */ void storeSource(DataContainer *source) const; /** * stores all sources marked for storage */ void storeAllSources(); /** * retrieves source data * @param the data container to populate */ void retrieveStoredData(DataContainer *s); /** * Call this method when you call setData directly on a DataContainer instead * of using the DataEngine::setData methods. * If this method is not called, no dataUpdated(..) signals will be emitted! */ void scheduleSourcesUpdated(); DataEngine *q; - KPluginInfo dataEngineDescription; + KPluginMetaData dataEngineDescription; int refCount; int checkSourcesTimerId; int updateTimerId; int minPollingInterval; QElapsedTimer updateTimer; DataEngine::SourceDict sources; bool valid; DataEngineScript *script; QString serviceName; Package *package; QString waitingSourceRequest; }; } // Plasma namespace #endif // multiple inclusion guard diff --git a/src/plasma/private/storage.cpp b/src/plasma/private/storage.cpp index 6d4c5c41d..06c1e82d6 100644 --- a/src/plasma/private/storage.cpp +++ b/src/plasma/private/storage.cpp @@ -1,149 +1,149 @@ ///////////////////////////////////////////////////////////////////////// // storage.cpp // // // // Copyright (C) 2010 Brian Pritchett // // Copyright (C) 2010 Marco Martin // // // // This library is free software; you can redistribute it and/or // // modify it under the terms of the GNU Lesser General Public // // License as published by the Free Software Foundation; either // // version 2.1 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 // // Lesser General Public License for more details. // // // // You should have received a copy of the GNU Lesser General Public // // License along with this library; if not, write to the Free Software // // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // // 02110-1301 USA // ///////////////////////////////////////////////////////////////////////// #include "private/storage_p.h" //Qt #include #include #include #include #include #include //KDE #include //Plasma #include "applet.h" #include "dataengine.h" #include "storagethread_p.h" StorageJob::StorageJob(const QString &destination, const QString &operation, const QVariantMap ¶meters, QObject *parent) : ServiceJob(destination, operation, parameters, parent), m_clientName(destination) { Plasma::StorageThread::self()->start(); connect(Plasma::StorageThread::self(), &Plasma::StorageThread::newResult, this, &StorageJob::resultSlot); qRegisterMetaType(); qRegisterMetaType >(); } StorageJob::~StorageJob() { } void StorageJob::setData(const QVariantMap &data) { m_data = data; } QVariantMap StorageJob::data() const { return m_data; } QString StorageJob::clientName() const { return m_clientName; } void StorageJob::start() { //FIXME: QHASH QVariantMap params = parameters(); QString valueGroup = params[QStringLiteral("group")].toString(); if (valueGroup.isEmpty()) { valueGroup = QStringLiteral("default"); } QPointer me(this); if (operationName() == QLatin1String("save")) { QMetaObject::invokeMethod(Plasma::StorageThread::self(), "save", Qt::QueuedConnection, Q_ARG(QPointer, me), Q_ARG(QVariantMap, params)); } else if (operationName() == QLatin1String("retrieve")) { QMetaObject::invokeMethod(Plasma::StorageThread::self(), "retrieve", Qt::QueuedConnection, Q_ARG(QPointer, me), Q_ARG(QVariantMap, params)); } else if (operationName() == QLatin1String("delete")) { QMetaObject::invokeMethod(Plasma::StorageThread::self(), "deleteEntry", Qt::QueuedConnection, Q_ARG(QPointer, me), Q_ARG(QVariantMap, params)); } else if (operationName() == QLatin1String("expire")) { QMetaObject::invokeMethod(Plasma::StorageThread::self(), "expire", Qt::QueuedConnection, Q_ARG(QPointer, me), Q_ARG(QVariantMap, params)); } else { setError(true); setResult(false); } } void StorageJob::resultSlot(StorageJob *job, const QVariant &result) { if (job == this) { if (result.type() == QVariant::Map) { m_data = result.toMap(); } setResult(result); } } Plasma::ServiceJob *Storage::createJob(const QString &operation, QVariantMap ¶meters) { if (m_clientName.isEmpty()) { return nullptr; } return new StorageJob(m_clientName, operation, parameters, this); } //Storage implementation Storage::Storage(QObject *parent) : Plasma::Service(parent), m_clientName(QStringLiteral("data")) { //search among parents for an applet or dataengine: if found call the table as its plugin name QObject *parentObject = this; while ((parentObject = parentObject->parent())) { Plasma::Applet *applet = qobject_cast(parentObject); if (applet) { m_clientName = applet->pluginMetaData().pluginId(); break; } Plasma::DataEngine *engine = qobject_cast(parentObject); if (engine) { - m_clientName = engine->pluginInfo().pluginName(); + m_clientName = engine->metadata().pluginId(); break; } } m_clientName.replace(QLatin1Char('.'), QLatin1Char('_')); m_clientName.replace(QLatin1Char('-'), QLatin1Char('_')); setName(QStringLiteral("storage")); } Storage::~Storage() { } #include "moc_storage_p.cpp" diff --git a/src/plasma/private/theme_p.cpp b/src/plasma/private/theme_p.cpp index 804d1662a..b0731c8f3 100644 --- a/src/plasma/private/theme_p.cpp +++ b/src/plasma/private/theme_p.cpp @@ -1,891 +1,891 @@ /* * Copyright 2006-2007 Aaron Seigo * 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 "theme_p.h" #include "framesvg.h" #include "framesvg_p.h" #include "debug_p.h" #include #include #include #include #include #include #include #include #include namespace Plasma { const char ThemePrivate::defaultTheme[] = "default"; const char ThemePrivate::themeRcFile[] = "plasmarc"; // the system colors theme is used to cache unthemed svgs with colorization needs // these svgs do not follow the theme's colors, but rather the system colors const char ThemePrivate::systemColorsTheme[] = "internal-system-colors"; #if HAVE_X11 EffectWatcher *ThemePrivate::s_backgroundContrastEffectWatcher = nullptr; #endif ThemePrivate *ThemePrivate::globalTheme = nullptr; QHash ThemePrivate::themes = QHash(); ThemePrivate::ThemePrivate(QObject *parent) : QObject(parent), colorScheme(QPalette::Active, KColorScheme::Window, KSharedConfigPtr(nullptr)), selectionColorScheme(QPalette::Active, KColorScheme::Selection, KSharedConfigPtr(nullptr)), buttonColorScheme(QPalette::Active, KColorScheme::Button, KSharedConfigPtr(nullptr)), viewColorScheme(QPalette::Active, KColorScheme::View, KSharedConfigPtr(nullptr)), complementaryColorScheme(QPalette::Active, KColorScheme::Complementary, KSharedConfigPtr(nullptr)), defaultWallpaperTheme(QStringLiteral(DEFAULT_WALLPAPER_THEME)), defaultWallpaperSuffix(QStringLiteral(DEFAULT_WALLPAPER_SUFFIX)), defaultWallpaperWidth(DEFAULT_WALLPAPER_WIDTH), defaultWallpaperHeight(DEFAULT_WALLPAPER_HEIGHT), pixmapCache(nullptr), cacheSize(0), cachesToDiscard(NoCache), compositingActive(KWindowSystem::self()->compositingActive()), backgroundContrastActive(KWindowEffects::isEffectAvailable(KWindowEffects::BackgroundContrast)), isDefault(true), useGlobal(true), hasWallpapers(false), fixedName(false), backgroundContrast(qQNaN()), backgroundIntensity(qQNaN()), backgroundSaturation(qQNaN()), backgroundContrastEnabled(true), blurBehindEnabled(true), apiMajor(1), apiMinor(0), apiRevision(0) { ThemeConfig config; cacheTheme = config.cacheTheme(); pixmapSaveTimer = new QTimer(this); pixmapSaveTimer->setSingleShot(true); pixmapSaveTimer->setInterval(600); QObject::connect(pixmapSaveTimer, &QTimer::timeout, this, &ThemePrivate::scheduledCacheUpdate); rectSaveTimer = new QTimer(this); rectSaveTimer->setSingleShot(true); //2 minutes rectSaveTimer->setInterval(2 * 60 * 1000); QObject::connect(rectSaveTimer, &QTimer::timeout, this, &ThemePrivate::saveSvgElementsCache); updateNotificationTimer = new QTimer(this); updateNotificationTimer->setSingleShot(true); updateNotificationTimer->setInterval(100); QObject::connect(updateNotificationTimer, &QTimer::timeout, this, &ThemePrivate::notifyOfChanged); if (QPixmap::defaultDepth() > 8) { #if HAVE_X11 //watch for background contrast effect property changes as well if (!s_backgroundContrastEffectWatcher) { s_backgroundContrastEffectWatcher = new EffectWatcher(QStringLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION")); } QObject::connect(s_backgroundContrastEffectWatcher, &EffectWatcher::effectChanged, this, [this](bool active) { if (backgroundContrastActive != active) { backgroundContrastActive = active; scheduleThemeChangeNotification(PixmapCache | SvgElementsCache); } }); #endif } QCoreApplication::instance()->installEventFilter(this); const QString configFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + QLatin1String(themeRcFile); KDirWatch::self()->addFile(configFile); // Catch both, direct changes to the config file ... connect(KDirWatch::self(), &KDirWatch::dirty, this, &ThemePrivate::settingsFileChanged); // ... but also remove/recreate cycles, like KConfig does it connect(KDirWatch::self(), &KDirWatch::created, this, &ThemePrivate::settingsFileChanged); QObject::connect(KIconLoader::global(), &KIconLoader::iconChanged, this, [this]() { scheduleThemeChangeNotification(PixmapCache|SvgElementsCache); }); connect(KWindowSystem::self(), &KWindowSystem::compositingChanged, this, &ThemePrivate::compositingChanged); } ThemePrivate::~ThemePrivate() { saveSvgElementsCache(); FrameSvgPrivate::s_sharedFrames.remove(this); delete pixmapCache; } KConfigGroup &ThemePrivate::config() { if (!cfg.isValid()) { QString groupName = QStringLiteral("Theme"); if (!useGlobal) { QString app = QCoreApplication::applicationName(); if (!app.isEmpty()) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "using theme for app" << app; #endif groupName.append(QLatin1Char('-')).append(app); } } cfg = KConfigGroup(KSharedConfig::openConfig(QFile::decodeName(themeRcFile)), groupName); } return cfg; } bool ThemePrivate::useCache() { bool cachesTooOld = false; if (cacheTheme && !pixmapCache) { if (cacheSize == 0) { ThemeConfig config; cacheSize = config.themeCacheKb(); } const bool isRegularTheme = themeName != QLatin1String(systemColorsTheme); QString cacheFile = QLatin1String("plasma_theme_") + themeName; // clear any cached values from the previous theme cache themeVersion.clear(); if (!themeMetadataPath.isEmpty()) { KDirWatch::self()->removeFile(themeMetadataPath); } if (isRegularTheme) { themeMetadataPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % themeName % QStringLiteral("/metadata.desktop")); const auto *iconTheme = KIconLoader::global()->theme(); if (iconTheme) { iconThemeMetadataPath = iconTheme->dir() + QStringLiteral("index.theme"); } Q_ASSERT(!themeMetadataPath.isEmpty() || themeName.isEmpty()); const QString cacheFileBase = cacheFile + QLatin1String("*.kcache"); QString currentCacheFileName; if (!themeMetadataPath.isEmpty()) { // now we record the theme version, if we can const KPluginInfo pluginInfo(themeMetadataPath); if (pluginInfo.isValid()) { themeVersion = pluginInfo.version(); } if (!themeVersion.isEmpty()) { cacheFile += QLatin1String("_v") + themeVersion; currentCacheFileName = cacheFile + QLatin1String(".kcache"); } // watch the metadata file for changes at runtime KDirWatch::self()->addFile(themeMetadataPath); QObject::connect(KDirWatch::self(), &KDirWatch::created, this, &ThemePrivate::settingsFileChanged, Qt::UniqueConnection); QObject::connect(KDirWatch::self(), &KDirWatch::dirty, this, &ThemePrivate::settingsFileChanged, Qt::UniqueConnection); if (!iconThemeMetadataPath.isEmpty()) { KDirWatch::self()->addFile(iconThemeMetadataPath); } } // now we check for, and remove if necessary, old caches QDir cacheDir(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)); cacheDir.setNameFilters(QStringList({cacheFileBase})); const auto files = cacheDir.entryInfoList(); for (const QFileInfo &file : files) { if (currentCacheFileName.isEmpty() || !file.absoluteFilePath().endsWith(currentCacheFileName)) { QFile::remove(file.absoluteFilePath()); } } } // now we do a sanity check: if the metadata.desktop file is newer than the cache, drop the cache if (isRegularTheme && !themeMetadataPath.isEmpty()) { // now we check to see if the theme metadata file itself is newer than the pixmap cache // this is done before creating the pixmapCache object since that can change the mtime // on the cache file // FIXME: when using the system colors, if they change while the application is not running // the cache should be dropped; we need a way to detect system color change when the // application is not running. // check for expired cache const QString cacheFilePath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1Char('/') + cacheFile + QLatin1String(".kcache"); if (!cacheFilePath.isEmpty()) { const QFileInfo cacheFileInfo(cacheFilePath); const QFileInfo metadataFileInfo(themeMetadataPath); const QFileInfo iconThemeMetadataFileInfo(iconThemeMetadataPath); cachesTooOld = (cacheFileInfo.lastModified().toSecsSinceEpoch() < metadataFileInfo.lastModified().toSecsSinceEpoch()) || (cacheFileInfo.lastModified().toSecsSinceEpoch() < iconThemeMetadataFileInfo.lastModified().toSecsSinceEpoch()); } } ThemeConfig config; pixmapCache = new KImageCache(cacheFile, config.themeCacheKb() * 1024); if (cachesTooOld) { discardCache(PixmapCache | SvgElementsCache); } } if (cacheTheme && !svgElementsCache) { const QString svgElementsFileNameBase = QLatin1String("plasma-svgelements-") + themeName; QString svgElementsFileName = svgElementsFileNameBase; if (!themeVersion.isEmpty()) { svgElementsFileName += QLatin1String("_v") + themeVersion; } // now we check for (and remove) old caches QDir cacheDir(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)); cacheDir.setNameFilters(QStringList({svgElementsFileNameBase + QLatin1Char('*')})); const auto files = cacheDir.entryInfoList(); for (const QFileInfo &file : files) { if (!file.absoluteFilePath().endsWith(svgElementsFileName)) { QFile::remove(file.absoluteFilePath()); } } const QString svgElementsFile = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1Char('/') + svgElementsFileName; svgElementsCache = KSharedConfig::openConfig(svgElementsFile, KConfig::SimpleConfig); QString currentIconThemePath; const auto *iconTheme = KIconLoader::global()->theme(); if (iconTheme) { currentIconThemePath = iconTheme->dir(); } KConfigGroup globalGroup(svgElementsCache, QLatin1String("Global")); const QString oldIconThemePath = globalGroup.readEntry("currentIconThemePath", QString()); if (oldIconThemePath != currentIconThemePath) { discardCache(PixmapCache | SvgElementsCache); globalGroup.writeEntry("currentIconThemePath", currentIconThemePath); svgElementsCache = KSharedConfig::openConfig(svgElementsFile, KConfig::SimpleConfig); } } return cacheTheme; } void ThemePrivate::onAppExitCleanup() { pixmapsToCache.clear(); delete pixmapCache; pixmapCache = nullptr; cacheTheme = false; } QString ThemePrivate::imagePath(const QString& theme, const QString& type, const QString& image) { QString subdir = QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % type % image; return QStandardPaths::locate(QStandardPaths::GenericDataLocation, subdir); } QString ThemePrivate::findInTheme(const QString &image, const QString &theme, bool cache) { if (cache) { auto it = discoveries.constFind(image); if (it != discoveries.constEnd()) { return it.value(); } } QString type = QStringLiteral("/"); if (!compositingActive) { type = QStringLiteral("/opaque/"); } else if (backgroundContrastActive) { type = QStringLiteral("/translucent/"); } QString search = imagePath(theme, type, image); //not found or compositing enabled if (search.isEmpty()) { search = imagePath(theme, QStringLiteral("/"), image); } if (cache && !search.isEmpty()) { discoveries.insert(image, search); } return search; } void ThemePrivate::compositingChanged(bool active) { #if HAVE_X11 if (compositingActive != active) { compositingActive = active; //qCDebug(LOG_PLASMA) << QTime::currentTime(); scheduleThemeChangeNotification(PixmapCache | SvgElementsCache); } #endif } void ThemePrivate::discardCache(CacheTypes caches) { if (caches & PixmapCache) { pixmapsToCache.clear(); pixmapSaveTimer->stop(); if (pixmapCache) { pixmapCache->clear(); } } else { // This deletes the object but keeps the on-disk cache for later use delete pixmapCache; pixmapCache = nullptr; } cachedDefaultStyleSheet = QString(); cachedSvgStyleSheets.clear(); cachedSelectedSvgStyleSheets.clear(); if (caches & SvgElementsCache) { discoveries.clear(); invalidElements.clear(); svgElementsCache = nullptr; } } void ThemePrivate::scheduledCacheUpdate() { if (useCache()) { QHashIterator it(pixmapsToCache); while (it.hasNext()) { it.next(); pixmapCache->insertPixmap(idsToCache[it.key()], it.value()); } } pixmapsToCache.clear(); keysToCache.clear(); idsToCache.clear(); } void ThemePrivate::colorsChanged() { // in the case the theme follows the desktop settings, refetch the colorschemes // and discard the svg pixmap cache if (!colors) { KSharedConfig::openConfig()->reparseConfiguration(); } colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors); buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors); viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors); selectionColorScheme = KColorScheme(QPalette::Active, KColorScheme::Selection, colors); scheduleThemeChangeNotification(PixmapCache | SvgElementsCache); emit applicationPaletteChange(); } void ThemePrivate::scheduleThemeChangeNotification(CacheTypes caches) { cachesToDiscard |= caches; updateNotificationTimer->start(); } void ThemePrivate::notifyOfChanged() { //qCDebug(LOG_PLASMA) << cachesToDiscard; discardCache(cachesToDiscard); cachesToDiscard = NoCache; emit themeChanged(); } const QString ThemePrivate::processStyleSheet(const QString &css, Plasma::Svg::Status status) { QString stylesheet; if (css.isEmpty()) { stylesheet = cachedDefaultStyleSheet; if (stylesheet.isEmpty()) { stylesheet = QStringLiteral("\n\ body {\n\ color: %textcolor;\n\ generalfont-size: %fontsize;\n\ font-family: %fontfamily;\n\ }\n\ a:active { color: %activatedlink; }\n\ a:link { color: %link; }\n\ a:visited { color: %visitedlink; }\n\ a:hover { color: %hoveredlink; text-decoration: none; }\n\ "); stylesheet = cachedDefaultStyleSheet = processStyleSheet(stylesheet, status); } return stylesheet; } else { stylesheet = css; } QHash elements; // If you add elements here, make sure their names are sufficiently unique to not cause // clashes between element keys elements[QStringLiteral("%textcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%backgroundcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%highlightcolor")] = color(Theme::HighlightColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%highlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%visitedlink")] = color(Theme::VisitedLinkColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%activatedlink")] = color(Theme::HighlightColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%hoveredlink")] = color(Theme::HighlightColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%link")] = color(Theme::LinkColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%positivetextcolor")] = color(Theme::PositiveTextColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%neutraltextcolor")] = color(Theme::NeutralTextColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%negativetextcolor")] = color(Theme::NegativeTextColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%buttontextcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonbackgroundcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonhovercolor")] = color(Theme::HoverColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonfocuscolor")] = color(Theme::FocusColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonhighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonpositivetextcolor")] = color(Theme::PositiveTextColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonnegativetextcolor")] = color(Theme::NegativeTextColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%viewtextcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewbackgroundcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewhovercolor")] = color(Theme::HoverColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewfocuscolor")] = color(Theme::FocusColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewhighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewpositivetextcolor")] = color(Theme::PositiveTextColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewnegativetextcolor")] = color(Theme::NegativeTextColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%complementarytextcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementarybackgroundcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementaryhovercolor")] = color(Theme::HoverColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementaryfocuscolor")] = color(Theme::FocusColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementaryhighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementarypositivetextcolor")] = color(Theme::PositiveTextColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementaryneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementarynegativetextcolor")] = color(Theme::NegativeTextColor, Theme::ComplementaryColorGroup).name(); QFont font = QGuiApplication::font(); elements[QStringLiteral("%fontsize")] = QStringLiteral("%1pt").arg(font.pointSize()); elements[QStringLiteral("%fontfamily")] = font.family().splitRef(QLatin1Char('[')).first().toString(); elements[QStringLiteral("%smallfontsize")] = QStringLiteral("%1pt").arg(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont).pointSize()); QHash::const_iterator it = elements.constBegin(); QHash::const_iterator itEnd = elements.constEnd(); for (; it != itEnd; ++it) { stylesheet.replace(it.key(), it.value()); } return stylesheet; } const QString ThemePrivate::svgStyleSheet(Plasma::Theme::ColorGroup group, Plasma::Svg::Status status) { QString stylesheet = (status == Svg::Status::Selected) ? cachedSelectedSvgStyleSheets.value(group) : cachedSvgStyleSheets.value(group); if (stylesheet.isEmpty()) { QString skel = QStringLiteral(".ColorScheme-%1{color:%2;}"); switch (group) { case Theme::ButtonColorGroup: stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%buttontextcolor")); stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%buttonbackgroundcolor")); stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%buttonhovercolor")); stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%buttonhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%buttonpositivetextcolor")); stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%buttonneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%buttonnegativetextcolor")); break; case Theme::ViewColorGroup: stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%viewtextcolor")); stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%viewbackgroundcolor")); stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%viewhovercolor")); stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%viewhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%viewpositivetextcolor")); stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%viewneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%viewnegativetextcolor")); break; case Theme::ComplementaryColorGroup: stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%complementarytextcolor")); stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%complementarybackgroundcolor")); stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%complementaryhovercolor")); stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%complementaryhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%complementarypositivetextcolor")); stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%complementaryneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%complementarynegativetextcolor")); break; default: stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%textcolor")); stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%backgroundcolor")); stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%highlightcolor")); stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%highlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%positivetextcolor")); stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%neutraltextcolor")); stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%negativetextcolor")); } stylesheet += skel.arg(QStringLiteral("ButtonText"), QStringLiteral("%buttontextcolor")); stylesheet += skel.arg(QStringLiteral("ButtonBackground"), QStringLiteral("%buttonbackgroundcolor")); stylesheet += skel.arg(QStringLiteral("ButtonHover"), QStringLiteral("%buttonhovercolor")); stylesheet += skel.arg(QStringLiteral("ButtonFocus"), QStringLiteral("%buttonfocuscolor")); stylesheet += skel.arg(QStringLiteral("ButtonHighlightedText"), QStringLiteral("%buttonhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("ButtonPositiveText"), QStringLiteral("%buttonpositivetextcolor")); stylesheet += skel.arg(QStringLiteral("ButtonNeutralText"), QStringLiteral("%buttonneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("ButtonNegativeText"), QStringLiteral("%buttonnegativetextcolor")); stylesheet += skel.arg(QStringLiteral("ViewText"), QStringLiteral("%viewtextcolor")); stylesheet += skel.arg(QStringLiteral("ViewBackground"), QStringLiteral("%viewbackgroundcolor")); stylesheet += skel.arg(QStringLiteral("ViewHover"), QStringLiteral("%viewhovercolor")); stylesheet += skel.arg(QStringLiteral("ViewFocus"), QStringLiteral("%viewfocuscolor")); stylesheet += skel.arg(QStringLiteral("ViewHighlightedText"), QStringLiteral("%viewhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("ViewPositiveText"), QStringLiteral("%viewpositivetextcolor")); stylesheet += skel.arg(QStringLiteral("ViewNeutralText"), QStringLiteral("%viewneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("ViewNegativeText"), QStringLiteral("%viewnegativetextcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryText"), QStringLiteral("%complementarytextcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryBackground"), QStringLiteral("%complementarybackgroundcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryHover"), QStringLiteral("%complementaryhovercolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryFocus"), QStringLiteral("%complementaryfocuscolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryHighlightedText"), QStringLiteral("%complementaryhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryPositiveText"), QStringLiteral("%complementarypositivetextcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryNeutralText"), QStringLiteral("%complementaryneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryNegativeText"), QStringLiteral("%complementarynegativetextcolor")); stylesheet = processStyleSheet(stylesheet, status); if (status == Svg::Status::Selected) { cachedSelectedSvgStyleSheets.insert(group, stylesheet); } else { cachedSvgStyleSheets.insert(group, stylesheet); } } return stylesheet; } void ThemePrivate::settingsFileChanged(const QString &file) { qCDebug(LOG_PLASMA) << "settingsFile: " << file; if (file == themeMetadataPath) { const KPluginInfo pluginInfo(themeMetadataPath); if (!pluginInfo.isValid() || themeVersion != pluginInfo.version()) { scheduleThemeChangeNotification(SvgElementsCache); } } else if (file.endsWith(QLatin1String(themeRcFile))) { config().config()->reparseConfiguration(); settingsChanged(true); } } void ThemePrivate::settingsChanged(bool emitChanges) { if (fixedName) { return; } //qCDebug(LOG_PLASMA) << "Settings Changed!"; KConfigGroup cg = config(); setThemeName(cg.readEntry("name", ThemePrivate::defaultTheme), false, emitChanges); } void ThemePrivate::saveSvgElementsCache() { if (svgElementsCache) { QHashIterator > it(invalidElements); while (it.hasNext()) { it.next(); KConfigGroup imageGroup(svgElementsCache, it.key()); imageGroup.writeEntry("invalidElements", it.value().values()); //FIXME: add QSet support to KConfig } //Pretty drastic, but this is executed only very rarely svgElementsCache->sync(); } } QColor ThemePrivate::color(Theme::ColorRole role, Theme::ColorGroup group) const { const KColorScheme *scheme = nullptr; //Before 5.0 Plasma theme really only used Normal and Button //many old themes are built on this assumption and will break //otherwise if (apiMajor < 5 && group != Theme::NormalColorGroup) { group = Theme::ButtonColorGroup; } switch (group) { case Theme::ButtonColorGroup: { scheme = &buttonColorScheme; break; } case Theme::ViewColorGroup: { scheme = &viewColorScheme; break; } //this doesn't have a real kcolorscheme case Theme::ComplementaryColorGroup: { scheme = &complementaryColorScheme; break; } case Theme::NormalColorGroup: default: { scheme = &colorScheme; break; } } switch (role) { case Theme::TextColor: return scheme->foreground(KColorScheme::NormalText).color(); case Theme::BackgroundColor: return scheme->background(KColorScheme::NormalBackground).color(); case Theme::HoverColor: return scheme->decoration(KColorScheme::HoverColor).color(); case Theme::HighlightColor: return selectionColorScheme.background(KColorScheme::NormalBackground).color(); case Theme::FocusColor: return scheme->decoration(KColorScheme::FocusColor).color(); case Theme::LinkColor: return scheme->foreground(KColorScheme::LinkText).color(); case Theme::VisitedLinkColor: return scheme->foreground(KColorScheme::VisitedText).color(); case Theme::HighlightedTextColor: return selectionColorScheme.foreground(KColorScheme::NormalText).color(); case Theme::PositiveTextColor: return scheme->foreground(KColorScheme::PositiveText).color(); case Theme::NeutralTextColor: return scheme->foreground(KColorScheme::NeutralText).color(); case Theme::NegativeTextColor: return scheme->foreground(KColorScheme::NegativeText).color(); case Theme::DisabledTextColor: return scheme->foreground(KColorScheme::InactiveText).color(); } return QColor(); } void ThemePrivate::processWallpaperSettings(KConfigBase *metadata) { if (!defaultWallpaperTheme.isEmpty() && defaultWallpaperTheme != QLatin1String(DEFAULT_WALLPAPER_THEME)) { return; } KConfigGroup cg; if (metadata->hasGroup("Wallpaper")) { // we have a theme color config, so let's also check to see if // there is a wallpaper defined in there. cg = KConfigGroup(metadata, "Wallpaper"); } else { // since we didn't find an entry in the theme, let's look in the main // theme config cg = config(); } defaultWallpaperTheme = cg.readEntry("defaultWallpaperTheme", DEFAULT_WALLPAPER_THEME); defaultWallpaperSuffix = cg.readEntry("defaultFileSuffix", DEFAULT_WALLPAPER_SUFFIX); defaultWallpaperWidth = cg.readEntry("defaultWidth", DEFAULT_WALLPAPER_WIDTH); defaultWallpaperHeight = cg.readEntry("defaultHeight", DEFAULT_WALLPAPER_HEIGHT); } void ThemePrivate::processContrastSettings(KConfigBase *metadata) { KConfigGroup cg; if (metadata->hasGroup("ContrastEffect")) { cg = KConfigGroup(metadata, "ContrastEffect"); backgroundContrastEnabled = cg.readEntry("enabled", false); backgroundContrast = cg.readEntry("contrast", qQNaN()); backgroundIntensity = cg.readEntry("intensity", qQNaN()); backgroundSaturation = cg.readEntry("saturation", qQNaN()); } else { backgroundContrastEnabled = false; } } void ThemePrivate::processBlurBehindSettings(KConfigBase *metadata) { KConfigGroup cg; if (metadata->hasGroup("BlurBehindEffect")) { cg = KConfigGroup(metadata, "BlurBehindEffect"); blurBehindEnabled = cg.readEntry("enabled", true); } else { blurBehindEnabled = true; } } void ThemePrivate::setThemeName(const QString &tempThemeName, bool writeSettings, bool emitChanged) { QString theme = tempThemeName; if (theme.isEmpty() || theme == themeName) { // let's try and get the default theme at least if (themeName.isEmpty()) { theme = QLatin1String(ThemePrivate::defaultTheme); } else { return; } } // we have one special theme: essentially a dummy theme used to cache things with // the system colors. bool realTheme = theme != QLatin1String(systemColorsTheme); if (realTheme) { QString themePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1String("/metadata.desktop")); if (themePath.isEmpty() && themeName.isEmpty()) { // note: can't use QStringLiteral("foo" "bar") on Windows themePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/default"), QStandardPaths::LocateDirectory); if (themePath.isEmpty()) { return; } theme = QLatin1String(ThemePrivate::defaultTheme); } } // check again as ThemePrivate::defaultTheme might be empty if (themeName == theme) { return; } themeName = theme; // load the color scheme config const QString colorsFile = realTheme ? QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1String("/colors")) : QString(); //qCDebug(LOG_PLASMA) << "we're going for..." << colorsFile << "*******************"; if (colorsFile.isEmpty()) { colors = nullptr; } else { colors = KSharedConfig::openConfig(colorsFile); } colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors); selectionColorScheme = KColorScheme(QPalette::Active, KColorScheme::Selection, colors); buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors); viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors); complementaryColorScheme = KColorScheme(QPalette::Active, KColorScheme::Complementary, colors); const QString wallpaperPath = QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1String("/wallpapers/"); hasWallpapers = !QStandardPaths::locate(QStandardPaths::GenericDataLocation, wallpaperPath, QStandardPaths::LocateDirectory).isEmpty(); // load the wallpaper settings, if any if (realTheme) { const QString metadataPath(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1String("/metadata.desktop"))); KConfig metadata(metadataPath, KConfig::SimpleConfig); - pluginInfo = KPluginInfo(metadataPath); + pluginMetaData = KPluginMetaData(metadataPath); processContrastSettings(&metadata); processBlurBehindSettings(&metadata); processWallpaperSettings(&metadata); KConfigGroup cg(&metadata, "Settings"); QString fallback = cg.readEntry("FallbackTheme", QString()); fallbackThemes.clear(); while (!fallback.isEmpty() && !fallbackThemes.contains(fallback)) { fallbackThemes.append(fallback); QString metadataPath(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % fallback % QLatin1String("/metadata.desktop"))); KConfig metadata(metadataPath, KConfig::SimpleConfig); KConfigGroup cg(&metadata, "Settings"); fallback = cg.readEntry("FallbackTheme", QString()); } if (!fallbackThemes.contains(QLatin1String(ThemePrivate::defaultTheme))) { fallbackThemes.append(QLatin1String(ThemePrivate::defaultTheme)); } foreach (const QString &theme, fallbackThemes) { QString metadataPath(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1String("/metadata.desktop"))); KConfig metadata(metadataPath, KConfig::SimpleConfig); processWallpaperSettings(&metadata); } //Check for what Plasma version the theme has been done //There are some behavioral differences between KDE4 Plasma and Plasma 5 cg = KConfigGroup(&metadata, "Desktop Entry"); const QString apiVersion = cg.readEntry("X-Plasma-API", QString()); apiMajor = 1; apiMinor = 0; apiRevision = 0; if (!apiVersion.isEmpty()) { QVector parts = apiVersion.splitRef(QLatin1Char('.')); if (!parts.isEmpty()) { apiMajor = parts.value(0).toInt(); } if (parts.count() > 1) { apiMinor = parts.value(1).toInt(); } if (parts.count() > 2) { apiRevision = parts.value(2).toInt(); } } } if (realTheme && isDefault && writeSettings) { // we're the default theme, let's save our status KConfigGroup &cg = config(); cg.writeEntry("name", themeName); cg.sync(); } if(emitChanged) { scheduleThemeChangeNotification(PixmapCache | SvgElementsCache); } } bool ThemePrivate::eventFilter(QObject *watched, QEvent *event) { if (watched == QCoreApplication::instance()) { if (event->type() == QEvent::ApplicationPaletteChange) { colorsChanged(); } if (event->type() == QEvent::ApplicationFontChange || event->type() == QEvent::FontChange) { Q_EMIT defaultFontChanged(); Q_EMIT smallestFontChanged(); } } return QObject::eventFilter(watched, event); } } #include "moc_theme_p.cpp" diff --git a/src/plasma/private/theme_p.h b/src/plasma/private/theme_p.h index 8f1676549..088053214 100644 --- a/src/plasma/private/theme_p.h +++ b/src/plasma/private/theme_p.h @@ -1,170 +1,171 @@ /* * Copyright 2006-2007 Aaron Seigo * 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 PLASMA_THEME_P_H #define PLASMA_THEME_P_H #include "theme.h" #include "svg.h" #include #include #include #include #include #include +#include #include #include #if HAVE_X11 #include "private/effectwatcher_p.h" #endif #include "libplasma-theme-global.h" namespace Plasma { class Theme; //NOTE: Default wallpaper can be set from the theme configuration #define DEFAULT_WALLPAPER_THEME "default" #define DEFAULT_WALLPAPER_SUFFIX ".png" static const int DEFAULT_WALLPAPER_WIDTH = 1920; static const int DEFAULT_WALLPAPER_HEIGHT = 1200; enum CacheType { NoCache = 0, PixmapCache = 1, SvgElementsCache = 2 }; Q_DECLARE_FLAGS(CacheTypes, CacheType) Q_DECLARE_OPERATORS_FOR_FLAGS(CacheTypes) class ThemePrivate : public QObject, public QSharedData { Q_OBJECT public: explicit ThemePrivate(QObject *parent = nullptr); ~ThemePrivate() override; KConfigGroup &config(); QString imagePath(const QString &theme, const QString &type, const QString &image); QString findInTheme(const QString &image, const QString &theme, bool cache = true); void discardCache(CacheTypes caches); void scheduleThemeChangeNotification(CacheTypes caches); bool useCache(); void setThemeName(const QString &themeName, bool writeSettings, bool emitChanged); void processWallpaperSettings(KConfigBase *metadata); void processContrastSettings(KConfigBase *metadata); void processBlurBehindSettings(KConfigBase *metadata); const QString processStyleSheet(const QString &css, Plasma::Svg::Status status); const QString svgStyleSheet(Plasma::Theme::ColorGroup group, Plasma::Svg::Status status); QColor color(Theme::ColorRole role, Theme::ColorGroup group = Theme::NormalColorGroup) const; public Q_SLOTS: void compositingChanged(bool active); void colorsChanged(); void settingsFileChanged(const QString &settings); void scheduledCacheUpdate(); void onAppExitCleanup(); void notifyOfChanged(); void settingsChanged(bool emitChanges); void saveSvgElementsCache(); Q_SIGNALS: void themeChanged(); void defaultFontChanged(); void smallestFontChanged(); void applicationPaletteChange(); public: static const char defaultTheme[]; static const char systemColorsTheme[]; static const char themeRcFile[]; #if HAVE_X11 static EffectWatcher *s_backgroundContrastEffectWatcher; #endif //Ref counting of ThemePrivate instances static ThemePrivate *globalTheme; static QHash themes; QString themeName; - KPluginInfo pluginInfo; + KPluginMetaData pluginMetaData; QList fallbackThemes; KSharedConfigPtr colors; KColorScheme colorScheme; KColorScheme selectionColorScheme; KColorScheme buttonColorScheme; KColorScheme viewColorScheme; KColorScheme complementaryColorScheme; bool eventFilter(QObject *watched, QEvent *event) override; KConfigGroup cfg; QString defaultWallpaperTheme; QString defaultWallpaperSuffix; int defaultWallpaperWidth; int defaultWallpaperHeight; KImageCache *pixmapCache; KSharedConfigPtr svgElementsCache; QString cachedDefaultStyleSheet; QHash > invalidElements; QHash pixmapsToCache; QHash keysToCache; QHash idsToCache; QHash cachedSvgStyleSheets; QHash cachedSelectedSvgStyleSheets; QHash discoveries; QTimer *pixmapSaveTimer; QTimer *rectSaveTimer; QTimer *updateNotificationTimer; unsigned cacheSize; CacheTypes cachesToDiscard; QString themeVersion; QString themeMetadataPath; QString iconThemeMetadataPath; bool compositingActive : 1; bool backgroundContrastActive : 1; bool isDefault : 1; bool useGlobal : 1; bool hasWallpapers : 1; bool cacheTheme : 1; bool fixedName : 1; qreal backgroundContrast; qreal backgroundIntensity; qreal backgroundSaturation; bool backgroundContrastEnabled; bool blurBehindEnabled; //Version number of Plasma the Theme has been designed for int apiMajor; int apiMinor; int apiRevision; }; } #endif extern const QString s; diff --git a/src/plasma/scripting/dataenginescript.cpp b/src/plasma/scripting/dataenginescript.cpp index bf3013c07..ae97dd1b4 100644 --- a/src/plasma/scripting/dataenginescript.cpp +++ b/src/plasma/scripting/dataenginescript.cpp @@ -1,200 +1,205 @@ /* * Copyright 2007 by Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dataenginescript.h" #include "package.h" #include "private/dataengine_p.h" #include "private/service_p.h" namespace Plasma { class DataEngineScriptPrivate { public: DataEngine *dataEngine; }; DataEngineScript::DataEngineScript(QObject *parent) : ScriptEngine(parent), d(new DataEngineScriptPrivate) { } DataEngineScript::~DataEngineScript() { delete d; } void DataEngineScript::setDataEngine(DataEngine *dataEngine) { d->dataEngine = dataEngine; } DataEngine *DataEngineScript::dataEngine() const { return d->dataEngine; } QStringList DataEngineScript::sources() const { return d->dataEngine->containerDict().keys(); } bool DataEngineScript::sourceRequestEvent(const QString &name) { Q_UNUSED(name); return false; } bool DataEngineScript::updateSourceEvent(const QString &source) { Q_UNUSED(source); return false; } Service *DataEngineScript::serviceForSource(const QString &source) { Q_ASSERT(d->dataEngine); return new NullService(source, d->dataEngine); } QString DataEngineScript::mainScript() const { Q_ASSERT(d->dataEngine); return d->dataEngine->package().filePath("mainscript"); } Package DataEngineScript::package() const { Q_ASSERT(d->dataEngine); return d->dataEngine->package(); } KPluginInfo DataEngineScript::description() const +{ + return KPluginInfo(metadata()); +} + +KPluginMetaData DataEngineScript::metadata() const { Q_ASSERT(d->dataEngine); - return d->dataEngine->d->dataEngineDescription; + return d->dataEngine->metadata(); } void DataEngineScript::setData(const QString &source, const QString &key, const QVariant &value) { if (d->dataEngine) { d->dataEngine->setData(source, key, value); } } void DataEngineScript::setData(const QString &source, const QVariant &value) { if (d->dataEngine) { d->dataEngine->setData(source, value); } } void DataEngineScript::setData(const QString &source, const DataEngine::Data &values) { if (d->dataEngine) { d->dataEngine->setData(source, values); } } void DataEngineScript::removeAllData(const QString &source) { if (d->dataEngine) { d->dataEngine->removeAllData(source); } } void DataEngineScript::removeData(const QString &source, const QString &key) { if (d->dataEngine) { d->dataEngine->removeData(source, key); } } void DataEngineScript::setMinimumPollingInterval(int minimumMs) { if (d->dataEngine) { d->dataEngine->setMinimumPollingInterval(minimumMs); } } int DataEngineScript::minimumPollingInterval() const { if (d->dataEngine) { return d->dataEngine->minimumPollingInterval(); } return 0; } void DataEngineScript::setPollingInterval(uint frequency) { if (d->dataEngine) { d->dataEngine->setPollingInterval(frequency); } } void DataEngineScript::removeAllSources() { if (d->dataEngine) { d->dataEngine->removeAllSources(); } } void DataEngineScript::addSource(DataContainer *source) { if (d->dataEngine) { d->dataEngine->addSource(source); } } DataEngine::SourceDict DataEngineScript::containerDict() const { if (d->dataEngine) { return d->dataEngine->containerDict(); } return DataEngine::SourceDict(); } void DataEngineScript::removeSource(const QString &source) { if (d->dataEngine) { d->dataEngine->removeSource(source); } } void DataEngineScript::updateAllSources() { if (d->dataEngine) { d->dataEngine->updateAllSources(); } } void DataEngineScript::forceImmediateUpdateOfAllVisualizations() { if (d->dataEngine) { d->dataEngine->forceImmediateUpdateOfAllVisualizations(); } } } // Plasma namespace #include "moc_dataenginescript.cpp" diff --git a/src/plasma/scripting/dataenginescript.h b/src/plasma/scripting/dataenginescript.h index aadcc3d3a..903066c3d 100644 --- a/src/plasma/scripting/dataenginescript.h +++ b/src/plasma/scripting/dataenginescript.h @@ -1,154 +1,166 @@ /* * Copyright 2007 by Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PLASMA_DATAENGINESCRIPT_H #define PLASMA_DATAENGINESCRIPT_H #include #include #include #include namespace Plasma { class DataEngineScriptPrivate; class Service; /** * @class DataEngineScript plasma/scripting/dataenginescript.h * * @short Provides a restricted interface for scripting a DataEngine */ class PLASMA_EXPORT DataEngineScript : public ScriptEngine { Q_OBJECT public: /** * Default constructor for a DataEngineScript. * Subclasses should not attempt to access the Plasma::DataEngine * associated with this DataEngineScript in the constructor. All * such set up that requires the DataEngine itself should be done * in the init() method. */ explicit DataEngineScript(QObject *parent = nullptr); ~DataEngineScript() override; /** * Sets the Plasma::DataEngine associated with this DataEngineScript */ void setDataEngine(DataEngine *dataEngine); /** * Returns the Plasma::DataEngine associated with this script component */ DataEngine *dataEngine() const; /** * @return a list of all the data sources available via this DataEngine * Whether these sources are currently available (which is what * the default implementation provides) or not is up to the * DataEngine to decide. By default, this returns dataEngine()->sources() */ virtual QStringList sources() const; /** * Called when the script should create a source that does not currently * exist. * * @param name the name of the source that should be created * @return true if a DataContainer was set up, false otherwise */ virtual bool sourceRequestEvent(const QString &name); /** * Called when the script should refresh the data contained in a given * source. * * @param source the name of the source that should be updated * @return true if the data was changed, or false if there was no * change or if the change will occur later **/ virtual bool updateSourceEvent(const QString &source); /** * @param source the source to targe the Service at * @return a Service that has the source as a destination. The service * is parented to the DataEngine, but may be deleted by the * caller when finished with it */ virtual Service *serviceForSource(const QString &source); protected: /** * @return absolute path to the main script file for this plasmoid */ QString mainScript() const override; /** * @return the Package associated with this plasmoid which can * be used to request resources, such as images and * interface files. */ Package package() const override; +#if PLASMA_ENABLE_DEPRECATED_SINCE(5, 67) /** - * @return the KPluginInfo associated with this plasmoid + * @return the KPluginInfo associated with this dataengine + * + * @deprecated since 5.67 use metadata() */ + PLASMA_DEPRECATED_VERSION(5, 67, "Use KPluginMetaData metadata()") KPluginInfo description() const; +#endif + + /** + * @return the KPluginMetaData associated with this dataengine + * + * @since 5.62 + */ + KPluginMetaData metadata() const; void setData(const QString &source, const QString &key, const QVariant &value); void setData(const QString &source, const QVariant &value); /** * Adds a set of values to a source * @param source the name of the source to set the data on * @values a key/value collection to add to the source * @since 4.5 */ void setData(const QString &source, const DataEngine::Data &values); void removeAllData(const QString &source); void removeData(const QString &source, const QString &key); void setMinimumPollingInterval(int minimumMs); int minimumPollingInterval() const; void setPollingInterval(uint frequency); void removeAllSources(); void addSource(DataContainer *source); DataEngine::SourceDict containerDict() const; void removeSource(const QString &source); void updateAllSources(); void forceImmediateUpdateOfAllVisualizations(); private: DataEngineScriptPrivate *const d; }; #define K_EXPORT_PLASMA_DATAENGINESCRIPTENGINE(libname, classname) \ K_PLUGIN_FACTORY(factory, registerPlugin();) #define K_EXPORT_PLASMA_DATAENGINESCRIPTENGINE_WITH_JSON(libname, classname, jsonFile) \ K_PLUGIN_FACTORY_WITH_JSON(factory, jsonFile, registerPlugin();) \ K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION) } //Plasma namespace #endif diff --git a/src/plasma/theme.cpp b/src/plasma/theme.cpp index 58bb7a15d..d581d5ab0 100644 --- a/src/plasma/theme.cpp +++ b/src/plasma/theme.cpp @@ -1,511 +1,511 @@ /* * Copyright 2006-2007 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "theme.h" #include "private/theme_p.h" #include #include #include #include #include #include #include #include #include #include #include "config-plasma.h" #include #include #include #include #include #include #include #include #include #include "debug_p.h" namespace Plasma { Theme::Theme(QObject *parent) : QObject(parent) { if (!ThemePrivate::globalTheme) { ThemePrivate::globalTheme = new ThemePrivate; } ThemePrivate::globalTheme->ref.ref(); d = ThemePrivate::globalTheme; d->settingsChanged(false); if (QCoreApplication::instance()) { connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, d, &ThemePrivate::onAppExitCleanup); } connect(d, &ThemePrivate::themeChanged, this, &Theme::themeChanged); connect(d, &ThemePrivate::defaultFontChanged, this, &Theme::defaultFontChanged); connect(d, &ThemePrivate::smallestFontChanged, this, &Theme::smallestFontChanged); } Theme::Theme(const QString &themeName, QObject *parent) : QObject(parent) { auto& priv = ThemePrivate::themes[themeName]; if (!priv) { priv = new ThemePrivate; } priv->ref.ref(); d = priv; // turn off caching so we don't accidentally trigger unnecessary disk activity at this point bool useCache = d->cacheTheme; d->cacheTheme = false; d->setThemeName(themeName, false, false); d->cacheTheme = useCache; d->fixedName = true; if (QCoreApplication::instance()) { connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, d, &ThemePrivate::onAppExitCleanup); } connect(d, &ThemePrivate::themeChanged, this, &Theme::themeChanged); } Theme::~Theme() { if (d == ThemePrivate::globalTheme) { if (!d->ref.deref()) { disconnect(ThemePrivate::globalTheme, nullptr, this, nullptr); delete ThemePrivate::globalTheme; ThemePrivate::globalTheme = nullptr; d = nullptr; } } else { if (!d->ref.deref()) { delete ThemePrivate::themes.take(d->themeName); } } } void Theme::setThemeName(const QString &themeName) { if (d->themeName == themeName) { return; } if (d != ThemePrivate::globalTheme) { disconnect(QCoreApplication::instance(), nullptr, d, nullptr); if (!d->ref.deref()) { delete ThemePrivate::themes.take(d->themeName); } auto& priv = ThemePrivate::themes[themeName]; if (!priv) { priv = new ThemePrivate; } priv->ref.ref(); d = priv; if (QCoreApplication::instance()) { connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, d, &ThemePrivate::onAppExitCleanup); } connect(d, &ThemePrivate::themeChanged, this, &Theme::themeChanged); } d->setThemeName(themeName, true, true); } QString Theme::themeName() const { return d->themeName; } QString Theme::imagePath(const QString &name) const { // look for a compressed svg file in the theme if (name.contains(QLatin1String("../")) || name.isEmpty()) { // we don't support relative paths //qCDebug(LOG_PLASMA) << "Theme says: bad image path " << name; return QString(); } const QString svgzName = name % QLatin1String(".svgz"); QString path = d->findInTheme(svgzName, d->themeName); if (path.isEmpty()) { // try for an uncompressed svg file const QString svgName = name % QLatin1String(".svg"); path = d->findInTheme(svgName, d->themeName); // search in fallback themes if necessary for (int i = 0; path.isEmpty() && i < d->fallbackThemes.count(); ++i) { if (d->themeName == d->fallbackThemes[i]) { continue; } // try a compressed svg file in the fallback theme path = d->findInTheme(svgzName, d->fallbackThemes[i]); if (path.isEmpty()) { // try an uncompressed svg file in the fallback theme path = d->findInTheme(svgName, d->fallbackThemes[i]); } } } /* if (path.isEmpty()) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Theme says: bad image path " << name; #endif } */ return path; } QString Theme::backgroundPath(const QString& image) const { return d->imagePath(themeName(), QStringLiteral("/appbackgrounds/"), image); } QString Theme::styleSheet(const QString &css) const { return d->processStyleSheet(css, Svg::Status::Normal); } QString Theme::wallpaperPath(const QSize &size) const { QString fullPath; QString image = d->defaultWallpaperTheme + QStringLiteral("/contents/images/%1x%2") + d->defaultWallpaperSuffix; QString defaultImage = image.arg(d->defaultWallpaperWidth).arg(d->defaultWallpaperHeight); if (size.isValid()) { // try to customize the paper to the size requested //TODO: this should do better than just fallback to the default size. // a "best fit" matching would be far better, so we don't end // up returning a 1920x1200 wallpaper for a 640x480 request ;) image = image.arg(size.width()).arg(size.height()); } else { image = defaultImage; } //TODO: the theme's wallpaper overrides regularly installed wallpapers. // should it be possible for user installed (e.g. locateLocal) wallpapers // to override the theme? if (d->hasWallpapers) { // check in the theme first fullPath = d->findInTheme(QLatin1String("wallpapers/") % image, d->themeName); if (fullPath.isEmpty()) { fullPath = d->findInTheme(QLatin1String("wallpapers/") % defaultImage, d->themeName); } } if (fullPath.isEmpty()) { // we failed to find it in the theme, so look in the standard directories //qCDebug(LOG_PLASMA) << "looking for" << image; fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("wallpapers/") + image); } if (fullPath.isEmpty()) { // we still failed to find it in the theme, so look for the default in // the standard directories //qCDebug(LOG_PLASMA) << "looking for" << defaultImage; fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("wallpapers/") + defaultImage); if (fullPath.isEmpty()) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "exhausted every effort to find a wallpaper."; #endif } } return fullPath; } QString Theme::wallpaperPathForSize(int width, int height) const { return Plasma::Theme::wallpaperPath(QSize(width, height)); } bool Theme::currentThemeHasImage(const QString &name) const { if (name.contains(QLatin1String("../"))) { // we don't support relative paths return false; } return !(d->findInTheme(name % QLatin1String(".svgz"), d->themeName, false).isEmpty()) || !(d->findInTheme(name % QLatin1String(".svg"), d->themeName, false).isEmpty()); } KSharedConfigPtr Theme::colorScheme() const { return d->colors; } QColor Theme::color(ColorRole role, ColorGroup group) const { return d->color(role, group); } void Theme::setUseGlobalSettings(bool useGlobal) { if (d->useGlobal == useGlobal) { return; } d->useGlobal = useGlobal; d->cfg = KConfigGroup(); d->themeName.clear(); d->settingsChanged(true); } bool Theme::useGlobalSettings() const { return d->useGlobal; } bool Theme::findInCache(const QString &key, QPixmap &pix, unsigned int lastModified) { if (lastModified != 0 && d->useCache() && lastModified > uint(d->pixmapCache->lastModifiedTime().toSecsSinceEpoch())) { return false; } if (d->useCache()) { const QString id = d->keysToCache.value(key); const auto it = d->pixmapsToCache.constFind(id); if (it != d->pixmapsToCache.constEnd()) { pix = *it; return !pix.isNull(); } QPixmap temp; if (d->pixmapCache->findPixmap(key, &temp) && !temp.isNull()) { pix = temp; return true; } } return false; } void Theme::insertIntoCache(const QString &key, const QPixmap &pix) { if (d->useCache()) { d->pixmapCache->insertPixmap(key, pix); } } void Theme::insertIntoCache(const QString &key, const QPixmap &pix, const QString &id) { if (d->useCache()) { d->pixmapsToCache[id] = pix; d->keysToCache[key] = id; d->idsToCache[id] = key; //always start timer in d->pixmapSaveTimer's thread QMetaObject::invokeMethod(d->pixmapSaveTimer, "start", Qt::QueuedConnection); } } bool Theme::findInRectsCache(const QString &image, const QString &element, QRectF &rect) const { if (!d->useCache()) { return false; } KConfigGroup imageGroup(d->svgElementsCache, image); rect = imageGroup.readEntry(element % QLatin1String("Size"), QRectF()); if (rect.isValid()) { return true; } //Name starting by _ means the element is empty and we're asked for the size of //the whole image, so the whole image is never invalid if (element.indexOf(QLatin1Char('_')) <= 0) { return false; } bool invalid = false; QHash >::iterator it = d->invalidElements.find(image); if (it == d->invalidElements.end()) { QSet elements = imageGroup.readEntry("invalidElements", QStringList()).toSet(); d->invalidElements.insert(image, elements); invalid = elements.contains(element); } else { invalid = it.value().contains(element); } return invalid; } QStringList Theme::listCachedRectKeys(const QString &image) const { if (!d->useCache()) { return QStringList(); } KConfigGroup imageGroup(d->svgElementsCache, image); QStringList keys = imageGroup.keyList(); QMutableListIterator i(keys); while (i.hasNext()) { QString key = i.next(); if (key.endsWith(QLatin1String("Size"))) { // The actual cache id used from outside doesn't end on "Size". key.resize(key.size() - 4); i.setValue(key); } else { i.remove(); } } return keys; } void Theme::insertIntoRectsCache(const QString &image, const QString &element, const QRectF &rect) { if (!d->useCache()) { return; } if (rect.isValid()) { KConfigGroup imageGroup(d->svgElementsCache, image); imageGroup.writeEntry(element % QLatin1String("Size"), rect); } else { QHash >::iterator it = d->invalidElements.find(image); if (it == d->invalidElements.end()) { d->invalidElements[image].insert(element); } else if (!it.value().contains(element)) { if (it.value().count() > 1000) { it.value().erase(it.value().begin()); } it.value().insert(element); } } QMetaObject::invokeMethod(d->rectSaveTimer, "start"); } void Theme::invalidateRectsCache(const QString &image) { if (d->useCache()) { KConfigGroup imageGroup(d->svgElementsCache, image); imageGroup.deleteGroup(); } d->invalidElements.remove(image); } void Theme::releaseRectsCache(const QString &image) { QHash >::iterator it = d->invalidElements.find(image); if (it != d->invalidElements.end()) { if (d->useCache()) { KConfigGroup imageGroup(d->svgElementsCache, it.key()); imageGroup.writeEntry("invalidElements", it.value().values()); } d->invalidElements.erase(it); } } void Theme::setCacheLimit(int kbytes) { d->cacheSize = kbytes; delete d->pixmapCache; d->pixmapCache = nullptr; } KPluginInfo Theme::pluginInfo() const { - return d->pluginInfo; + return KPluginInfo(d->pluginMetaData); } QFont Theme::defaultFont() const { return QGuiApplication::font(); } QFont Theme::smallestFont() const { return QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont); } QSizeF Theme::mSize(const QFont &font) const { return QFontMetrics(font).boundingRect(QStringLiteral("M")).size(); } bool Theme::backgroundContrastEnabled() const { return d->backgroundContrastEnabled; } qreal Theme::backgroundContrast() const { if (qIsNaN(d->backgroundContrast)) { // Make up sensible default values, based on the background color // If we're using a dark background color, darken the background if (qGray(color(Plasma::Theme::BackgroundColor).rgb()) < 127) { return 0.45; // for a light theme lighten up the background } else { return 0.3; } } return d->backgroundContrast; } qreal Theme::backgroundIntensity() const { if (qIsNaN(d->backgroundIntensity)) { if (qGray(color(Plasma::Theme::BackgroundColor).rgb()) < 127) { return 0.45; } else { return 1.9; } } return d->backgroundIntensity; } qreal Theme::backgroundSaturation() const { if (qIsNaN(d->backgroundSaturation)) { return 1.7; } return d->backgroundSaturation; } bool Theme::blurBehindEnabled() const { return d->blurBehindEnabled; } } #include "moc_theme.cpp" diff --git a/src/plasma/theme.h b/src/plasma/theme.h index c65b8094d..80327281d 100644 --- a/src/plasma/theme.h +++ b/src/plasma/theme.h @@ -1,449 +1,463 @@ /* * Copyright 2006-2007 Aaron Seigo * 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 PLASMA_THEME_H #define PLASMA_THEME_H #include #include #include #include #include #include +class KPluginMetaData; + namespace Plasma { class ThemePrivate; class SvgPrivate; /** * @class Theme plasma/theme.h * * @short Interface to the Plasma theme * * * Plasma::Theme provides access to a common and standardized set of graphic * elements stored in SVG format. This allows artists to create single packages * of SVGs that will affect the look and feel of all workspace components. * * Plasma::Svg uses Plasma::Theme internally to locate and load the appropriate * SVG data. Alternatively, Plasma::Theme can be used directly to retrieve * file system paths to SVGs by name. */ class PLASMA_EXPORT Theme : public QObject { Q_OBJECT Q_PROPERTY(QString themeName READ themeName NOTIFY themeChanged) Q_PROPERTY(bool useGlobalSettings READ useGlobalSettings NOTIFY themeChanged) Q_PROPERTY(QString wallpaperPath READ wallpaperPath NOTIFY themeChanged) //fonts Q_PROPERTY(QFont defaultFont READ defaultFont NOTIFY defaultFontChanged) Q_PROPERTY(QFont smallestFont READ smallestFont NOTIFY smallestFontChanged) // stylesheet Q_PROPERTY(QString styleSheet READ styleSheet NOTIFY themeChanged) public: enum ColorRole { TextColor = 0, /**< the text color to be used by items resting on the background */ BackgroundColor = 1, /**< the default background color */ HighlightColor = 2, /**< the text highlight color to be used by items resting on the background */ HoverColor = 3, /**< color for hover effect on view */ FocusColor = 4, /**< color for focus effect on view */ LinkColor = 5, /**< color for clickable links */ VisitedLinkColor = 6, /**< color visited clickable links */ HighlightedTextColor = 7,/**< color contrasting with HighlightColor, to be used for instance with */ PositiveTextColor = 8, /**< color of foreground objects with a "positive message" connotation (usually green) */ NeutralTextColor = 9, /**< color of foreground objects with a "neutral message" connotation (usually yellow) */ NegativeTextColor = 10, /**< color of foreground objects with a "negative message" connotation (usually red) */ DisabledTextColor = 11 /**< color of disabled text @since 5.64 */ }; enum ColorGroup { NormalColorGroup = 0, ButtonColorGroup = 1, ViewColorGroup = 2, ComplementaryColorGroup = 3 }; Q_ENUM(ColorGroup) /** * Default constructor. It will be the global theme configured in plasmarc * @param parent the parent object */ explicit Theme(QObject *parent = nullptr); /** * Construct a theme. It will be a custom theme instance of themeName. * @param themeName the name of the theme to create * @param parent the parent object * @since 4.3 */ explicit Theme(const QString &themeName, QObject *parent = nullptr); ~Theme(); /** * Sets the current theme being used. */ void setThemeName(const QString &themeName); /** * @return the name of the theme. */ QString themeName() const; /** * Retrieve the path for an SVG image in the current theme. * * @param name the name of the file in the theme directory (without the * ".svg" part or a leading slash) * @return the full path to the requested file for the current theme */ QString imagePath(const QString &name) const; /** * Retrieves the default wallpaper associated with this theme. * * @param size the target height and width of the wallpaper; if an invalid size * is passed in, then a default size will be provided instead. * @return the full path to the wallpaper image */ QString wallpaperPath(const QSize &size = QSize()) const; Q_INVOKABLE QString wallpaperPathForSize(int width = -1, int height = -1) const; /** * Checks if this theme has an image named in a certain way * * @param name the name of the file in the theme directory (without the * ".svg" part or a leading slash) * @return true if the image exists for this theme */ bool currentThemeHasImage(const QString &name) const; /** * Returns the color scheme configurationthat goes along this theme. * This can be used with KStatefulBrush and KColorScheme to determine * the proper colours to use along with the visual elements in this theme. */ KSharedConfigPtr colorScheme() const; /** * Returns the text color to be used by items resting on the background * * @param role which role (usage pattern) to get the color for * @param group which group we want a color of */ QColor color(ColorRole role, ColorGroup group = NormalColorGroup) const; /** * Tells the theme whether to follow the global settings or use application * specific settings * * @param useGlobal pass in true to follow the global settings */ void setUseGlobalSettings(bool useGlobal); /** * @return true if the global settings are followed, false if application * specific settings are used. */ bool useGlobalSettings() const; /** * Provides a Plasma::Theme-themed stylesheet for hybrid (web / native Plasma) widgets. * * You can use this method to retrieve a basic default stylesheet, or to theme your * custom stylesheet you use for example in Plasma::WebView. The QString you can pass * into this method does not have to be a valid stylesheet, in fact you can use this * method to replace color placeholders with the theme's color in any QString. * * In order to use this method with a custom stylesheet, just put for example %textcolor * in your QString and it will be replaced with the theme's text (or foreground) color. * * Just like in many other methods for retrieving theme information, do not forget to * update your stylesheet upon the themeChanged() signal. * * The following tags will be replaced by corresponding colors from Plasma::Theme: * * %textcolor * %backgroundcolor * %buttonbackgroundcolor * * %link * %activatedlink * %hoveredlink * %visitedlink * * %fontfamily * %fontsize * %smallfontsize * * @param css a stylesheet to theme, leave empty for a default stylesheet containing * theming for some commonly used elements, body text and links, for example. * * @return a piece of CSS that sets the most commonly used style elements to a theme * matching Plasma::Theme. * * @since 4.5 */ QString styleSheet(const QString &css = QString()) const; /** * This is an overloaded member provided to check with file timestamp * where cache is still valid. * * @param key the name to use in the cache for this image * @param pix the pixmap object to populate with the resulting data if found * @param lastModified if non-zero, the time stamp is also checked on the file, * and must be newer than the timestamp to be loaded * * @return true when pixmap was found and loaded from cache, false otherwise * @since 4.3 **/ bool findInCache(const QString &key, QPixmap &pix, unsigned int lastModified = 0); /** * Insert specified pixmap into the cache. * If the cache already contains pixmap with the specified key then it is * overwritten. * * @param key the name to use in the cache for this pixmap * @param pix the pixmap data to store in the cache **/ void insertIntoCache(const QString &key, const QPixmap &pix); /** * Insert specified pixmap into the cache. * If the cache already contains pixmap with the specified key then it is * overwritten. * The actual insert is delayed for optimization reasons and the id * parameter is used to discard repeated inserts in the delay time, useful * when for instance the graphics to insert comes from a quickly resizing * object: the frames between the start and destination sizes aren't * useful in the cache and just cause overhead. * * @param key the name to use in the cache for this pixmap * @param pix the pixmap data to store in the cache * @param id a name that identifies the caller class of this function in an unique fashion. * This is needed to limit disk writes of the cache. * If an image with the same id changes quickly, * only the last size where insertIntoCache was called is actually stored on disk * @since 4.3 **/ void insertIntoCache(const QString &key, const QPixmap &pix, const QString &id); /** * Sets the maximum size of the cache (in kilobytes). If cache gets bigger * the limit then some entries are removed * Setting cache limit to 0 disables automatic cache size limiting. * * Note that the cleanup might not be done immediately, so the cache might * temporarily (for a few seconds) grow bigger than the limit. **/ void setCacheLimit(int kbytes); /** * Tries to load the rect of a sub element from a disk cache * * @param image path of the image we want to check * @param element sub element we want to retrieve * @param rect output parameter of the element rect found in cache * if not found or if we are sure it doesn't exist it will be QRect() * @return true if the element was found in cache or if we are sure the element doesn't exist **/ bool findInRectsCache(const QString &image, const QString &element, QRectF &rect) const; /** * Returns a list of all keys of cached rects for the given image. * * @param image path of the image for which the keys should be returned * * @return a QStringList whose elements are the entry keys in the rects cache * * @since 4.6 */ QStringList listCachedRectKeys(const QString &image) const; /** * Inserts a rectangle of a sub element of an image into a disk cache * * @param image path of the image we want to insert information * @param element sub element we want insert the rect * @param rect element rectangle **/ void insertIntoRectsCache(const QString &image, const QString &element, const QRectF &rect); /** * Discards all the information about a given image from the rectangle disk cache * * @param image the path to the image the cache is associated with **/ void invalidateRectsCache(const QString &image); /** * Frees up memory used by cached information for a given image without removing * the permanent record of it on disk. * @see invalidateRectsCache * * @param image the path to the image the cache is associated with */ void releaseRectsCache(const QString &image); +#if PLASMA_ENABLE_DEPRECATED_SINCE(5, 64) /** * @return plugin info for this theme, with information such as * name, description, author, website etc * @since 5.0 + * + * @deprecated since 5.67, use KPluginMetaData */ + PLASMA_DEPRECATED_VERSION(5, 67, "Use KPluginMetaData metadata()") KPluginInfo pluginInfo() const; +#endif + + /** + * @return metadata for this theme, with information such as + * name, description, author, website etc + * @since 5.64 + */ + KPluginMetaData metadata() const; /** * @return The default application font * @since 5.0 */ QFont defaultFont() const; /** * @return The smallest readable font * @since 5.0 */ QFont smallestFont() const; /** This method allows Plasma to enable and disable the background * contrast effect for a given theme, improving readability. The * value is read from the "enabled" key in the "ContrastEffect" * group in the Theme's metadata file. * The configuration in the metadata.desktop file of the theme * could look like this (for a lighter background): * \code * [ContrastEffect] * enabled=true * contrast=0.45 * intensity=0.45 * saturation=1.7 * \endcode * @return Whether or not to enable the contrasteffect * @since 5.0 */ bool backgroundContrastEnabled() const; /** This method allows Plasma to set a background contrast effect * for a given theme, improving readability. The value is read * from the "contrast" key in the "ContrastEffect" group in the * Theme's metadata file. * @return The contrast provided to the contrasteffect * @since 5.0 * @see backgroundContrastEnabled */ qreal backgroundContrast() const; /** This method allows Plasma to set a background contrast effect * for a given theme, improving readability. The value is read * from the "intensity" key in the "ContrastEffect" group in the * Theme's metadata file. * @return The intensity provided to the contrasteffect * @since 5.0 * @see backgroundContrastEnabled */ qreal backgroundIntensity() const; /** This method allows Plasma to set a background contrast effect * for a given theme, improving readability. The value is read * from the "saturation" key in the "ContrastEffect" group in the * Theme's metadata file. * @return The saturation provided to the contrasteffect * @since 5.0 * @see backgroundContrastEnabled */ qreal backgroundSaturation() const; /** This method allows Plasma to enable and disable the blurring * of what is behind the background for a given theme. The * value is read from the "enabled" key in the "BlurBehindEffect" * group in the Theme's metadata file. Default is @c true. * * The configuration in the metadata.desktop file of the theme * could look like this: * \code * [BlurBehindEffect] * enabled=false * \endcode * @return Whether or not to enable blurring of what is behind * @since 5.57 */ bool blurBehindEnabled() const; /** * Returns the size of the letter "M" as rendered on the screen with the given font. * This values gives you a base size that: * * scales dependent on the DPI of the screen * * Scales with the default font as set by the user * You can use it like this in QML Items: * \code * Item { * width: theme.mSize(theme.defaultFont).height * height: width * } * \endcode * This allows you to dynamically scale elements of your user interface with different font settings and * different physical outputs (with different DPI). * @param font The font to use for the metrics. * @return The size of the letter "M" as rendered on the screen with the given font. * @since 5.0 */ Q_INVOKABLE QSizeF mSize(const QFont &font = QGuiApplication::font()) const; QString backgroundPath(const QString &image) const; Q_SIGNALS: /** * Emitted when the user changes the theme. Stylesheet usage, colors, etc. should * be updated at this point. However, SVGs should *not* be repainted in response * to this signal; connect to Svg::repaintNeeded() instead for that, as Svg objects * need repainting not only when themeChanged() is emitted; moreover Svg objects * connect to and respond appropriately to themeChanged() internally, emitting * Svg::repaintNeeded() at an appropriate time. */ void themeChanged(); /** Notifier for change of defaultFont property */ void defaultFontChanged(); /** Notifier for change of smallestFont property */ void smallestFontChanged(); private: friend class SvgPrivate; friend class FrameSvg; friend class FrameSvgPrivate; friend class ThemePrivate; ThemePrivate *d; }; } // Plasma namespace #endif // multiple inclusion guard