diff --git a/kcms/ksplash/CMakeLists.txt b/kcms/ksplash/CMakeLists.txt index 1c31f2c50..cbeef8a3a 100644 --- a/kcms/ksplash/CMakeLists.txt +++ b/kcms/ksplash/CMakeLists.txt @@ -1,29 +1,31 @@ # KI18N Translation Domain for this library add_definitions(-DTRANSLATION_DOMAIN=\"kcm_splashscreen\") set(kcm_splashscreen_SRCS kcm.cpp ) add_library(kcm_splashscreen MODULE ${kcm_splashscreen_SRCS}) target_link_libraries(kcm_splashscreen KF5::KIOWidgets KF5::CoreAddons KF5::QuickAddons KF5::I18n KF5::Plasma KF5::PlasmaQuick + KF5::NewStuff Qt5::DBus Qt5::Widgets Qt5::QuickWidgets ) kcoreaddons_desktop_to_json(kcm_splashscreen "kcm_splashscreen.desktop" SERVICE_TYPES kcmodule.desktop) #this desktop file is installed only for retrocompatibility with sycoca install(FILES kcm_splashscreen.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) install(TARGETS kcm_splashscreen DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms) +install( FILES ksplash.knsrc DESTINATION ${KDE_INSTALL_CONFDIR} ) kpackage_install_package(package kcm_splashscreen kcms) diff --git a/kcms/ksplash/kcm.cpp b/kcms/ksplash/kcm.cpp index 1b0f1fc5c..c46210ca9 100644 --- a/kcms/ksplash/kcm.cpp +++ b/kcms/ksplash/kcm.cpp @@ -1,187 +1,200 @@ /* This file is part of the KDE Project Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include + K_PLUGIN_FACTORY_WITH_JSON(KCMSplashScreenFactory, "kcm_splashscreen.json", registerPlugin();) KCMSplashScreen::KCMSplashScreen(QObject* parent, const QVariantList& args) : KQuickAddons::ConfigModule(parent, args) , m_config(QStringLiteral("ksplashrc")) , m_configGroup(m_config.group("KSplash")) { qmlRegisterType(); KAboutData* about = new KAboutData(QStringLiteral("kcm_splashscreen"), i18n("Configure Splash screen details"), QStringLiteral("0.1"), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("Marco Martin"), QString(), QStringLiteral("mart@kde.org")); setAboutData(about); setButtons(Help | Apply | Default); m_model = new QStandardItemModel(this); QHash roles = m_model->roleNames(); roles[PluginNameRole] = "pluginName"; roles[ScreenhotRole] = "screenshot"; m_model->setItemRoleNames(roles); } QList KCMSplashScreen::availablePackages(const QString &component) { QList packages; QStringList paths; const QStringList dataPaths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); for (const QString &path : dataPaths) { QDir dir(path + QStringLiteral("/plasma/look-and-feel")); paths << dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); } for (const QString &path : paths) { Plasma::Package pkg = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); pkg.setPath(path); pkg.setFallbackPackage(Plasma::Package()); if (component.isEmpty() || !pkg.filePath(component.toUtf8()).isEmpty()) { packages << pkg; } } return packages; } QStandardItemModel *KCMSplashScreen::splashModel() { return m_model; } QString KCMSplashScreen::selectedPlugin() const { return m_selectedPlugin; } void KCMSplashScreen::setSelectedPlugin(const QString &plugin) { if (m_selectedPlugin == plugin) { return; } if (!m_selectedPlugin.isEmpty()) { setNeedsSave(true); } m_selectedPlugin = plugin; emit selectedPluginChanged(); } +void KCMSplashScreen::getNewClicked() +{ + KNS3::DownloadDialog dialog("ksplash.knsrc", 0); + if (dialog.exec()) { + KNS3::Entry::List list = dialog.changedEntries(); + if (list.count() > 0) { + load(); + } + } +} + void KCMSplashScreen::load() { m_package = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); const QString packageName = cg.readEntry("LookAndFeelPackage", QString()); if (!packageName.isEmpty()) { m_package.setPath(packageName); } QString currentPlugin = m_configGroup.readEntry("Theme", QString()); if (currentPlugin.isEmpty()) { currentPlugin = m_package.metadata().pluginName(); } setSelectedPlugin(currentPlugin); m_model->clear(); QStandardItem* row = new QStandardItem(i18n("None")); row->setData("None", PluginNameRole); m_model->appendRow(row); const QList pkgs = availablePackages(QStringLiteral("splashmainscript")); for (const Plasma::Package &pkg : pkgs) { QStandardItem* row = new QStandardItem(pkg.metadata().name()); row->setData(pkg.metadata().pluginName(), PluginNameRole); row->setData(pkg.filePath("previews", QStringLiteral("splash.png")), ScreenhotRole); m_model->appendRow(row); } setNeedsSave(false); } void KCMSplashScreen::save() { if (m_selectedPlugin.isEmpty()) { return; } else if (m_selectedPlugin == QLatin1String("None")) { m_configGroup.writeEntry("Theme", m_selectedPlugin); m_configGroup.writeEntry("Engine", "none"); } else { m_configGroup.writeEntry("Theme", m_selectedPlugin); m_configGroup.writeEntry("Engine", "KSplashQML"); } m_configGroup.sync(); setNeedsSave(false); } void KCMSplashScreen::defaults() { if (!m_package.metadata().isValid()) { return; } setSelectedPlugin(m_package.metadata().pluginName()); } int KCMSplashScreen::selectedPluginIndex() const { for (int i = 0; i < m_model->rowCount(); ++i) { if (m_model->data(m_model->index(i, 0), PluginNameRole).toString() == m_selectedPlugin) { return i; } } return -1; } void KCMSplashScreen::test(const QString &plugin) { if (plugin.isEmpty() || plugin == QLatin1String("None")) { return; } QProcess proc; QStringList arguments; arguments << plugin << QStringLiteral("--test"); if (proc.execute(QStringLiteral("ksplashqml"), arguments)) { QMessageBox::critical(0, i18n("Error"), i18n("Failed to successfully test the splash screen.")); } } #include "kcm.moc" diff --git a/kcms/ksplash/kcm.h b/kcms/ksplash/kcm.h index cc0a27b2d..ff56613cd 100644 --- a/kcms/ksplash/kcm.h +++ b/kcms/ksplash/kcm.h @@ -1,73 +1,74 @@ /* Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KCM_SEARCH_H #define _KCM_SEARCH_H #include #include #include #include class QStandardItemModel; class KCMSplashScreen : public KQuickAddons::ConfigModule { Q_OBJECT Q_PROPERTY(QStandardItemModel *splashModel READ splashModel CONSTANT) Q_PROPERTY(QString selectedPlugin READ selectedPlugin WRITE setSelectedPlugin NOTIFY selectedPluginChanged) Q_PROPERTY(int selectedPluginIndex READ selectedPluginIndex NOTIFY selectedPluginIndexChanged) public: enum Roles { PluginNameRole = Qt::UserRole +1, ScreenhotRole }; KCMSplashScreen(QObject* parent, const QVariantList& args); QList availablePackages(const QString &component); QStandardItemModel *splashModel(); QString selectedPlugin() const; void setSelectedPlugin(const QString &plugin); int selectedPluginIndex() const; public Q_SLOTS: + void getNewClicked(); void load() override; void save() override; void defaults() override; void test(const QString &plugin); Q_SIGNALS: void selectedPluginChanged(); void selectedPluginIndexChanged(); private: QStandardItemModel *m_model; Plasma::Package m_package; QString m_selectedPlugin; KConfig m_config; KConfigGroup m_configGroup; }; #endif diff --git a/kcms/ksplash/ksplash.knsrc b/kcms/ksplash/ksplash.knsrc new file mode 100644 index 000000000..25b641fb1 --- /dev/null +++ b/kcms/ksplash/ksplash.knsrc @@ -0,0 +1,8 @@ +[KNewStuff3] +Name=Splash Screens + +ProvidersUrl=https://autoconfig.kde.org/ocs/providers.xml +Categories=Plasma Splashscreens +StandardResource=tmp +InstallationCommand=kpackagetool5 -t Plasma/LookAndFeel -i %f +UninstallCommand=kpackagetool5 -t Plasma/LookAndFeel -r %f diff --git a/kcms/ksplash/package/contents/ui/main.qml b/kcms/ksplash/package/contents/ui/main.qml index 4b93a5fa2..0169d4619 100644 --- a/kcms/ksplash/package/contents/ui/main.qml +++ b/kcms/ksplash/package/contents/ui/main.qml @@ -1,155 +1,170 @@ /* Copyright (c) 2014 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.0 as QtControls import org.kde.kquickcontrolsaddons 2.0 import QtQuick.Controls.Private 1.0 import org.kde.kirigami 2.0 // for Units import org.kde.plasma.components 2.0 as PlasmaComponents Item { implicitWidth: Units.gridUnit * 20 implicitHeight: Units.gridUnit * 20 SystemPalette {id: syspal} - QtControls.ScrollView { + ColumnLayout { anchors.fill: parent - GridView { - id: grid - model: kcm.splashModel - cellWidth: Math.floor(grid.width / Math.max(Math.floor(grid.width / (Units.gridUnit*12)), 3)) - cellHeight: cellWidth / 1.6 + QtControls.ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + GridView { + id: grid + model: kcm.splashModel + cellWidth: Math.floor(grid.width / Math.max(Math.floor(grid.width / (Units.gridUnit*12)), 3)) + cellHeight: cellWidth / 1.6 - onCountChanged: { - grid.currentIndex = kcm.selectedPluginIndex; - grid.positionViewAtIndex(grid.currentIndex, GridView.Visible) - } + onCountChanged: { + grid.currentIndex = kcm.selectedPluginIndex; + grid.positionViewAtIndex(grid.currentIndex, GridView.Visible) + } - delegate: Item { - width: grid.cellWidth - height: grid.cellHeight - Rectangle { - anchors { - fill: parent - margins: Units.smallSpacing - } + delegate: Item { + width: grid.cellWidth + height: grid.cellHeight + Rectangle { + anchors { + fill: parent + margins: Units.smallSpacing + } - Connections { - target: kcm - onSelectedPluginChanged: { - if (kcm.selectedPlugin == model.pluginName) { - grid.currentIndex = index + Connections { + target: kcm + onSelectedPluginChanged: { + if (kcm.selectedPlugin == model.pluginName) { + grid.currentIndex = index + } } } - } - QIconItem { - id: icon - anchors.centerIn: parent - width: Units.iconSizes.large - height: width - icon: "view-preview" - } - Image { - id: image - anchors { - fill: parent - margins: Units.smallSpacing * 2 + QIconItem { + id: icon + anchors.centerIn: parent + width: Units.iconSizes.large + height: width + icon: "view-preview" } - source: model.screenshot || "" - Rectangle { + Image { + id: image anchors { - left: parent.left - right: parent.right - bottom: parent.bottom + fill: parent + margins: Units.smallSpacing * 2 } - height: childrenRect.height - gradient: Gradient { - GradientStop { - position: 0.0 - color: "transparent" + source: model.screenshot || "" + Rectangle { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom } - GradientStop { - position: 1.0 - color: image.status == Image.Ready ? Qt.rgba(0, 0, 0, 0.5) : "transparent" + height: childrenRect.height + gradient: Gradient { + GradientStop { + position: 0.0 + color: "transparent" + } + GradientStop { + position: 1.0 + color: image.status == Image.Ready ? Qt.rgba(0, 0, 0, 0.5) : "transparent" + } } - } - QtControls.Label { - anchors { - horizontalCenter: parent.horizontalCenter + QtControls.Label { + anchors { + horizontalCenter: parent.horizontalCenter + } + color: image.status == Image.Ready ? "white" : "gray" + text: model.display } - color: image.status == Image.Ready ? "white" : "gray" - text: model.display } } - } - Rectangle { - opacity: grid.currentIndex == index ? 1.0 : 0 - anchors.fill: parent - border.width: Units.smallSpacing * 2 - border.color: syspal.highlight - color: "transparent" - Behavior on opacity { - PropertyAnimation { - duration: Units.longDuration - easing.type: Easing.OutQuad + Rectangle { + opacity: grid.currentIndex == index ? 1.0 : 0 + anchors.fill: parent + border.width: Units.smallSpacing * 2 + border.color: syspal.highlight + color: "transparent" + Behavior on opacity { + PropertyAnimation { + duration: Units.longDuration + easing.type: Easing.OutQuad + } } } - } - MouseArea { - anchors.fill: parent - hoverEnabled: true - onClicked: { - grid.currentIndex = index - kcm.selectedPlugin = model.pluginName - } - Timer { - interval: 1000 // FIXME TODO: Use platform value for tooltip activation delay. + MouseArea { + anchors.fill: parent + hoverEnabled: true + onClicked: { + grid.currentIndex = index + kcm.selectedPlugin = model.pluginName + } + Timer { + interval: 1000 // FIXME TODO: Use platform value for tooltip activation delay. - running: parent.containsMouse && !parent.pressedButtons + running: parent.containsMouse && !parent.pressedButtons - onTriggered: { - Tooltip.showText(parent, Qt.point(parent.mouseX, parent.mouseY), model.display); - } - } - PlasmaComponents.ToolButton { - anchors { - top: parent.top - right: parent.right - margins: Units.smallSpacing + onTriggered: { + Tooltip.showText(parent, Qt.point(parent.mouseX, parent.mouseY), model.display); + } } - visible: model.pluginName != "None" - iconSource: "media-playback-start" - tooltip: i18n("Test Splashscreen") - flat: false - onClicked: kcm.test(model.pluginName) - opacity: parent.containsMouse ? 1 : 0 - Behavior on opacity { - PropertyAnimation { - duration: Units.longDuration - easing.type: Easing.OutQuad + PlasmaComponents.ToolButton { + anchors { + top: parent.top + right: parent.right + margins: Units.smallSpacing + } + visible: model.pluginName != "None" + iconSource: "media-playback-start" + tooltip: i18n("Test Splashscreen") + flat: false + onClicked: kcm.test(model.pluginName) + opacity: parent.containsMouse ? 1 : 0 + Behavior on opacity { + PropertyAnimation { + duration: Units.longDuration + easing.type: Easing.OutQuad + } } } } } } } } + RowLayout { + Item { + Layout.fillWidth: true + } + QtControls.Button { + iconName: "get-hot-new-stuff" + text: i18n("&Get New Theme...") + onClicked: kcm.getNewClicked(); + enabled: kcm.canInstall + } + } } } diff --git a/kcms/lookandfeel/kcm.cpp b/kcms/lookandfeel/kcm.cpp index b1646b77c..6cc785cbb 100644 --- a/kcms/lookandfeel/kcm.cpp +++ b/kcms/lookandfeel/kcm.cpp @@ -1,810 +1,817 @@ /* This file is part of the KDE Project Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcm.h" #include "../krdb/krdb.h" #include "../cursortheme/xcursor/xcursortheme.h" #include "config-kcm.h" #include "config-workspace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_XFIXES # include #endif KCMLookandFeel::KCMLookandFeel(QObject* parent, const QVariantList& args) : KQuickAddons::ConfigModule(parent, args) , m_config(QStringLiteral("kdeglobals")) , m_configGroup(m_config.group("KDE")) , m_applyColors(true) , m_applyWidgetStyle(true) , m_applyIcons(true) , m_applyPlasmaTheme(true) , m_applyCursors(true) , m_applyWindowSwitcher(true) , m_applyDesktopSwitcher(true) , m_resetDefaultLayout(false) , m_applyWindowDecoration(true) { //This flag seems to be needed in order for QQuickWidget to work //see https://bugreports.qt-project.org/browse/QTBUG-40765 //also, it seems to work only if set in the kcm, not in the systemsettings' main qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); qmlRegisterType(); qmlRegisterType(); KAboutData* about = new KAboutData(QStringLiteral("kcm_lookandfeel"), i18n("Configure Look and Feel details"), QStringLiteral("0.1"), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("Marco Martin"), QString(), QStringLiteral("mart@kde.org")); setAboutData(about); setButtons(Apply | Default); m_model = new QStandardItemModel(this); QHash roles = m_model->roleNames(); roles[PluginNameRole] = "pluginName"; roles[ScreenhotRole] = "screenshot"; roles[FullScreenPreviewRole] = "fullScreenPreview"; roles[HasSplashRole] = "hasSplash"; roles[HasLockScreenRole] = "hasLockScreen"; roles[HasRunCommandRole] = "hasRunCommand"; roles[HasLogoutRole] = "hasLogout"; roles[HasColorsRole] = "hasColors"; roles[HasWidgetStyleRole] = "hasWidgetStyle"; roles[HasIconsRole] = "hasIcons"; roles[HasPlasmaThemeRole] = "hasPlasmaTheme"; roles[HasCursorsRole] = "hasCursors"; roles[HasWindowSwitcherRole] = "hasWindowSwitcher"; roles[HasDesktopSwitcherRole] = "hasDesktopSwitcher"; m_model->setItemRoleNames(roles); } KCMLookandFeel::~KCMLookandFeel() { } void KCMLookandFeel::getNewStuff() { if (!m_newStuffDialog) { m_newStuffDialog = new KNS3::DownloadDialog( QLatin1String("lookandfeel.knsrc") ); m_newStuffDialog.data()->setWindowTitle(i18n("Download New Look And Feel Packages")); connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, &KCMLookandFeel::load); } m_newStuffDialog.data()->show(); } QStandardItemModel *KCMLookandFeel::lookAndFeelModel() { return m_model; } QString KCMLookandFeel::selectedPlugin() const { return m_selectedPlugin; } void KCMLookandFeel::setSelectedPlugin(const QString &plugin) { if (m_selectedPlugin == plugin) { return; } const bool firstTime = m_selectedPlugin.isNull(); m_selectedPlugin = plugin; emit selectedPluginChanged(); emit selectedPluginIndexChanged(); if (!firstTime) { setNeedsSave(true); } } int KCMLookandFeel::selectedPluginIndex() const { for (int i = 0; i < m_model->rowCount(); ++i) { if (m_model->data(m_model->index(i, 0), PluginNameRole).toString() == m_selectedPlugin) { return i; } } return -1; } -QList KCMLookandFeel::availablePackages(const QString &component) +QList KCMLookandFeel::availablePackages(const QStringList &components) { QList packages; QStringList paths; const QStringList dataPaths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); paths.reserve(dataPaths.count()); for (const QString &path : dataPaths) { QDir dir(path + QStringLiteral("/plasma/look-and-feel")); paths << dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); } for (const QString &path : paths) { Plasma::Package pkg = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); pkg.setPath(path); pkg.setFallbackPackage(Plasma::Package()); - if (component.isEmpty() || !pkg.filePath(component.toUtf8()).isEmpty()) { + if (components.isEmpty()) { packages << pkg; + } else { + for (const auto &component : components) { + if (!pkg.filePath(component.toUtf8()).isEmpty()) { + packages << pkg; + break; + } + } } } return packages; } void KCMLookandFeel::load() { m_package = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); const QString packageName = cg.readEntry("LookAndFeelPackage", QString()); if (!packageName.isEmpty()) { m_package.setPath(packageName); } if (!m_package.metadata().isValid()) { return; } setSelectedPlugin(m_package.metadata().pluginName()); m_model->clear(); - const QList pkgs = availablePackages(); + const QList pkgs = availablePackages({"defaults", "layouts"}); for (const Plasma::Package &pkg : pkgs) { if (!pkg.metadata().isValid()) { continue; } QStandardItem* row = new QStandardItem(pkg.metadata().name()); row->setData(pkg.metadata().pluginName(), PluginNameRole); row->setData(pkg.filePath("preview"), ScreenhotRole); row->setData(pkg.filePath("fullscreenpreview"), FullScreenPreviewRole); //What the package provides row->setData(!pkg.filePath("splashmainscript").isEmpty(), HasSplashRole); row->setData(!pkg.filePath("lockscreenmainscript").isEmpty(), HasLockScreenRole); row->setData(!pkg.filePath("runcommandmainscript").isEmpty(), HasRunCommandRole); row->setData(!pkg.filePath("logoutmainscript").isEmpty(), HasLogoutRole); if (!pkg.filePath("defaults").isEmpty()) { KSharedConfigPtr conf = KSharedConfig::openConfig(pkg.filePath("defaults")); KConfigGroup cg(conf, "kdeglobals"); cg = KConfigGroup(&cg, "General"); bool hasColors = !cg.readEntry("ColorScheme", QString()).isEmpty(); if (!hasColors) { hasColors = !pkg.filePath("colors").isEmpty(); } row->setData(hasColors, HasColorsRole); cg = KConfigGroup(&cg, "KDE"); row->setData(!cg.readEntry("widgetStyle", QString()).isEmpty(), HasWidgetStyleRole); cg = KConfigGroup(conf, "kdeglobals"); cg = KConfigGroup(&cg, "Icons"); row->setData(!cg.readEntry("Theme", QString()).isEmpty(), HasIconsRole); cg = KConfigGroup(conf, "kdeglobals"); cg = KConfigGroup(&cg, "Theme"); row->setData(!cg.readEntry("name", QString()).isEmpty(), HasPlasmaThemeRole); cg = KConfigGroup(conf, "kcminputrc"); cg = KConfigGroup(&cg, "Mouse"); row->setData(!cg.readEntry("cursorTheme", QString()).isEmpty(), HasCursorsRole); cg = KConfigGroup(conf, "kwinrc"); cg = KConfigGroup(&cg, "WindowSwitcher"); row->setData(!cg.readEntry("LayoutName", QString()).isEmpty(), HasWindowSwitcherRole); cg = KConfigGroup(conf, "kwinrc"); cg = KConfigGroup(&cg, "DesktopSwitcher"); row->setData(!cg.readEntry("LayoutName", QString()).isEmpty(), HasDesktopSwitcherRole); } m_model->appendRow(row); } setNeedsSave(false); } void KCMLookandFeel::save() { Plasma::Package package = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); package.setPath(m_selectedPlugin); if (!package.isValid()) { return; } m_configGroup.writeEntry("LookAndFeelPackage", m_selectedPlugin); if (m_resetDefaultLayout) { QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"), QStringLiteral("/PlasmaShell"), QStringLiteral("org.kde.PlasmaShell"), QStringLiteral("loadLookAndFeelDefaultLayout")); QList args; args << m_selectedPlugin; message.setArguments(args); QDBusConnection::sessionBus().call(message, QDBus::NoBlock); } if (!package.filePath("defaults").isEmpty()) { KSharedConfigPtr conf = KSharedConfig::openConfig(package.filePath("defaults")); KConfigGroup cg(conf, "kdeglobals"); cg = KConfigGroup(&cg, "KDE"); if (m_applyWidgetStyle) { setWidgetStyle(cg.readEntry("widgetStyle", QString())); } if (m_applyColors) { QString colorsFile = package.filePath("colors"); KConfigGroup cg(conf, "kdeglobals"); cg = KConfigGroup(&cg, "General"); QString colorScheme = cg.readEntry("ColorScheme", QString()); if (!colorsFile.isEmpty()) { if (!colorScheme.isEmpty()) { setColors(colorScheme, colorsFile); } else { setColors(package.metadata().name(), colorsFile); } } else if (!colorScheme.isEmpty()) { colorScheme.remove('\''); // So Foo's does not become FooS QRegExp fixer(QStringLiteral("[\\W,.-]+(.?)")); int offset; while ((offset = fixer.indexIn(colorScheme)) >= 0) { colorScheme.replace(offset, fixer.matchedLength(), fixer.cap(1).toUpper()); } colorScheme.replace(0, 1, colorScheme.at(0).toUpper()); //NOTE: why this loop trough all the scheme files? //the scheme theme name is an heuristic, there is no plugin metadata whatsoever. //is based on the file name stripped from weird characters or the //eventual id- prefix store.kde.org puts, so we can just find a //theme that ends as the specified name bool schemeFound = false; const QStringList schemeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); for (const QString &dir : schemeDirs) { const QStringList fileNames = QDir(dir).entryList(QStringList()<emitChange(KGlobalSettings::StyleChanged); } void KCMLookandFeel::setColors(const QString &scheme, const QString &colorFile) { if (scheme.isEmpty() && colorFile.isEmpty()) { return; } KConfigGroup configGroup(&m_config, "General"); configGroup.writeEntry("ColorScheme", scheme); KSharedConfigPtr conf = KSharedConfig::openConfig(colorFile, KSharedConfig::CascadeConfig); foreach (const QString &grp, conf->groupList()) { KConfigGroup cg(conf, grp); KConfigGroup cg2(&m_config, grp); cg.copyTo(&cg2); } configGroup.sync(); KGlobalSettings::self()->emitChange(KGlobalSettings::PaletteChanged); } void KCMLookandFeel::setIcons(const QString &theme) { if (theme.isEmpty()) { return; } KConfigGroup cg(&m_config, "Icons"); cg.writeEntry("Theme", theme); cg.sync(); for (int i=0; i < KIconLoader::LastGroup; i++) { KIconLoader::emitChange(KIconLoader::Group(i)); } } void KCMLookandFeel::setPlasmaTheme(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("plasmarc")); KConfigGroup cg(&config, "Theme"); cg.writeEntry("name", theme); cg.sync(); } void KCMLookandFeel::setCursorTheme(const QString themeName) { //TODO: use pieces of cursor kcm when moved to plasma-desktop if (themeName.isEmpty()) { return; } KConfig config(QStringLiteral("kcminputrc")); KConfigGroup cg(&config, "Mouse"); cg.writeEntry("cursorTheme", themeName); cg.sync(); // Require the Xcursor version that shipped with X11R6.9 or greater, since // in previous versions the Xfixes code wasn't enabled due to a bug in the // build system (freedesktop bug #975). #if HAVE_XFIXES && XFIXES_MAJOR >= 2 && XCURSOR_LIB_VERSION >= 10105 const int cursorSize = cg.readEntry("cursorSize", 0); QDir themeDir = cursorThemeDir(themeName, 0); if (!themeDir.exists()) { return; } XCursorTheme theme(themeDir); if (!CursorTheme::haveXfixes()) { return; } // Set up the proper launch environment for newly started apps OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), QStringLiteral("/KLauncher"), QDBusConnection::sessionBus()); klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), themeName); // Update the Xcursor X resources runRdb(0); // Notify all applications that the cursor theme has changed KGlobalSettings::self()->emitChange(KGlobalSettings::CursorChanged); // Reload the standard cursors QStringList names; // Qt cursors names << QStringLiteral("left_ptr") << QStringLiteral("up_arrow") << QStringLiteral("cross") << QStringLiteral("wait") << QStringLiteral("left_ptr_watch") << QStringLiteral("ibeam") << QStringLiteral("size_ver") << QStringLiteral("size_hor") << QStringLiteral("size_bdiag") << QStringLiteral("size_fdiag") << QStringLiteral("size_all") << QStringLiteral("split_v") << QStringLiteral("split_h") << QStringLiteral("pointing_hand") << QStringLiteral("openhand") << QStringLiteral("closedhand") << QStringLiteral("forbidden") << QStringLiteral("whats_this") << QStringLiteral("copy") << QStringLiteral("move") << QStringLiteral("link"); // X core cursors names << QStringLiteral("X_cursor") << QStringLiteral("right_ptr") << QStringLiteral("hand1") << QStringLiteral("hand2") << QStringLiteral("watch") << QStringLiteral("xterm") << QStringLiteral("crosshair") << QStringLiteral("left_ptr_watch") << QStringLiteral("center_ptr") << QStringLiteral("sb_h_double_arrow") << QStringLiteral("sb_v_double_arrow") << QStringLiteral("fleur") << QStringLiteral("top_left_corner") << QStringLiteral("top_side") << QStringLiteral("top_right_corner") << QStringLiteral("right_side") << QStringLiteral("bottom_right_corner") << QStringLiteral("bottom_side") << QStringLiteral("bottom_left_corner") << QStringLiteral("left_side") << QStringLiteral("question_arrow") << QStringLiteral("pirate"); foreach (const QString &name, names) { XFixesChangeCursorByName(QX11Info::display(), theme.loadCursor(name, cursorSize), QFile::encodeName(name)); } #else KMessageBox::information(this, i18n("You have to restart the Plasma session for these changes to take effect."), i18n("Cursor Settings Changed"), "CursorSettingsChanged"); #endif } QDir KCMLookandFeel::cursorThemeDir(const QString &theme, const int depth) { // Prevent infinite recursion if (depth > 10) { return QDir(); } // Search each icon theme directory for 'theme' foreach (const QString &baseDir, cursorSearchPaths()) { QDir dir(baseDir); if (!dir.exists() || !dir.cd(theme)) { continue; } // If there's a cursors subdir, we'll assume this is a cursor theme if (dir.exists(QStringLiteral("cursors"))) { return dir; } // If the theme doesn't have an index.theme file, it can't inherit any themes. if (!dir.exists(QStringLiteral("index.theme"))) { continue; } // Open the index.theme file, so we can get the list of inherited themes KConfig config(dir.path() + "/index.theme", KConfig::NoGlobals); KConfigGroup cg(&config, "Icon Theme"); // Recurse through the list of inherited themes, to check if one of them // is a cursor theme. QStringList inherits = cg.readEntry("Inherits", QStringList()); foreach (const QString &inherit, inherits) { // Avoid possible DoS if (inherit == theme) { continue; } if (cursorThemeDir(inherit, depth + 1).exists()) { return dir; } } } return QDir(); } const QStringList KCMLookandFeel::cursorSearchPaths() { if (!m_cursorSearchPaths.isEmpty()) return m_cursorSearchPaths; #if XCURSOR_LIB_MAJOR == 1 && XCURSOR_LIB_MINOR < 1 // These are the default paths Xcursor will scan for cursor themes QString path("~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons"); // If XCURSOR_PATH is set, use that instead of the default path char *xcursorPath = std::getenv("XCURSOR_PATH"); if (xcursorPath) path = xcursorPath; #else // Get the search path from Xcursor QString path = XcursorLibraryPath(); #endif // Separate the paths m_cursorSearchPaths = path.split(QLatin1Char(':'), QString::SkipEmptyParts); // Remove duplicates QMutableStringListIterator i(m_cursorSearchPaths); while (i.hasNext()) { const QString path = i.next(); QMutableStringListIterator j(i); while (j.hasNext()) if (j.next() == path) j.remove(); } // Expand all occurrences of ~/ to the home dir m_cursorSearchPaths.replaceInStrings(QRegExp(QStringLiteral("^~\\/")), QDir::home().path() + QLatin1Char('/')); return m_cursorSearchPaths; } void KCMLookandFeel::setSplashScreen(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("ksplashrc")); KConfigGroup cg(&config, "KSplash"); cg.writeEntry("Theme", theme); //TODO: a way to set none as spash in the l&f cg.writeEntry("Engine", "KSplashQML"); cg.sync(); } void KCMLookandFeel::setLockScreen(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("kscreenlockerrc")); KConfigGroup cg(&config, "Greeter"); cg.writeEntry("Theme", theme); cg.sync(); } void KCMLookandFeel::setWindowSwitcher(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("kwinrc")); KConfigGroup cg(&config, "TabBox"); cg.writeEntry("LayoutName", theme); cg.sync(); } void KCMLookandFeel::setDesktopSwitcher(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("kwinrc")); KConfigGroup cg(&config, "TabBox"); cg.writeEntry("DesktopLayout", theme); cg.writeEntry("DesktopListLayout", theme); cg.sync(); } void KCMLookandFeel::setWindowDecoration(const QString &library, const QString &theme) { if (library.isEmpty()) { return; } KConfig config(QStringLiteral("kwinrc")); KConfigGroup cg(&config, "org.kde.kdecoration2"); cg.writeEntry("library", library); cg.writeEntry("theme", theme); cg.sync(); // Reload KWin. QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); QDBusConnection::sessionBus().send(message); } void KCMLookandFeel::setApplyColors(bool apply) { if (m_applyColors == apply) { return; } m_applyColors = apply; emit applyColorsChanged(); } bool KCMLookandFeel::applyColors() const { return m_applyColors; } void KCMLookandFeel::setApplyWidgetStyle(bool apply) { if (m_applyWidgetStyle == apply) { return; } m_applyWidgetStyle = apply; emit applyWidgetStyleChanged(); } bool KCMLookandFeel::applyWidgetStyle() const { return m_applyWidgetStyle; } void KCMLookandFeel::setApplyIcons(bool apply) { if (m_applyIcons == apply) { return; } m_applyIcons = apply; emit applyIconsChanged(); } bool KCMLookandFeel::applyIcons() const { return m_applyIcons; } void KCMLookandFeel::setApplyPlasmaTheme(bool apply) { if (m_applyPlasmaTheme == apply) { return; } m_applyPlasmaTheme = apply; emit applyPlasmaThemeChanged(); } bool KCMLookandFeel::applyPlasmaTheme() const { return m_applyPlasmaTheme; } void KCMLookandFeel::setApplyWindowSwitcher(bool apply) { if (m_applyWindowSwitcher == apply) { return; } m_applyWindowSwitcher = apply; emit applyWindowSwitcherChanged(); } bool KCMLookandFeel::applyWindowSwitcher() const { return m_applyWindowSwitcher; } void KCMLookandFeel::setApplyDesktopSwitcher(bool apply) { if (m_applyDesktopSwitcher == apply) { return; } m_applyDesktopSwitcher = apply; emit applyDesktopSwitcherChanged(); } bool KCMLookandFeel::applyDesktopSwitcher() const { return m_applyDesktopSwitcher; } void KCMLookandFeel::setResetDefaultLayout(bool reset) { if (m_resetDefaultLayout == reset) { return; } m_resetDefaultLayout = reset; emit resetDefaultLayoutChanged(); if (reset) { setNeedsSave(true); } } bool KCMLookandFeel::resetDefaultLayout() const { return m_resetDefaultLayout; } diff --git a/kcms/lookandfeel/kcm.h b/kcms/lookandfeel/kcm.h index c940af8f5..911534171 100644 --- a/kcms/lookandfeel/kcm.h +++ b/kcms/lookandfeel/kcm.h @@ -1,149 +1,150 @@ /* Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KCM_SEARCH_H #define _KCM_SEARCH_H #include #include #include #include #include #include #include class QQuickView; class QStandardItemModel; class KCMLookandFeel : public KQuickAddons::ConfigModule { Q_OBJECT Q_PROPERTY(QStandardItemModel *lookAndFeelModel READ lookAndFeelModel CONSTANT) Q_PROPERTY(QString selectedPlugin READ selectedPlugin WRITE setSelectedPlugin NOTIFY selectedPluginChanged) Q_PROPERTY(int selectedPluginIndex READ selectedPluginIndex NOTIFY selectedPluginIndexChanged) Q_PROPERTY(bool applyColors READ applyColors WRITE setApplyColors NOTIFY applyColorsChanged) Q_PROPERTY(bool applyWidgetStyle READ applyWidgetStyle WRITE setApplyWidgetStyle NOTIFY applyWidgetStyleChanged) Q_PROPERTY(bool applyIcons READ applyIcons WRITE setApplyIcons NOTIFY applyIconsChanged) Q_PROPERTY(bool applyPlasmaTheme READ applyPlasmaTheme WRITE setApplyPlasmaTheme NOTIFY applyPlasmaThemeChanged) Q_PROPERTY(bool applyWindowSwitcher READ applyWindowSwitcher WRITE setApplyWindowSwitcher NOTIFY applyWindowSwitcherChanged) Q_PROPERTY(bool applyDesktopSwitcher READ applyDesktopSwitcher WRITE setApplyDesktopSwitcher NOTIFY applyDesktopSwitcherChanged) Q_PROPERTY(bool resetDefaultLayout READ resetDefaultLayout WRITE setResetDefaultLayout NOTIFY resetDefaultLayoutChanged) public: enum Roles { PluginNameRole = Qt::UserRole +1, ScreenhotRole, FullScreenPreviewRole, HasSplashRole, HasLockScreenRole, HasRunCommandRole, HasLogoutRole, HasColorsRole, HasWidgetStyleRole, HasIconsRole, HasPlasmaThemeRole, HasCursorsRole, HasWindowSwitcherRole, HasDesktopSwitcherRole }; KCMLookandFeel(QObject* parent, const QVariantList& args); ~KCMLookandFeel(); - QList availablePackages(const QString &component = QString()); + //List only packages which provide at least one of the components + QList availablePackages(const QStringList &components); QStandardItemModel *lookAndFeelModel(); QString selectedPlugin() const; void setSelectedPlugin(const QString &plugin); int selectedPluginIndex() const; //Setters of the various theme pieces void setWidgetStyle(const QString &style); void setColors(const QString &scheme, const QString &colorFile); void setIcons(const QString &theme); void setPlasmaTheme(const QString &theme); void setCursorTheme(const QString theme); void setSplashScreen(const QString &theme); void setLockScreen(const QString &theme); void setWindowSwitcher(const QString &theme); void setDesktopSwitcher(const QString &theme); void setWindowDecoration(const QString &library, const QString &theme); void setApplyColors(bool apply); bool applyColors() const; void setApplyWidgetStyle(bool apply); bool applyWidgetStyle() const; void setApplyIcons(bool apply); bool applyIcons() const; void setApplyPlasmaTheme(bool apply); bool applyPlasmaTheme() const; void setApplyWindowSwitcher(bool apply); bool applyWindowSwitcher() const; void setApplyDesktopSwitcher(bool apply); bool applyDesktopSwitcher() const; bool resetDefaultLayout() const; void setResetDefaultLayout(bool reset); Q_INVOKABLE void getNewStuff(); public Q_SLOTS: void load() override; void save() override; void defaults() override; Q_SIGNALS: void selectedPluginChanged(); void applyColorsChanged(); void applyWidgetStyleChanged(); void applyIconsChanged(); void applyPlasmaThemeChanged(); void applyWindowSwitcherChanged(); void applyDesktopSwitcherChanged(); void resetDefaultLayoutChanged(); void selectedPluginIndexChanged(); private: QDir cursorThemeDir(const QString &theme, const int depth); const QStringList cursorSearchPaths(); QStandardItemModel *m_model; Plasma::Package m_package; QString m_selectedPlugin; QStringList m_cursorSearchPaths; QPointer m_newStuffDialog; KConfig m_config; KConfigGroup m_configGroup; bool m_applyColors : 1; bool m_applyWidgetStyle : 1; bool m_applyIcons : 1; bool m_applyPlasmaTheme : 1; bool m_applyCursors : 1; bool m_applyWindowSwitcher : 1; bool m_applyDesktopSwitcher : 1; bool m_resetDefaultLayout : 1; bool m_applyWindowDecoration : 1; }; #endif