diff --git a/CMakeLists.txt b/CMakeLists.txt index bfacebb..939b1c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,36 +1,37 @@ project(kde-gtk-config) set(PROJECT_VERSION "5.17.90") cmake_minimum_required(VERSION 3.10) find_package(ECM REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ${ECM_MODULE_PATH}) find_package(Qt5 REQUIRED NO_MODULE COMPONENTS DBus) find_package(KF5CoreAddons REQUIRED) find_package(KF5Config REQUIRED) find_package(KF5IconThemes REQUIRED) find_package(KF5DBusAddons REQUIRED) find_package(GTK3 REQUIRED) find_package(GSettingSchemas REQUIRED) include(ECMSetupVersion) include(ECMInstallIcons) include(ECMMarkAsTest) include(GenerateExportHeader) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDEClangFormat) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake kded/config.h) add_subdirectory(gtkproxies) add_subdirectory(gtk3proxies) add_subdirectory(kded) +add_subdirectory(kconf_update) # add clang-format target for all our real source files file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/kconf_update/CMakeLists.txt b/kconf_update/CMakeLists.txt new file mode 100644 index 0000000..94eaa55 --- /dev/null +++ b/kconf_update/CMakeLists.txt @@ -0,0 +1,38 @@ +add_executable(gtk_theme gtktheme.cpp) + +target_sources(gtk_theme + PRIVATE + ../kded/configeditor.cpp +) + +target_link_libraries(gtk_theme + PRIVATE + Qt5::Core + KF5::ConfigCore + ${GIO2_LIBRARY} + ${GOBJECT2_LIBRARY} +) + +target_include_directories(gtk_theme + PRIVATE + ../kded/ + ${GTK3_INCLUDE_DIRS} +) + +target_compile_definitions(gtk_theme + PRIVATE + QT_NO_SIGNALS_SLOTS_KEYWORDS +) + +install( + TARGETS + gtk_theme + DESTINATION + ${LIB_INSTALL_DIR}/kconf_update_bin/ +) +install( + FILES + gtkconfig.upd + DESTINATION + ${KCONF_UPDATE_INSTALL_DIR} +) diff --git a/kconf_update/gtkconfig.upd b/kconf_update/gtkconfig.upd new file mode 100644 index 0000000..6214e77 --- /dev/null +++ b/kconf_update/gtkconfig.upd @@ -0,0 +1,3 @@ +Version=5 +Id=gtk_theme +Script=gtk_theme diff --git a/kconf_update/gtktheme.cpp b/kconf_update/gtktheme.cpp new file mode 100644 index 0000000..bf9993c --- /dev/null +++ b/kconf_update/gtktheme.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 Mikhail Zolotukhin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "configeditor.h" + +void upgradeGtk2Theme(); +void upgradeGtk3Theme(); + +int main() { + upgradeGtk2Theme(); + upgradeGtk3Theme(); + return 0; +} + +void upgradeGtk2Theme() { + QString currentGtk2Theme = ConfigEditor::gtk2ConfigValue(QStringLiteral("gtk-theme-name")); + if (currentGtk2Theme.isEmpty() + || currentGtk2Theme == QStringLiteral("oxygen-gtk") + || currentGtk2Theme == QStringLiteral("BreezyGTK") + || currentGtk2Theme == QStringLiteral("Orion") + ) { + ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-theme-name"), QStringLiteral("Breeze")); + } +} + +void upgradeGtk3Theme() { + QString currentGtk3Theme = ConfigEditor::gtk3ConfigValueSettingsIni(QStringLiteral("gtk-theme-name")); + if (currentGtk3Theme.isEmpty() + || currentGtk3Theme == QStringLiteral("oxygen-gtk") + || currentGtk3Theme == QStringLiteral("BreezyGTK") + || currentGtk3Theme == QStringLiteral("Orion") + ) { + ConfigEditor::setGtk3ConfigValueDconf(QStringLiteral("gtk-theme"), QStringLiteral("Breeze")); + ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-theme-name"), QStringLiteral("Breeze")); + ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Net/ThemeName"), QStringLiteral("Breeze")); + } +} diff --git a/kded/configeditor.cpp b/kded/configeditor.cpp index 244fe2c..3074f70 100644 --- a/kded/configeditor.cpp +++ b/kded/configeditor.cpp @@ -1,238 +1,238 @@ /* * Copyright (C) 2019 Mikhail Zolotukhin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "configeditor.h" void ConfigEditor::setGtk3ConfigValueDconf(const QString ¶mName, bool paramValue, const QString &category) { g_autoptr(GSettings) gsettings = g_settings_new(category.toUtf8().constData()); g_settings_set_boolean(gsettings, paramName.toUtf8().constData(), paramValue); } void ConfigEditor::setGtk3ConfigValueDconf(const QString ¶mName, const QString ¶mValue, const QString &category) { g_autoptr(GSettings) gsettings = g_settings_new(category.toUtf8().constData()); g_settings_set_string(gsettings, paramName.toUtf8().constData(), paramValue.toUtf8().constData()); } void ConfigEditor::setGtk3ConfigValueSettingsIni(const QString ¶mName, const QString ¶mValue) { QString configLocation = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QString gtk3ConfigPath = configLocation + QStringLiteral("/gtk-3.0/settings.ini"); KSharedConfig::Ptr gtk3Config = KSharedConfig::openConfig(gtk3ConfigPath, KConfig::NoGlobals); KConfigGroup group = gtk3Config->group(QStringLiteral("Settings")); group.writeEntry(paramName, paramValue); group.sync(); } void ConfigEditor::setGtk3ConfigValueXSettingsd(const QString ¶mName, const QString ¶mValue) { using qsp = QStandardPaths; QString configLocation = qsp::writableLocation(qsp::GenericConfigLocation); QDir xsettingsdPath = configLocation + QStringLiteral("/xsettingsd"); if (!xsettingsdPath.exists()) { xsettingsdPath.mkpath(QStringLiteral(".")); } QString xSettingsdConfigPath = xsettingsdPath.path() + QStringLiteral("/xsettingsd.conf"); QFile xSettingsdConfig(xSettingsdConfigPath); QString xSettingsdConfigContents = readFileContents(xSettingsdConfig); replaceValueInXSettingsdContents(xSettingsdConfigContents, paramName, paramValue); xSettingsdConfig.remove(); xSettingsdConfig.open(QIODevice::WriteOnly | QIODevice::Text); xSettingsdConfig.write(xSettingsdConfigContents.toUtf8()); reloadXSettingsd(); } void ConfigEditor::setGtk2ConfigValue(const QString ¶mName, const QString ¶mValue) { QString gtkrcPath = qEnvironmentVariable("GTK2_RC_FILES", QDir::homePath() + QStringLiteral("/.gtkrc-2.0")); if (gtkrcPath.contains(QStringLiteral(":/"))) { // I.e. env variable contains multiple paths gtkrcPath = QDir::homePath() + QStringLiteral("/.gtkrc-2.0"); } QFile gtkrc(gtkrcPath); QString gtkrcContents = readFileContents(gtkrc); replaceValueInGtkrcContents(gtkrcContents, paramName, paramValue); gtkrc.remove(); gtkrc.open(QIODevice::WriteOnly | QIODevice::Text); gtkrc.write(gtkrcContents.toUtf8()); reloadGtk2Apps(); } QString ConfigEditor::gtk2ConfigValue(const QString& paramName) { QString gtkrcPath = QDir::homePath() + QStringLiteral("/.gtkrc-2.0"); QFile gtkrc(gtkrcPath); if (gtkrc.open(QIODevice::ReadWrite | QIODevice::Text)) { const QRegularExpression regExp(paramName + QStringLiteral("=[^\n]*($|\n)")); while (!gtkrc.atEnd()) { QString line = gtkrc.readLine(); if (line.contains(regExp)) { return line.split('"')[1]; } } } - return QStringLiteral("Breeze"); + return QString(); } QString ConfigEditor::gtk3ConfigValueSettingsIni(const QString& paramName) { QString configLocation = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QString gtk3ConfigPath = configLocation + QStringLiteral("/gtk-3.0/settings.ini"); KSharedConfig::Ptr gtk3Config = KSharedConfig::openConfig(gtk3ConfigPath, KConfig::NoGlobals); KConfigGroup group = gtk3Config->group(QStringLiteral("Settings")); - return group.readEntry(paramName, QStringLiteral("Breeze")); + return group.readEntry(paramName); } void ConfigEditor::removeLegacyGtk2Strings() { QString gtkrcPath = QDir::homePath() + QStringLiteral("/.gtkrc-2.0"); QFile gtkrc(gtkrcPath); QString gtkrcContents = readFileContents(gtkrc); // Remove "include" lines // Example: // include "/usr/share/themes/Adwaita-dark/gtk-2.0/gtkrc" static const QRegularExpression includeRegExp(QStringLiteral("include .*\n")); gtkrcContents.remove(includeRegExp); // Remove redundant font config lines // Example: // style "user-font" // { // font_name="Noto Sans Regular" // } // widget_class "*" style "user-font" static const QRegularExpression userFontStyleRegexp(QStringLiteral("style(.|\n)*{(.|\n)*}\nwidget_class.*\"user-font\"")); gtkrcContents.remove(userFontStyleRegexp); gtkrc.remove(); gtkrc.open(QIODevice::WriteOnly | QIODevice::Text); gtkrc.write(gtkrcContents.toUtf8()); reloadGtk2Apps(); } QString ConfigEditor::readFileContents(QFile &file) { if (file.open(QIODevice::ReadWrite | QIODevice::Text)) { return file.readAll(); } else { return QString(); } } void ConfigEditor::replaceValueInGtkrcContents(QString >krcContents, const QString ¶mName, const QString ¶mValue) { const QRegularExpression regExp(paramName + QStringLiteral("=[^\n]*($|\n)")); static const QStringList nonStringProperties{ QStringLiteral("gtk-toolbar-style"), QStringLiteral("gtk-menu-images"), QStringLiteral("gtk-button-images"), QStringLiteral("gtk-primary-button-warps-slider"), QStringLiteral("gtk-enable-animations"), }; QString newConfigString; if (nonStringProperties.contains(paramName)) { newConfigString = paramName + QStringLiteral("=") + paramValue + QStringLiteral("\n"); } else { newConfigString = paramName + QStringLiteral("=\"") + paramValue + QStringLiteral("\"\n"); } if (gtkrcContents.contains(regExp)) { gtkrcContents.replace(regExp, newConfigString); } else { gtkrcContents = newConfigString + QStringLiteral("\n") + gtkrcContents; } } void ConfigEditor::replaceValueInXSettingsdContents(QString &xSettingsdContents, const QString ¶mName, const QString ¶mValue) { const QRegularExpression regExp(paramName + QStringLiteral(" [^\n]*($|\n)")); static const QStringList nonStringProperties{ QStringLiteral("Gtk/ButtonImages"), QStringLiteral("Gtk/MenuImages"), QStringLiteral("Gtk/ToolbarStyle"), QStringLiteral("Gtk/PrimaryButtonWarpsSlider"), QStringLiteral("Gtk/EnableAnimations"), }; QString newConfigString; if (nonStringProperties.contains(paramName)) { newConfigString = paramName + QStringLiteral(" ") + paramValue + QStringLiteral("\n"); } else { newConfigString = paramName + QStringLiteral(" \"") + paramValue + QStringLiteral("\"\n"); } if (xSettingsdContents.contains(regExp)) { xSettingsdContents.replace(regExp, newConfigString); } else { xSettingsdContents = newConfigString + QStringLiteral("\n") + xSettingsdContents; } } void ConfigEditor::reloadGtk2Apps() { QProcess::startDetached(QStandardPaths::findExecutable(QStringLiteral("reload_gtk_apps"))); } pid_t ConfigEditor::pidOfXSettingsd() { QProcess pidof; pidof.start(QStringLiteral("pidof"), QStringList() << QStringLiteral("-s") << QStringLiteral("xsettingsd")); pidof.waitForFinished(); QString xsettingsdPid = QString(pidof.readAllStandardOutput()).remove('\n'); return xsettingsdPid.toInt(); } void ConfigEditor::reloadXSettingsd() { pid_t xSettingsdPid = pidOfXSettingsd(); if (xSettingsdPid == 0) { QProcess::startDetached(QStandardPaths::findExecutable(QStringLiteral("xsettingsd"))); } else { kill(xSettingsdPid, SIGHUP); } }