fd166a1ccf1a41a103f274ae340249f46740c5a2
[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
30 struct _GDelayedSettingsBackendPrivate
31 {
32   GSettingsBackend *backend;
33   GStaticMutex lock;
34   GTree *delayed;
35
36   GMainContext *owner_context;
37   gpointer owner;
38 };
39
40 G_DEFINE_TYPE (GDelayedSettingsBackend,
41                g_delayed_settings_backend,
42                G_TYPE_SETTINGS_BACKEND)
43
44 static gboolean
45 invoke_notify_unapplied (gpointer data)
46 {
47   g_object_notify (data, "has-unapplied");
48   g_object_unref (data);
49
50   return FALSE;
51 }
52
53 static void
54 g_delayed_settings_backend_notify_unapplied (GDelayedSettingsBackend *delayed)
55 {
56   GMainContext *target_context;
57   GObject *target;
58
59   g_static_mutex_lock (&delayed->priv->lock);
60   if (delayed->priv->owner)
61     {
62       target_context = delayed->priv->owner_context;
63       target = g_object_ref (delayed->priv->owner);
64     }
65   else
66     {
67       target_context = NULL;
68       target = NULL;
69     }
70   g_static_mutex_unlock (&delayed->priv->lock);
71
72   if (target != NULL)
73     {
74       if (g_settings_backend_get_active_context () != target_context)
75         {
76           GSource *source;
77
78           source = g_idle_source_new ();
79           g_source_set_priority (source, G_PRIORITY_DEFAULT);
80           g_source_set_callback (source, invoke_notify_unapplied,
81                                  target, g_object_unref);
82           g_source_attach (source, target_context);
83           g_source_unref (source);
84         }
85       else
86         invoke_notify_unapplied (target);
87     }
88 }
89
90
91 static GVariant *
92 g_delayed_settings_backend_read (GSettingsBackend   *backend,
93                                  const gchar        *key,
94                                  const GVariantType *expected_type,
95                                  gboolean            default_value)
96 {
97   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
98   gpointer result = NULL;
99
100   if (!default_value)
101     {
102       g_static_mutex_lock (&delayed->priv->lock);
103       if (g_tree_lookup_extended (delayed->priv->delayed, key, NULL, &result))
104         {
105           /* NULL in the tree means we should consult the default value */
106           if (result != NULL)
107             g_variant_ref (result);
108           else
109             default_value = TRUE;
110         }
111       g_static_mutex_unlock (&delayed->priv->lock);
112     }
113
114   if (result == NULL)
115     result = g_settings_backend_read (delayed->priv->backend, key,
116                                       expected_type, default_value);
117
118   return result;
119 }
120
121 static gboolean
122 g_delayed_settings_backend_write (GSettingsBackend *backend,
123                                   const gchar      *key,
124                                   GVariant         *value,
125                                   gpointer          origin_tag)
126 {
127   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
128   gboolean was_empty;
129
130   g_static_mutex_lock (&delayed->priv->lock);
131   was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
132   g_tree_insert (delayed->priv->delayed, g_strdup (key),
133                  g_variant_ref_sink (value));
134   g_static_mutex_unlock (&delayed->priv->lock);
135
136   g_settings_backend_changed (backend, key, origin_tag);
137
138   if (was_empty)
139     g_delayed_settings_backend_notify_unapplied (delayed);
140
141   return TRUE;
142 }
143
144 static gboolean
145 add_to_tree (gpointer key,
146              gpointer value,
147              gpointer user_data)
148 {
149   g_tree_insert (user_data, g_strdup (key), g_variant_ref (value));
150   return FALSE;
151 }
152
153 static gboolean
154 g_delayed_settings_backend_write_keys (GSettingsBackend *backend,
155                                        GTree            *tree,
156                                        gpointer          origin_tag)
157 {
158   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
159   gboolean was_empty;
160
161   g_static_mutex_lock (&delayed->priv->lock);
162   was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
163
164   g_tree_foreach (tree, add_to_tree, delayed->priv->delayed);
165   g_static_mutex_unlock (&delayed->priv->lock);
166
167   g_settings_backend_changed_tree (backend, tree, origin_tag);
168
169   if (was_empty)
170     g_delayed_settings_backend_notify_unapplied (delayed);
171
172   return TRUE;
173 }
174
175 static gboolean
176 g_delayed_settings_backend_get_writable (GSettingsBackend *backend,
177                                          const gchar      *name)
178 {
179   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
180
181   return g_settings_backend_get_writable (delayed->priv->backend, name);
182 }
183
184 static void
185 g_delayed_settings_backend_reset (GSettingsBackend *backend,
186                                   const gchar      *key,
187                                   gpointer          origin_tag)
188 {
189   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
190   gboolean was_empty;
191
192   g_static_mutex_lock (&delayed->priv->lock);
193   was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
194   g_tree_insert (delayed->priv->delayed, g_strdup (key), NULL);
195   g_static_mutex_unlock (&delayed->priv->lock);
196
197   if (was_empty)
198     g_delayed_settings_backend_notify_unapplied (delayed);
199 }
200
201 static void
202 g_delayed_settings_backend_reset_path (GSettingsBackend *backend,
203                                        const gchar      *path,
204                                        gpointer          origin_tag)
205 {
206   /* deal with this... */
207 }
208
209 static void
210 g_delayed_settings_backend_subscribe (GSettingsBackend *backend,
211                                       const char       *name)
212 {
213   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
214
215   g_settings_backend_subscribe (delayed->priv->backend, name);
216 }
217
218 static void
219 g_delayed_settings_backend_unsubscribe (GSettingsBackend *backend,
220                                         const char       *name)
221 {
222   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
223
224   g_settings_backend_unsubscribe (delayed->priv->backend, name);
225 }
226
227 static GPermission *
228 g_delayed_settings_backend_get_permission (GSettingsBackend *backend,
229                                            const gchar      *path)
230 {
231   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
232
233   return g_settings_backend_get_permission (delayed->priv->backend, path);
234 }
235
236
237 /* method calls */
238 gboolean
239 g_delayed_settings_backend_get_has_unapplied (GDelayedSettingsBackend *delayed)
240 {
241   /* we don't need to lock for this... */
242
243   return g_tree_nnodes (delayed->priv->delayed) > 0;
244 }
245
246 void
247 g_delayed_settings_backend_apply (GDelayedSettingsBackend *delayed)
248 {
249   if (g_tree_nnodes (delayed->priv->delayed) > 0)
250     {
251       gboolean success;
252       GTree *tmp;
253
254       g_static_mutex_lock (&delayed->priv->lock);
255       tmp = delayed->priv->delayed;
256       delayed->priv->delayed = g_settings_backend_create_tree ();
257       success = g_settings_backend_write_keys (delayed->priv->backend,
258                                                tmp, delayed->priv);
259       g_static_mutex_unlock (&delayed->priv->lock);
260
261       if (!success)
262         g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed),
263                                          tmp, NULL);
264
265       g_tree_unref (tmp);
266
267       g_delayed_settings_backend_notify_unapplied (delayed);
268     }
269 }
270
271 void
272 g_delayed_settings_backend_revert (GDelayedSettingsBackend *delayed)
273 {
274   if (g_tree_nnodes (delayed->priv->delayed) > 0)
275     {
276       GTree *tmp;
277
278       g_static_mutex_lock (&delayed->priv->lock);
279       tmp = delayed->priv->delayed;
280       delayed->priv->delayed = g_settings_backend_create_tree ();
281       g_static_mutex_unlock (&delayed->priv->lock);
282       g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed), tmp, NULL);
283       g_tree_unref (tmp);
284
285       g_delayed_settings_backend_notify_unapplied (delayed);
286     }
287 }
288
289 /* change notification */
290 static void
291 delayed_backend_changed (GSettingsBackend *backend,
292                          GObject          *target,
293                          const gchar      *key,
294                          gpointer          origin_tag)
295 {
296   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
297
298   if (origin_tag != delayed->priv)
299     g_settings_backend_changed (G_SETTINGS_BACKEND (delayed),
300                                 key, origin_tag);
301 }
302
303 static void
304 delayed_backend_keys_changed (GSettingsBackend    *backend,
305                               GObject             *target,
306                               const gchar         *path,
307                               const gchar * const *items,
308                               gpointer             origin_tag)
309 {
310   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
311
312   if (origin_tag != delayed->priv)
313     g_settings_backend_keys_changed (G_SETTINGS_BACKEND (delayed),
314                                      path, items, origin_tag);
315 }
316
317 static void
318 delayed_backend_path_changed (GSettingsBackend *backend,
319                               GObject          *target,
320                               const gchar      *path,
321                               gpointer          origin_tag)
322 {
323   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
324
325   if (origin_tag != delayed->priv)
326     g_settings_backend_path_changed (G_SETTINGS_BACKEND (delayed),
327                                      path, origin_tag);
328 }
329
330 static void
331 delayed_backend_writable_changed (GSettingsBackend *backend,
332                                   GObject          *target,
333                                   const gchar      *key)
334 {
335   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
336   gboolean last_one = FALSE;
337
338   g_static_mutex_lock (&delayed->priv->lock);
339
340   if (g_tree_lookup (delayed->priv->delayed, key) &&
341       !g_settings_backend_get_writable (delayed->priv->backend, key))
342     {
343       /* drop the key from our changeset if it just became read-only.
344        * no need to signal since the writable change below implies it.
345        */
346       g_tree_remove (delayed->priv->delayed, key);
347
348       /* if that was the only key... */
349       last_one = g_tree_nnodes (delayed->priv->delayed) == 0;
350     }
351
352   g_static_mutex_unlock (&delayed->priv->lock);
353
354   if (last_one)
355     g_delayed_settings_backend_notify_unapplied (delayed);
356
357   g_settings_backend_writable_changed (G_SETTINGS_BACKEND (delayed), key);
358 }
359
360 /* slow method until we get foreach-with-remove in GTree
361  */
362 typedef struct
363 {
364   const gchar *path;
365   const gchar **keys;
366   gsize index;
367 } CheckPrefixState;
368
369 static gboolean
370 check_prefix (gpointer key,
371               gpointer value,
372               gpointer data)
373 {
374   CheckPrefixState *state = data;
375
376   if (g_str_has_prefix (key, state->path))
377     state->keys[state->index++] = key;
378
379   return FALSE;
380 }
381
382 static void
383 delayed_backend_path_writable_changed (GSettingsBackend *backend,
384                                        GObject          *target,
385                                        const gchar      *path)
386 {
387   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
388   gboolean last_one = FALSE;
389   gsize n_keys;
390
391   g_static_mutex_lock (&delayed->priv->lock);
392
393   n_keys = g_tree_nnodes (delayed->priv->delayed);
394
395   if (n_keys > 0)
396     {
397       CheckPrefixState state = { path, g_new (const gchar *, n_keys) };
398       gsize i;
399
400       /* collect a list of possibly-affected keys (ie: matching the path) */
401       g_tree_foreach (delayed->priv->delayed, check_prefix, &state);
402
403       /* drop the keys that have been affected */
404       for (i = 0; i < state.index; i++)
405         if (!g_settings_backend_get_writable (delayed->priv->backend,
406                                               state.keys[i]))
407           g_tree_remove (delayed->priv->delayed, state.keys[i]);
408
409       g_free (state.keys);
410
411       last_one = g_tree_nnodes (delayed->priv->delayed) == 0;
412     }
413
414   g_static_mutex_unlock (&delayed->priv->lock);
415
416   if (last_one)
417     g_delayed_settings_backend_notify_unapplied (delayed);
418
419   g_settings_backend_path_writable_changed (G_SETTINGS_BACKEND (delayed),
420                                             path);
421 }
422
423 static void
424 g_delayed_settings_backend_finalize (GObject *object)
425 {
426   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object);
427
428   g_static_mutex_free (&delayed->priv->lock);
429   g_object_unref (delayed->priv->backend);
430   g_tree_unref (delayed->priv->delayed);
431
432   /* if our owner is still alive, why are we finalizing? */
433   g_assert (delayed->priv->owner == NULL);
434
435   G_OBJECT_CLASS (g_delayed_settings_backend_parent_class)
436     ->finalize (object);
437 }
438
439 static void
440 g_delayed_settings_backend_class_init (GDelayedSettingsBackendClass *class)
441 {
442   GSettingsBackendClass *backend_class = G_SETTINGS_BACKEND_CLASS (class);
443   GObjectClass *object_class = G_OBJECT_CLASS (class);
444
445   g_type_class_add_private (class, sizeof (GDelayedSettingsBackendPrivate));
446
447   backend_class->read = g_delayed_settings_backend_read;
448   backend_class->write = g_delayed_settings_backend_write;
449   backend_class->write_keys = g_delayed_settings_backend_write_keys;
450   backend_class->reset = g_delayed_settings_backend_reset;
451   backend_class->reset_path = g_delayed_settings_backend_reset_path;
452   backend_class->get_writable = g_delayed_settings_backend_get_writable;
453   backend_class->subscribe = g_delayed_settings_backend_subscribe;
454   backend_class->unsubscribe = g_delayed_settings_backend_unsubscribe;
455   backend_class->get_permission = g_delayed_settings_backend_get_permission;
456
457   object_class->finalize = g_delayed_settings_backend_finalize;
458 }
459
460 static void
461 g_delayed_settings_backend_init (GDelayedSettingsBackend *delayed)
462 {
463   delayed->priv =
464     G_TYPE_INSTANCE_GET_PRIVATE (delayed, G_TYPE_DELAYED_SETTINGS_BACKEND,
465                                  GDelayedSettingsBackendPrivate);
466
467   delayed->priv->delayed = g_settings_backend_create_tree ();
468   g_static_mutex_init (&delayed->priv->lock);
469 }
470
471 static void
472 g_delayed_settings_backend_disown (gpointer  data,
473                                    GObject  *where_the_object_was)
474 {
475   GDelayedSettingsBackend *delayed = data;
476
477   g_static_mutex_lock (&delayed->priv->lock);
478   delayed->priv->owner_context = NULL;
479   delayed->priv->owner = NULL;
480   g_static_mutex_unlock (&delayed->priv->lock);
481 }
482
483 GDelayedSettingsBackend *
484 g_delayed_settings_backend_new (GSettingsBackend *backend,
485                                 gpointer          owner,
486                                 GMainContext     *owner_context)
487 {
488   GDelayedSettingsBackend *delayed;
489
490   delayed = g_object_new (G_TYPE_DELAYED_SETTINGS_BACKEND, NULL);
491   delayed->priv->backend = g_object_ref (backend);
492   delayed->priv->owner_context = owner_context;
493   delayed->priv->owner = owner;
494
495   g_object_weak_ref (owner, g_delayed_settings_backend_disown, delayed);
496
497   g_settings_backend_watch (delayed->priv->backend, G_OBJECT (delayed), NULL,
498                             delayed_backend_changed,
499                             delayed_backend_path_changed,
500                             delayed_backend_keys_changed,
501                             delayed_backend_writable_changed,
502                             delayed_backend_path_writable_changed);
503
504   return delayed;
505 }