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