1 /*
2 * SPDX-FileCopyrightText: 2018-2019 Red Hat Inc
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 *
6 * SPDX-FileCopyrightText: 2018-2019 Jan Grulich <jgrulich@redhat.com>
7 */
8
9 #include "settings.h"
10
11 #include <QDBusConnection>
12 #include <QDBusContext>
13 #include <QDBusMessage>
14 #include <QDBusMetaType>
15
16 #include <QLoggingCategory>
17
18 #include <KConfigCore/KConfigGroup>
19
20 Q_LOGGING_CATEGORY(XdgDesktopPortalKdeSettings, "xdp-kde-settings")
21
22 Q_DECLARE_METATYPE(SettingsPortal::VariantMapMap)
23
24 QDBusArgument &operator<<(QDBusArgument &argument, const SettingsPortal::VariantMapMap &mymap)
25 {
26 argument.beginMap(QVariant::String, QVariant::Map);
27
28 QMapIterator<QString, QVariantMap> i(mymap);
29 while (i.hasNext()) {
30 i.next();
31 argument.beginMapEntry();
32 argument << i.key() << i.value();
33 argument.endMapEntry();
34 }
35 argument.endMap();
36 return argument;
37 }
38
operator >>(const QDBusArgument & argument,SettingsPortal::VariantMapMap & mymap)39 const QDBusArgument &operator>>(const QDBusArgument &argument, SettingsPortal::VariantMapMap &mymap)
40 {
41 argument.beginMap();
42 mymap.clear();
43
44 while (!argument.atEnd()) {
45 QString key;
46 QVariantMap value;
47 argument.beginMapEntry();
48 argument >> key >> value;
49 argument.endMapEntry();
50 mymap.insert(key, value);
51 }
52
53 argument.endMap();
54 return argument;
55 }
56
groupMatches(const QString & group,const QStringList & patterns)57 static bool groupMatches(const QString &group, const QStringList &patterns)
58 {
59 for (const QString &pattern : patterns) {
60 if (pattern.isEmpty()) {
61 return true;
62 }
63
64 if (pattern == group) {
65 return true;
66 }
67
68 if (pattern.endsWith(QLatin1Char('*')) && group.startsWith(pattern.left(pattern.length() - 1))) {
69 return true;
70 }
71 }
72
73 return false;
74 }
75
SettingsPortal(QObject * parent)76 SettingsPortal::SettingsPortal(QObject *parent)
77 : QDBusAbstractAdaptor(parent)
78 {
79 qDBusRegisterMetaType<VariantMapMap>();
80
81 m_kdeglobals = KSharedConfig::openConfig();
82
83 QDBusConnection::sessionBus().connect(QString(),
84 QStringLiteral("/KDEPlatformTheme"),
85 QStringLiteral("org.kde.KDEPlatformTheme"),
86 QStringLiteral("refreshFonts"),
87 this,
88 SLOT(fontChanged()));
89 QDBusConnection::sessionBus().connect(QString(),
90 QStringLiteral("/KGlobalSettings"),
91 QStringLiteral("org.kde.KGlobalSettings"),
92 QStringLiteral("notifyChange"),
93 this,
94 SLOT(globalSettingChanged(int, int)));
95 QDBusConnection::sessionBus()
96 .connect(QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), this, SLOT(toolbarStyleChanged()));
97 }
98
~SettingsPortal()99 SettingsPortal::~SettingsPortal()
100 {
101 }
102
ReadAll(const QStringList & groups)103 void SettingsPortal::ReadAll(const QStringList &groups)
104 {
105 qCDebug(XdgDesktopPortalKdeSettings) << "ReadAll called with parameters:";
106 qCDebug(XdgDesktopPortalKdeSettings) << " groups: " << groups;
107
108 // FIXME this is super ugly, but I was unable to make it properly return VariantMapMap
109 QObject *obj = QObject::parent();
110
111 if (!obj) {
112 qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
113 return;
114 }
115
116 void *ptr = obj->qt_metacast("QDBusContext");
117 QDBusContext *q_ptr = reinterpret_cast<QDBusContext *>(ptr);
118
119 if (!q_ptr) {
120 qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
121 return;
122 }
123
124 VariantMapMap result;
125 const auto groupList = m_kdeglobals->groupList();
126 for (const QString &settingGroupName : groupList) {
127 // NOTE: use org.kde.kdeglobals prefix
128
129 QString uniqueGroupName = QStringLiteral("org.kde.kdeglobals.") + settingGroupName;
130
131 if (!groupMatches(uniqueGroupName, groups)) {
132 continue;
133 }
134
135 QVariantMap map;
136 KConfigGroup configGroup(m_kdeglobals, settingGroupName);
137
138 const auto keyList = configGroup.keyList();
139 for (const QString &key : keyList) {
140 map.insert(key, configGroup.readEntry(key));
141 }
142
143 result.insert(uniqueGroupName, map);
144 }
145
146 QDBusMessage message = q_ptr->message();
147 QDBusMessage reply = message.createReply(QVariant::fromValue(result));
148 QDBusConnection::sessionBus().send(reply);
149 }
150
Read(const QString & group,const QString & key)151 void SettingsPortal::Read(const QString &group, const QString &key)
152 {
153 qCDebug(XdgDesktopPortalKdeSettings) << "Read called with parameters:";
154 qCDebug(XdgDesktopPortalKdeSettings) << " group: " << group;
155 qCDebug(XdgDesktopPortalKdeSettings) << " key: " << key;
156
157 // FIXME this is super ugly, but I was unable to make it properly return VariantMapMap
158 QObject *obj = QObject::parent();
159
160 if (!obj) {
161 qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
162 return;
163 }
164
165 void *ptr = obj->qt_metacast("QDBusContext");
166 QDBusContext *q_ptr = reinterpret_cast<QDBusContext *>(ptr);
167
168 if (!q_ptr) {
169 qCWarning(XdgDesktopPortalKdeSettings) << "Failed to get dbus context";
170 return;
171 }
172
173 QDBusMessage reply;
174 QDBusMessage message = q_ptr->message();
175
176 // All our namespaces start with this prefix
177 if (!group.startsWith(QStringLiteral("org.kde.kdeglobals"))) {
178 qCWarning(XdgDesktopPortalKdeSettings) << "Namespace " << group << " is not supported";
179 reply = message.createErrorReply(QDBusError::UnknownProperty, QStringLiteral("Namespace is not supported"));
180 QDBusConnection::sessionBus().send(reply);
181 return;
182 }
183
184 QDBusVariant result = readProperty(group, key);
185 if (result.variant().isNull()) {
186 reply = message.createErrorReply(QDBusError::UnknownProperty, QStringLiteral("Property doesn't exist"));
187 } else {
188 reply = message.createReply(QVariant::fromValue(result));
189 }
190
191 QDBusConnection::sessionBus().send(reply);
192 }
193
fontChanged()194 void SettingsPortal::fontChanged()
195 {
196 Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.General"),
197 QStringLiteral("font"),
198 readProperty(QStringLiteral("org.kde.kdeglobals.General"), QStringLiteral("font")));
199 }
200
globalSettingChanged(int type,int arg)201 void SettingsPortal::globalSettingChanged(int type, int arg)
202 {
203 m_kdeglobals->reparseConfiguration();
204
205 // Mostly based on plasma-integration needs
206 switch (type) {
207 case PaletteChanged:
208 // Plasma-integration will be loading whole palette again, there is no reason to try to identify
209 // particular categories or colors
210 Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.General"),
211 QStringLiteral("ColorScheme"),
212 readProperty(QStringLiteral("org.kde.kdeglobals.General"), QStringLiteral("ColorScheme")));
213 break;
214 case FontChanged:
215 fontChanged();
216 break;
217 case StyleChanged:
218 Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.KDE"),
219 QStringLiteral("widgetStyle"),
220 readProperty(QStringLiteral("org.kde.kdeglobals.KDE"), QStringLiteral("widgetStyle")));
221 break;
222 case SettingsChanged: {
223 SettingsCategory category = static_cast<SettingsCategory>(arg);
224 if (category == SETTINGS_QT || category == SETTINGS_MOUSE) {
225 // TODO
226 } else if (category == SETTINGS_STYLE) {
227 // TODO
228 }
229 break;
230 }
231 case IconChanged:
232 // we will get notified about each category, but it probably makes sense to send this signal just once
233 if (arg == 0) { // KIconLoader::Desktop
234 Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.Icons"),
235 QStringLiteral("Theme"),
236 readProperty(QStringLiteral("org.kde.kdeglobals.Icons"), QStringLiteral("Theme")));
237 }
238 break;
239 case CursorChanged:
240 // TODO
241 break;
242 case ToolbarStyleChanged:
243 toolbarStyleChanged();
244 break;
245 default:
246 break;
247 }
248 }
249
toolbarStyleChanged()250 void SettingsPortal::toolbarStyleChanged()
251 {
252 Q_EMIT SettingChanged(QStringLiteral("org.kde.kdeglobals.Toolbar style"),
253 QStringLiteral("ToolButtonStyle"),
254 readProperty(QStringLiteral("org.kde.kdeglobals.Toolbar style"), QStringLiteral("ToolButtonStyle")));
255 }
256
readProperty(const QString & group,const QString & key)257 QDBusVariant SettingsPortal::readProperty(const QString &group, const QString &key)
258 {
259 QString groupName = group.right(group.length() - QStringLiteral("org.kde.kdeglobals.").length());
260
261 if (!m_kdeglobals->hasGroup(groupName)) {
262 qCWarning(XdgDesktopPortalKdeSettings) << "Group " << group << " doesn't exist";
263 return QDBusVariant();
264 }
265
266 KConfigGroup configGroup(m_kdeglobals, groupName);
267
268 if (!configGroup.hasKey(key)) {
269 qCWarning(XdgDesktopPortalKdeSettings) << "Key " << key << " doesn't exist";
270 return QDBusVariant();
271 }
272
273 return QDBusVariant(configGroup.readEntry(key));
274 }
275