1 /*
2  * SPDX-FileCopyrightText: 2019 Mikhail Zolotukhin <zomial@protonmail.com>
3  * SPDX-FileCopyrightText: 2019 Nicolas Fella <nicolas.fella@gmx.de>
4  *
5  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
6  */
7 
8 #include <QDBusConnection>
9 #include <QDBusMessage>
10 #include <QFont>
11 #include <QGuiApplication>
12 
13 #include <KColorScheme>
14 #include <KPluginFactory>
15 
16 #include "gtkconfig.h"
17 
18 K_PLUGIN_CLASS_WITH_JSON(GtkConfig, "gtkconfig.json")
19 
GtkConfig(QObject * parent,const QVariantList &)20 GtkConfig::GtkConfig(QObject *parent, const QVariantList &)
21     : KDEDModule(parent)
22     , configValueProvider(new ConfigValueProvider())
23     , themePreviewer(new ThemePreviewer(this))
24     , kdeglobalsConfigWatcher(KConfigWatcher::create(KSharedConfig::openConfig()))
25     , kwinConfigWatcher(KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("kwinrc"))))
26     , kcminputConfigWatcher(KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("kcminputrc"))))
27     , breezeConfigWatcher(KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("breezerc"))))
28 {
29     QDBusConnection dbus = QDBusConnection::sessionBus();
30     dbus.registerService(QStringLiteral("org.kde.GtkConfig"));
31     dbus.registerObject(QStringLiteral("/GtkConfig"), this, QDBusConnection::ExportScriptableSlots);
32 
33     connect(kdeglobalsConfigWatcher.data(), &KConfigWatcher::configChanged, this, &GtkConfig::onKdeglobalsSettingsChange);
34     connect(kwinConfigWatcher.data(), &KConfigWatcher::configChanged, this, &GtkConfig::onKWinSettingsChange);
35     connect(kcminputConfigWatcher.data(), &KConfigWatcher::configChanged, this, &GtkConfig::onKCMInputSettingsChange);
36     connect(breezeConfigWatcher.data(), &KConfigWatcher::configChanged, this, &GtkConfig::onBreezeSettingsChange);
37 
38     ConfigEditor::removeLegacyGtk2Strings();
39     applyAllSettings();
40 }
41 
~GtkConfig()42 GtkConfig::~GtkConfig()
43 {
44     QDBusConnection dbus = QDBusConnection::sessionBus();
45     dbus.unregisterService(QStringLiteral("org.kde.GtkConfig"));
46     dbus.unregisterObject(QStringLiteral("/GtkConfig"));
47 }
48 
setGtkTheme(const QString & themeName) const49 void GtkConfig::setGtkTheme(const QString &themeName) const
50 {
51     ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-theme-name"), themeName);
52     ConfigEditor::setGtkConfigValueGSettings(QStringLiteral("gtk-theme"), themeName);
53     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-theme-name"), themeName);
54     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-theme-name"), themeName);
55     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Net/ThemeName"), themeName);
56 
57     // Window decorations are part of the theme, in case of Breeze we inject custom ones from KWin
58     setWindowDecorationsAppearance();
59 }
60 
gtkTheme() const61 QString GtkConfig::gtkTheme() const
62 {
63     return ConfigEditor::gtk3ConfigValueSettingsIni(QStringLiteral("gtk-theme-name"));
64 }
65 
showGtkThemePreview(const QString & themeName) const66 void GtkConfig::showGtkThemePreview(const QString &themeName) const
67 {
68     themePreviewer->showGtk3App(themeName);
69 }
70 
setFont() const71 void GtkConfig::setFont() const
72 {
73     const QString configFontName = configValueProvider->fontName();
74     ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-font-name"), configFontName);
75     ConfigEditor::setGtkConfigValueGSettings(QStringLiteral("font-name"), configFontName);
76     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-font-name"), configFontName);
77     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-font-name"), configFontName);
78     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/FontName"), configFontName);
79 }
80 
setIconTheme() const81 void GtkConfig::setIconTheme() const
82 {
83     const QString iconThemeName = configValueProvider->iconThemeName();
84     ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-icon-theme-name"), iconThemeName);
85     ConfigEditor::setGtkConfigValueGSettings(QStringLiteral("icon-theme"), iconThemeName);
86     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-icon-theme-name"), iconThemeName);
87     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-icon-theme-name"), iconThemeName);
88     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Net/IconThemeName"), iconThemeName);
89 }
90 
setCursorTheme() const91 void GtkConfig::setCursorTheme() const
92 {
93     const QString cursorThemeName = configValueProvider->cursorThemeName();
94     ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-cursor-theme-name"), cursorThemeName);
95     ConfigEditor::setGtkConfigValueGSettings(QStringLiteral("cursor-theme"), cursorThemeName);
96     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-cursor-theme-name"), cursorThemeName);
97     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-cursor-theme-name"), cursorThemeName);
98     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/CursorThemeName"), cursorThemeName);
99 }
100 
setCursorSize() const101 void GtkConfig::setCursorSize() const
102 {
103     const int cursorSize = configValueProvider->cursorSize();
104     ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-cursor-theme-size"), cursorSize);
105     ConfigEditor::setGtkConfigValueGSettings(QStringLiteral("cursor-size"), cursorSize);
106     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-cursor-theme-size"), cursorSize);
107     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-cursor-theme-size"), cursorSize);
108     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/CursorThemeSize"), cursorSize);
109 }
110 
setIconsOnButtons() const111 void GtkConfig::setIconsOnButtons() const
112 {
113     const bool iconsOnButtonsConfigValue = configValueProvider->iconsOnButtons();
114     ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-button-images"), iconsOnButtonsConfigValue);
115     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-button-images"), iconsOnButtonsConfigValue);
116     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-button-images"), iconsOnButtonsConfigValue);
117     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/ButtonImages"), iconsOnButtonsConfigValue);
118 }
119 
setIconsInMenus() const120 void GtkConfig::setIconsInMenus() const
121 {
122     const bool iconsInMenusConfigValue = configValueProvider->iconsInMenus();
123     ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-menu-images"), iconsInMenusConfigValue);
124     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-menu-images"), iconsInMenusConfigValue);
125     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-menu-images"), iconsInMenusConfigValue);
126     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/MenuImages"), iconsInMenusConfigValue);
127 }
128 
setToolbarStyle() const129 void GtkConfig::setToolbarStyle() const
130 {
131     const int toolbarStyle = configValueProvider->toolbarStyle();
132     ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-toolbar-style"), toolbarStyle);
133     ConfigEditor::setGtkConfigValueGSettingsAsEnum(QStringLiteral("toolbar-style"), toolbarStyle);
134     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-toolbar-style"), toolbarStyle);
135     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-toolbar-style"), toolbarStyle);
136     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/ToolbarStyle"), toolbarStyle);
137 }
138 
setScrollbarBehavior() const139 void GtkConfig::setScrollbarBehavior() const
140 {
141     const bool scrollbarBehavior = configValueProvider->scrollbarBehavior();
142     ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-primary-button-warps-slider"), scrollbarBehavior);
143     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-primary-button-warps-slider"), scrollbarBehavior);
144     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-primary-button-warps-slider"), scrollbarBehavior);
145     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/PrimaryButtonWarpsSlider"), scrollbarBehavior);
146 }
147 
setDarkThemePreference() const148 void GtkConfig::setDarkThemePreference() const
149 {
150     const bool preferDarkTheme = configValueProvider->preferDarkTheme();
151     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-application-prefer-dark-theme"), preferDarkTheme);
152     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-application-prefer-dark-theme"), preferDarkTheme);
153 }
154 
setWindowDecorationsAppearance() const155 void GtkConfig::setWindowDecorationsAppearance() const
156 {
157     if (gtkTheme() == QStringLiteral("Breeze")) { // Only Breeze GTK supports custom decoration buttons
158         const auto windowDecorationsButtonsImages = configValueProvider->windowDecorationsButtonsImages();
159         ConfigEditor::setCustomClientSideDecorations(windowDecorationsButtonsImages);
160     } else {
161         ConfigEditor::disableCustomClientSideDecorations();
162     }
163 }
164 
setWindowDecorationsButtonsOrder() const165 void GtkConfig::setWindowDecorationsButtonsOrder() const
166 {
167     const QString windowDecorationsButtonOrder = configValueProvider->windowDecorationsButtonsOrder();
168     ConfigEditor::setGtkConfigValueGSettings(QStringLiteral("button-layout"), windowDecorationsButtonOrder, QStringLiteral("org.gnome.desktop.wm.preferences"));
169     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-decoration-layout"), windowDecorationsButtonOrder);
170     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-decoration-layout"), windowDecorationsButtonOrder);
171     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/DecorationLayout"), windowDecorationsButtonOrder);
172 }
173 
setEnableAnimations() const174 void GtkConfig::setEnableAnimations() const
175 {
176     const bool enableAnimations = configValueProvider->enableAnimations();
177     ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-enable-animations"), enableAnimations);
178     ConfigEditor::setGtkConfigValueGSettings(QStringLiteral("enable-animations"), enableAnimations);
179     ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-enable-animations"), enableAnimations);
180     ConfigEditor::setGtk4ConfigValueSettingsIni(QStringLiteral("gtk-enable-animations"), enableAnimations);
181     ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/EnableAnimations"), enableAnimations);
182 }
183 
setColors() const184 void GtkConfig::setColors() const
185 {
186     const QMap<QString, QColor> colors = configValueProvider->colors();
187     ConfigEditor::setGtk3Colors(colors);
188 }
189 
applyAllSettings() const190 void GtkConfig::applyAllSettings() const
191 {
192     setFont();
193     setIconTheme();
194     setCursorTheme();
195     setCursorSize();
196     setIconsOnButtons();
197     setIconsInMenus();
198     setToolbarStyle();
199     setScrollbarBehavior();
200     setDarkThemePreference();
201     setWindowDecorationsAppearance();
202     setWindowDecorationsButtonsOrder();
203     setEnableAnimations();
204     setColors();
205 }
206 
onKdeglobalsSettingsChange(const KConfigGroup & group,const QByteArrayList & names) const207 void GtkConfig::onKdeglobalsSettingsChange(const KConfigGroup &group, const QByteArrayList &names) const
208 {
209     if (group.name() == QStringLiteral("KDE")) {
210         if (names.contains(QByteArrayLiteral("AnimationDurationFactor"))) {
211             setEnableAnimations();
212         }
213         if (names.contains(QByteArrayLiteral("ShowIconsInMenuItems"))) {
214             setIconsInMenus();
215         }
216         if (names.contains(QByteArrayLiteral("ShowIconsOnPushButtons"))) {
217             setIconsOnButtons();
218         }
219         if (names.contains(QByteArrayLiteral("ScrollbarLeftClickNavigatesByPage"))) {
220             setScrollbarBehavior();
221         }
222     } else if (group.name() == QStringLiteral("Icons")) {
223         if (names.contains(QByteArrayLiteral("Theme"))) {
224             setIconTheme();
225         }
226     } else if (group.name() == QStringLiteral("General")) {
227         if (names.contains(QByteArrayLiteral("font"))) {
228             setFont();
229         }
230         if (names.contains(QByteArrayLiteral("ColorScheme")) || names.contains(QByteArrayLiteral("AccentColor"))) {
231             setColors();
232             setDarkThemePreference();
233             setWindowDecorationsAppearance(); // Decorations' color can depend on the current color scheme
234         }
235     } else if (group.name() == QStringLiteral("Toolbar style")) {
236         if (names.contains(QByteArrayLiteral("ToolButtonStyle"))) {
237             setToolbarStyle();
238         }
239     }
240 }
241 
onKWinSettingsChange(const KConfigGroup & group,const QByteArrayList & names) const242 void GtkConfig::onKWinSettingsChange(const KConfigGroup &group, const QByteArrayList &names) const
243 {
244     if (group.name() == QStringLiteral("org.kde.kdecoration2")) {
245         if (names.contains(QByteArrayLiteral("ButtonsOnRight")) //
246             || names.contains(QByteArrayLiteral("ButtonsOnLeft"))) {
247             setWindowDecorationsButtonsOrder();
248         }
249         if (names.contains(QByteArrayLiteral("theme"))) {
250             setWindowDecorationsAppearance();
251         }
252     }
253 }
254 
onKCMInputSettingsChange(const KConfigGroup & group,const QByteArrayList & names) const255 void GtkConfig::onKCMInputSettingsChange(const KConfigGroup &group, const QByteArrayList &names) const
256 {
257     if (group.name() == QStringLiteral("Mouse")) {
258         if (names.contains("cursorTheme")) {
259             setCursorTheme();
260         }
261         if (names.contains("cursorSize")) {
262             setCursorSize();
263         }
264     }
265 }
266 
onBreezeSettingsChange(const KConfigGroup & group,const QByteArrayList & names) const267 void GtkConfig::onBreezeSettingsChange(const KConfigGroup &group, const QByteArrayList &names) const
268 {
269     if (group.name() == QStringLiteral("Common") //
270         && names.contains("OutlineCloseButton")) {
271         setWindowDecorationsAppearance();
272     }
273 }
274 
275 #include "gtkconfig.moc"
276