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