505a85b52ed2459677c3f6e75d36cc7d6b10d714
[platform/upstream/glib.git] / gio / gdelayedsettingsbackend.c
1 /*
2  * Copyright © 2009, 2010 Codethink Limited
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Author: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "config.h"
23
24 #include "gdelayedsettingsbackend.h"
25 #include "gsettingsbackendinternal.h"
26
27 #include <string.h>
28
29 #include "gioalias.h"
30
31 struct _GDelayedSettingsBackendPrivate
32 {
33   GSettingsBackend *backend;
34   GTree *delayed;
35   gpointer owner;
36 };
37
38 G_DEFINE_TYPE (GDelayedSettingsBackend,
39                g_delayed_settings_backend,
40                G_TYPE_SETTINGS_BACKEND)
41
42 static GVariant *
43 g_delayed_settings_backend_read (GSettingsBackend   *backend,
44                                  const gchar        *key,
45                                  const GVariantType *expected_type)
46 {
47   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
48   GVariant *result;
49
50   if ((result = g_tree_lookup (delayed->priv->delayed, key)))
51     return g_variant_ref (result);
52
53   return g_settings_backend_read (delayed->priv->backend,
54                                   key, expected_type);
55 }
56 static gboolean
57 g_delayed_settings_backend_write (GSettingsBackend *backend,
58                                   const gchar      *key,
59                                   GVariant         *value,
60                                   gpointer          origin_tag)
61 {
62   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
63   gboolean was_empty;
64
65   was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
66   g_tree_insert (delayed->priv->delayed, g_strdup (key),
67                  g_variant_ref_sink (value));
68   g_settings_backend_changed (backend, key, origin_tag);
69
70   if (was_empty && delayed->priv->owner)
71     g_object_notify (delayed->priv->owner, "has-unapplied");
72
73   return TRUE;
74 }
75
76 static gboolean
77 add_to_tree (gpointer key,
78              gpointer value,
79              gpointer user_data)
80 {
81   g_tree_insert (user_data, g_strdup (key), g_variant_ref (value));
82   return FALSE;
83 }
84
85 static gboolean
86 g_delayed_settings_backend_write_keys (GSettingsBackend *backend,
87                                        GTree            *tree,
88                                        gpointer          origin_tag)
89 {
90   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
91   gboolean was_empty;
92
93   was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
94
95   g_tree_foreach (tree, add_to_tree, delayed->priv->delayed);
96
97   g_settings_backend_changed_tree (backend, tree, origin_tag);
98
99   if (was_empty && delayed->priv->owner)
100     g_object_notify (delayed->priv->owner, "has-unapplied");
101
102   return TRUE;
103 }
104
105 static gboolean
106 g_delayed_settings_backend_get_writable (GSettingsBackend *backend,
107                                          const gchar      *name)
108 {
109   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
110
111   return g_settings_backend_get_writable (delayed->priv->backend, name);
112 }
113
114 static void
115 g_delayed_settings_backend_reset (GSettingsBackend *backend,
116                                   const gchar      *key,
117                                   gpointer          origin_tag)
118 {
119   /* deal with this... */
120 }
121
122 static void
123 g_delayed_settings_backend_reset_path (GSettingsBackend *backend,
124                                        const gchar      *path,
125                                        gpointer          origin_tag)
126 {
127   /* deal with this... */
128 }
129
130 static void
131 g_delayed_settings_backend_subscribe (GSettingsBackend *backend,
132                                       const char       *name)
133 {
134   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
135
136   g_settings_backend_subscribe (delayed->priv->backend, name);
137 }
138
139 static void
140 g_delayed_settings_backend_unsubscribe (GSettingsBackend *backend,
141                                         const char       *name)
142 {
143   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
144
145   g_settings_backend_unsubscribe (delayed->priv->backend, name);
146 }
147
148
149 /* method calls */
150 gboolean
151 g_delayed_settings_backend_get_has_unapplied (GDelayedSettingsBackend *delayed)
152 {
153   return g_tree_nnodes (delayed->priv->delayed) > 0;
154 }
155
156 void
157 g_delayed_settings_backend_apply (GDelayedSettingsBackend *delayed)
158 {
159   if (g_tree_nnodes (delayed->priv->delayed) > 0)
160     {
161       GTree *tmp;
162
163       tmp = delayed->priv->delayed;
164       delayed->priv->delayed = g_settings_backend_create_tree ();
165       g_settings_backend_write_keys (delayed->priv->backend,
166                                      tmp, delayed->priv);
167       g_tree_unref (tmp);
168
169       if (delayed->priv->owner)
170         g_object_notify (delayed->priv->owner, "has-unapplied");
171     }
172 }
173
174 void
175 g_delayed_settings_backend_revert (GDelayedSettingsBackend *delayed)
176 {
177   if (g_tree_nnodes (delayed->priv->delayed) > 0)
178     {
179       GTree *tmp;
180
181       tmp = delayed->priv->delayed;
182       delayed->priv->delayed = g_settings_backend_create_tree ();
183       g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed), tmp, NULL);
184       g_tree_unref (tmp);
185
186       if (delayed->priv->owner)
187         g_object_notify (delayed->priv->owner, "has-unapplied");
188     }
189 }
190
191 /* change notification */
192 static void
193 delayed_backend_changed (GSettingsBackend *backend,
194                          const gchar      *key,
195                          gpointer          origin_tag,
196                          gpointer          user_data)
197 {
198   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
199
200   if (origin_tag != delayed->priv)
201     g_settings_backend_changed (G_SETTINGS_BACKEND (delayed),
202                                 key, origin_tag);
203 }
204
205 static void
206 delayed_backend_keys_changed (GSettingsBackend    *backend,
207                               const gchar         *path,
208                               const gchar * const *items,
209                               gpointer             origin_tag,
210                               gpointer             user_data)
211 {
212   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
213
214   if (origin_tag != delayed->priv)
215     g_settings_backend_keys_changed (G_SETTINGS_BACKEND (delayed),
216                                      path, items, origin_tag);
217 }
218
219 static void
220 delayed_backend_path_changed (GSettingsBackend    *backend,
221                               const gchar         *path,
222                               gpointer             origin_tag,
223                               gpointer             user_data)
224 {
225   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
226
227   if (origin_tag != delayed->priv)
228     g_settings_backend_path_changed (G_SETTINGS_BACKEND (delayed),
229                                      path, origin_tag);
230 }
231
232 static void
233 delayed_backend_writable_changed (GSettingsBackend *backend,
234                                   const gchar      *key,
235                                   gpointer          user_data)
236 {
237   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
238
239   /* XXX: maybe drop keys from the delayed-apply settings
240    *      if they became non-writable?
241    */
242   g_settings_backend_writable_changed (G_SETTINGS_BACKEND (delayed), key);
243 }
244
245 static void
246 delayed_backend_path_writable_changed (GSettingsBackend *backend,
247                                        const gchar      *path,
248                                        gpointer          user_data)
249 {
250   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
251
252   /* XXX: maybe drop keys from the delayed-apply settings
253    *      if they became non-writable?
254    */
255   g_settings_backend_path_writable_changed (G_SETTINGS_BACKEND (delayed),
256                                             path);
257 }
258
259 static void
260 g_delayed_settings_backend_finalize (GObject *object)
261 {
262   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object);
263
264   g_settings_backend_unwatch (delayed->priv->backend, delayed);
265   g_object_unref (delayed->priv->backend);
266 }
267
268 static void
269 g_delayed_settings_backend_class_init (GDelayedSettingsBackendClass *class)
270 {
271   GSettingsBackendClass *backend_class = G_SETTINGS_BACKEND_CLASS (class);
272   GObjectClass *object_class = G_OBJECT_CLASS (class);
273
274   g_type_class_add_private (class, sizeof (GDelayedSettingsBackendPrivate));
275
276   backend_class->read = g_delayed_settings_backend_read;
277   backend_class->write = g_delayed_settings_backend_write;
278   backend_class->write_keys = g_delayed_settings_backend_write_keys;
279   backend_class->reset = g_delayed_settings_backend_reset;
280   backend_class->reset_path = g_delayed_settings_backend_reset_path;
281   backend_class->get_writable = g_delayed_settings_backend_get_writable;
282   backend_class->subscribe = g_delayed_settings_backend_subscribe;
283   backend_class->unsubscribe = g_delayed_settings_backend_unsubscribe;
284
285   object_class->finalize = g_delayed_settings_backend_finalize;
286 }
287
288 static void
289 g_delayed_settings_backend_init (GDelayedSettingsBackend *delayed)
290 {
291   delayed->priv =
292     G_TYPE_INSTANCE_GET_PRIVATE (delayed, G_TYPE_DELAYED_SETTINGS_BACKEND,
293                                  GDelayedSettingsBackendPrivate);
294
295   delayed->priv->delayed = g_settings_backend_create_tree ();
296 }
297
298 GDelayedSettingsBackend *
299 g_delayed_settings_backend_new (GSettingsBackend *backend,
300                                 gpointer          owner)
301 {
302   GDelayedSettingsBackend *delayed;
303
304   delayed = g_object_new (G_TYPE_DELAYED_SETTINGS_BACKEND, NULL);
305   delayed->priv->backend = g_object_ref (backend);
306   delayed->priv->owner = owner;
307
308   g_settings_backend_watch (delayed->priv->backend,
309                             delayed_backend_changed,
310                             delayed_backend_path_changed,
311                             delayed_backend_keys_changed,
312                             delayed_backend_writable_changed,
313                             delayed_backend_path_writable_changed,
314                             delayed);
315
316   return delayed;
317 }