1 /*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17 */
18
19 #include "qgsettings.h"
20
21 #include "qconftypes.h"
22 #include "util.h"
23 #include <gio/gio.h>
24
25 struct QGSettingsPrivate
26 {
27 QByteArray schema_id;
28 QByteArray path;
29 GSettings *settings;
30 GSettingsSchema *schema;
31 gulong signal_handler_id;
32
33 static void settingChanged(GSettings *settings, const gchar *key, gpointer user_data);
34 };
35
settingChanged(GSettings *,const gchar * key,gpointer user_data)36 void QGSettingsPrivate::settingChanged(GSettings *, const gchar *key, gpointer user_data)
37 {
38 QGSettings *self = (QGSettings *)user_data;
39
40 // work around https://bugreports.qt.io/browse/QTBUG-32859 and http://pad.lv/1460970
41 QMetaObject::invokeMethod(self, "changed", Qt::QueuedConnection, Q_ARG(QString, qtify_name(key)));
42 }
43
QGSettings(const QByteArray & schema_id,const QByteArray & path,QObject * parent)44 QGSettings::QGSettings(const QByteArray &schema_id, const QByteArray &path, QObject *parent):
45 QObject(parent)
46 {
47 priv = new QGSettingsPrivate;
48 priv->schema_id = schema_id;
49 priv->path = path;
50
51 if (priv->path.isEmpty())
52 priv->settings = g_settings_new(priv->schema_id.constData());
53 else
54 priv->settings = g_settings_new_with_path(priv->schema_id.constData(), priv->path.constData());
55
56 g_object_get (priv->settings, "settings-schema", &priv->schema, NULL);
57 priv->signal_handler_id = g_signal_connect(priv->settings, "changed", G_CALLBACK(QGSettingsPrivate::settingChanged), this);
58 }
59
~QGSettings()60 QGSettings::~QGSettings()
61 {
62 if (priv->schema) {
63 g_settings_sync ();
64 g_signal_handler_disconnect(priv->settings, priv->signal_handler_id);
65 g_object_unref (priv->settings);
66 g_settings_schema_unref (priv->schema);
67 }
68 delete priv;
69 }
70
get(const QString & key) const71 QVariant QGSettings::get(const QString &key) const
72 {
73 gchar *gkey = unqtify_name(key);
74 GVariant *value = g_settings_get_value(priv->settings, gkey);
75 QVariant qvalue = qconf_types_to_qvariant(value);
76 g_variant_unref(value);
77 g_free(gkey);
78 return qvalue;
79 }
80
set(const QString & key,const QVariant & value)81 void QGSettings::set(const QString &key, const QVariant &value)
82 {
83 if (!this->trySet(key, value))
84 qWarning("unable to set key '%s' to value '%s'", key.toUtf8().constData(), value.toString().toUtf8().constData());
85 }
86
trySet(const QString & key,const QVariant & value)87 bool QGSettings::trySet(const QString &key, const QVariant &value)
88 {
89 gchar *gkey = unqtify_name(key);
90 bool success = false;
91
92 /* fetch current value to find out the exact type */
93 GVariant *cur = g_settings_get_value(priv->settings, gkey);
94
95 GVariant *new_value = qconf_types_collect_from_variant(g_variant_get_type (cur), value);
96 if (new_value)
97 success = g_settings_set_value(priv->settings, gkey, new_value);
98
99 g_free(gkey);
100 g_variant_unref (cur);
101
102 return success;
103 }
104
keys() const105 QStringList QGSettings::keys() const
106 {
107 QStringList list;
108 gchar **keys = g_settings_list_keys(priv->settings);
109 for (int i = 0; keys[i]; i++)
110 list.append(qtify_name(keys[i]));
111
112 g_strfreev(keys);
113 return list;
114 }
115
choices(const QString & qkey) const116 QVariantList QGSettings::choices(const QString &qkey) const
117 {
118 gchar *key = unqtify_name (qkey);
119 GSettingsSchemaKey *schema_key = g_settings_schema_get_key (priv->schema, key);
120 GVariant *range = g_settings_schema_key_get_range(schema_key);
121 g_settings_schema_key_unref (schema_key);
122 g_free(key);
123
124 if (range == nullptr)
125 return QVariantList();
126
127 const gchar *type;
128 GVariant *value;
129 g_variant_get(range, "(&sv)", &type, &value);
130
131 QVariantList choices;
132 if (g_str_equal(type, "enum")) {
133 GVariantIter iter;
134 GVariant *child;
135
136 g_variant_iter_init (&iter, value);
137 while ((child = g_variant_iter_next_value(&iter))) {
138 choices.append(qconf_types_to_qvariant(child));
139 g_variant_unref(child);
140 }
141 }
142
143 g_variant_unref (value);
144 g_variant_unref (range);
145
146 return choices;
147 }
148
reset(const QString & qkey)149 void QGSettings::reset(const QString &qkey)
150 {
151 gchar *key = unqtify_name(qkey);
152 g_settings_reset(priv->settings, key);
153 g_free(key);
154 }
155
isSchemaInstalled(const QByteArray & schema_id)156 bool QGSettings::isSchemaInstalled(const QByteArray &schema_id)
157 {
158 GSettingsSchemaSource *source = g_settings_schema_source_get_default ();
159 GSettingsSchema *schema = g_settings_schema_source_lookup (source, schema_id.constData(), TRUE);
160 if (schema) {
161 g_settings_schema_unref (schema);
162 return true;
163 } else {
164 return false;
165 }
166 }
167