2 * Copyright © 2009, 2010 Codethink Limited
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.
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.
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.
19 * Author: Ryan Lortie <desrt@desrt.ca>
24 #include "gdelayedsettingsbackend.h"
25 #include "gsettingsbackendinternal.h"
30 struct _GDelayedSettingsBackendPrivate
32 GSettingsBackend
*backend
;
36 GMainContext
*owner_context
;
40 G_DEFINE_TYPE (GDelayedSettingsBackend
,
41 g_delayed_settings_backend
,
42 G_TYPE_SETTINGS_BACKEND
)
45 invoke_notify_unapplied (gpointer data
)
47 g_object_notify (data
, "has-unapplied");
48 g_object_unref (data
);
54 g_delayed_settings_backend_notify_unapplied (GDelayedSettingsBackend
*delayed
)
56 GMainContext
*target_context
;
59 g_static_mutex_lock (&delayed
->priv
->lock
);
60 if (delayed
->priv
->owner
)
62 target_context
= delayed
->priv
->owner_context
;
63 target
= g_object_ref (delayed
->priv
->owner
);
67 target_context
= NULL
;
70 g_static_mutex_unlock (&delayed
->priv
->lock
);
74 if (g_settings_backend_get_active_context () != target_context
)
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
);
86 invoke_notify_unapplied (target
);
92 g_delayed_settings_backend_read (GSettingsBackend
*backend
,
94 const GVariantType
*expected_type
,
95 gboolean default_value
)
97 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (backend
);
98 gpointer result
= NULL
;
102 g_static_mutex_lock (&delayed
->priv
->lock
);
103 if (g_tree_lookup_extended (delayed
->priv
->delayed
, key
, NULL
, &result
))
105 /* NULL in the tree means we should consult the default value */
107 g_variant_ref (result
);
109 default_value
= TRUE
;
111 g_static_mutex_unlock (&delayed
->priv
->lock
);
115 result
= g_settings_backend_read (delayed
->priv
->backend
, key
,
116 expected_type
, default_value
);
122 g_delayed_settings_backend_write (GSettingsBackend
*backend
,
127 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (backend
);
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
);
136 g_settings_backend_changed (backend
, key
, origin_tag
);
139 g_delayed_settings_backend_notify_unapplied (delayed
);
145 add_to_tree (gpointer key
,
149 g_tree_insert (user_data
, g_strdup (key
), g_variant_ref (value
));
154 g_delayed_settings_backend_write_tree (GSettingsBackend
*backend
,
158 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (backend
);
161 g_static_mutex_lock (&delayed
->priv
->lock
);
162 was_empty
= g_tree_nnodes (delayed
->priv
->delayed
) == 0;
164 g_tree_foreach (tree
, add_to_tree
, delayed
->priv
->delayed
);
165 g_static_mutex_unlock (&delayed
->priv
->lock
);
167 g_settings_backend_changed_tree (backend
, tree
, origin_tag
);
170 g_delayed_settings_backend_notify_unapplied (delayed
);
176 g_delayed_settings_backend_get_writable (GSettingsBackend
*backend
,
179 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (backend
);
181 return g_settings_backend_get_writable (delayed
->priv
->backend
, name
);
185 g_delayed_settings_backend_reset (GSettingsBackend
*backend
,
189 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (backend
);
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
);
198 g_delayed_settings_backend_notify_unapplied (delayed
);
202 g_delayed_settings_backend_subscribe (GSettingsBackend
*backend
,
205 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (backend
);
207 g_settings_backend_subscribe (delayed
->priv
->backend
, name
);
211 g_delayed_settings_backend_unsubscribe (GSettingsBackend
*backend
,
214 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (backend
);
216 g_settings_backend_unsubscribe (delayed
->priv
->backend
, name
);
220 g_delayed_settings_backend_get_permission (GSettingsBackend
*backend
,
223 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (backend
);
225 return g_settings_backend_get_permission (delayed
->priv
->backend
, path
);
231 g_delayed_settings_backend_get_has_unapplied (GDelayedSettingsBackend
*delayed
)
233 /* we don't need to lock for this... */
235 return g_tree_nnodes (delayed
->priv
->delayed
) > 0;
239 g_delayed_settings_backend_apply (GDelayedSettingsBackend
*delayed
)
241 if (g_tree_nnodes (delayed
->priv
->delayed
) > 0)
246 g_static_mutex_lock (&delayed
->priv
->lock
);
247 tmp
= delayed
->priv
->delayed
;
248 delayed
->priv
->delayed
= g_settings_backend_create_tree ();
249 success
= g_settings_backend_write_tree (delayed
->priv
->backend
,
251 g_static_mutex_unlock (&delayed
->priv
->lock
);
254 g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed
),
259 g_delayed_settings_backend_notify_unapplied (delayed
);
264 g_delayed_settings_backend_revert (GDelayedSettingsBackend
*delayed
)
266 if (g_tree_nnodes (delayed
->priv
->delayed
) > 0)
270 g_static_mutex_lock (&delayed
->priv
->lock
);
271 tmp
= delayed
->priv
->delayed
;
272 delayed
->priv
->delayed
= g_settings_backend_create_tree ();
273 g_static_mutex_unlock (&delayed
->priv
->lock
);
274 g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed
), tmp
, NULL
);
277 g_delayed_settings_backend_notify_unapplied (delayed
);
281 /* change notification */
283 delayed_backend_changed (GSettingsBackend
*backend
,
288 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (target
);
290 if (origin_tag
!= delayed
->priv
)
291 g_settings_backend_changed (G_SETTINGS_BACKEND (delayed
),
296 delayed_backend_keys_changed (GSettingsBackend
*backend
,
299 const gchar
* const *items
,
302 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (target
);
304 if (origin_tag
!= delayed
->priv
)
305 g_settings_backend_keys_changed (G_SETTINGS_BACKEND (delayed
),
306 path
, items
, origin_tag
);
310 delayed_backend_path_changed (GSettingsBackend
*backend
,
315 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (target
);
317 if (origin_tag
!= delayed
->priv
)
318 g_settings_backend_path_changed (G_SETTINGS_BACKEND (delayed
),
323 delayed_backend_writable_changed (GSettingsBackend
*backend
,
327 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (target
);
328 gboolean last_one
= FALSE
;
330 g_static_mutex_lock (&delayed
->priv
->lock
);
332 if (g_tree_lookup (delayed
->priv
->delayed
, key
) &&
333 !g_settings_backend_get_writable (delayed
->priv
->backend
, key
))
335 /* drop the key from our changeset if it just became read-only.
336 * no need to signal since the writable change below implies it.
338 g_tree_remove (delayed
->priv
->delayed
, key
);
340 /* if that was the only key... */
341 last_one
= g_tree_nnodes (delayed
->priv
->delayed
) == 0;
344 g_static_mutex_unlock (&delayed
->priv
->lock
);
347 g_delayed_settings_backend_notify_unapplied (delayed
);
349 g_settings_backend_writable_changed (G_SETTINGS_BACKEND (delayed
), key
);
352 /* slow method until we get foreach-with-remove in GTree
362 check_prefix (gpointer key
,
366 CheckPrefixState
*state
= data
;
368 if (g_str_has_prefix (key
, state
->path
))
369 state
->keys
[state
->index
++] = key
;
375 delayed_backend_path_writable_changed (GSettingsBackend
*backend
,
379 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (target
);
380 gboolean last_one
= FALSE
;
383 g_static_mutex_lock (&delayed
->priv
->lock
);
385 n_keys
= g_tree_nnodes (delayed
->priv
->delayed
);
389 CheckPrefixState state
= { path
, g_new (const gchar
*, n_keys
) };
392 /* collect a list of possibly-affected keys (ie: matching the path) */
393 g_tree_foreach (delayed
->priv
->delayed
, check_prefix
, &state
);
395 /* drop the keys that have been affected */
396 for (i
= 0; i
< state
.index
; i
++)
397 if (!g_settings_backend_get_writable (delayed
->priv
->backend
,
399 g_tree_remove (delayed
->priv
->delayed
, state
.keys
[i
]);
403 last_one
= g_tree_nnodes (delayed
->priv
->delayed
) == 0;
406 g_static_mutex_unlock (&delayed
->priv
->lock
);
409 g_delayed_settings_backend_notify_unapplied (delayed
);
411 g_settings_backend_path_writable_changed (G_SETTINGS_BACKEND (delayed
),
416 g_delayed_settings_backend_finalize (GObject
*object
)
418 GDelayedSettingsBackend
*delayed
= G_DELAYED_SETTINGS_BACKEND (object
);
420 g_static_mutex_free (&delayed
->priv
->lock
);
421 g_object_unref (delayed
->priv
->backend
);
422 g_tree_unref (delayed
->priv
->delayed
);
424 /* if our owner is still alive, why are we finalizing? */
425 g_assert (delayed
->priv
->owner
== NULL
);
427 G_OBJECT_CLASS (g_delayed_settings_backend_parent_class
)
432 g_delayed_settings_backend_class_init (GDelayedSettingsBackendClass
*class)
434 GSettingsBackendClass
*backend_class
= G_SETTINGS_BACKEND_CLASS (class);
435 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
437 g_type_class_add_private (class, sizeof (GDelayedSettingsBackendPrivate
));
439 backend_class
->read
= g_delayed_settings_backend_read
;
440 backend_class
->write
= g_delayed_settings_backend_write
;
441 backend_class
->write_tree
= g_delayed_settings_backend_write_tree
;
442 backend_class
->reset
= g_delayed_settings_backend_reset
;
443 backend_class
->get_writable
= g_delayed_settings_backend_get_writable
;
444 backend_class
->subscribe
= g_delayed_settings_backend_subscribe
;
445 backend_class
->unsubscribe
= g_delayed_settings_backend_unsubscribe
;
446 backend_class
->get_permission
= g_delayed_settings_backend_get_permission
;
448 object_class
->finalize
= g_delayed_settings_backend_finalize
;
452 g_delayed_settings_backend_init (GDelayedSettingsBackend
*delayed
)
455 G_TYPE_INSTANCE_GET_PRIVATE (delayed
, G_TYPE_DELAYED_SETTINGS_BACKEND
,
456 GDelayedSettingsBackendPrivate
);
458 delayed
->priv
->delayed
= g_settings_backend_create_tree ();
459 g_static_mutex_init (&delayed
->priv
->lock
);
463 g_delayed_settings_backend_disown (gpointer data
,
464 GObject
*where_the_object_was
)
466 GDelayedSettingsBackend
*delayed
= data
;
468 g_static_mutex_lock (&delayed
->priv
->lock
);
469 delayed
->priv
->owner_context
= NULL
;
470 delayed
->priv
->owner
= NULL
;
471 g_static_mutex_unlock (&delayed
->priv
->lock
);
474 GDelayedSettingsBackend
*
475 g_delayed_settings_backend_new (GSettingsBackend
*backend
,
477 GMainContext
*owner_context
)
479 GDelayedSettingsBackend
*delayed
;
481 delayed
= g_object_new (G_TYPE_DELAYED_SETTINGS_BACKEND
, NULL
);
482 delayed
->priv
->backend
= g_object_ref (backend
);
483 delayed
->priv
->owner_context
= owner_context
;
484 delayed
->priv
->owner
= owner
;
486 g_object_weak_ref (owner
, g_delayed_settings_backend_disown
, delayed
);
488 g_settings_backend_watch (delayed
->priv
->backend
, G_OBJECT (delayed
), NULL
,
489 delayed_backend_changed
,
490 delayed_backend_path_changed
,
491 delayed_backend_keys_changed
,
492 delayed_backend_writable_changed
,
493 delayed_backend_path_writable_changed
);