add get_permission API to GSettingsBackend
[platform/upstream/glib.git] / gio / gdelayedsettingsbackend.c
index 505a85b..890c614 100644 (file)
 struct _GDelayedSettingsBackendPrivate
 {
   GSettingsBackend *backend;
+  GStaticMutex lock;
   GTree *delayed;
+
+  GMainContext *owner_context;
   gpointer owner;
 };
 
@@ -39,19 +42,68 @@ G_DEFINE_TYPE (GDelayedSettingsBackend,
                g_delayed_settings_backend,
                G_TYPE_SETTINGS_BACKEND)
 
+static gboolean
+invoke_notify_unapplied (gpointer data)
+{
+  g_object_notify (data, "has-unapplied");
+  g_object_unref (data);
+
+  return FALSE;
+}
+
+static void
+g_delayed_settings_backend_notify_unapplied (GDelayedSettingsBackend *delayed)
+{
+  GMainContext *target_context;
+  GObject *target;
+
+  g_static_mutex_lock (&delayed->priv->lock);
+  if (delayed->priv->owner)
+    {
+      target_context = delayed->priv->owner_context;
+      target = g_object_ref (delayed->priv->owner);
+    }
+  else
+    {
+      target_context = NULL;
+      target = NULL;
+    }
+  g_static_mutex_unlock (&delayed->priv->lock);
+
+  if (target != NULL)
+    {
+      if (g_settings_backend_get_active_context () != target_context)
+        {
+          GSource *source;
+
+          source = g_idle_source_new ();
+          g_source_set_priority (source, G_PRIORITY_DEFAULT);
+          g_source_set_callback (source, invoke_notify_unapplied,
+                                 target, g_object_unref);
+          g_source_attach (source, target_context);
+          g_source_unref (source);
+        }
+      else
+        invoke_notify_unapplied (target);
+    }
+}
+
+
 static GVariant *
 g_delayed_settings_backend_read (GSettingsBackend   *backend,
                                  const gchar        *key,
-                                 const GVariantType *expected_type)
+                                 const GVariantType *expected_type,
+                                 gboolean            default_value)
 {
   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
   GVariant *result;
 
-  if ((result = g_tree_lookup (delayed->priv->delayed, key)))
+  if (!default_value &&
+      (result = g_tree_lookup (delayed->priv->delayed, key)))
     return g_variant_ref (result);
 
   return g_settings_backend_read (delayed->priv->backend,
-                                  key, expected_type);
+                                  key, expected_type, default_value);
 }
 static gboolean
 g_delayed_settings_backend_write (GSettingsBackend *backend,
@@ -62,13 +114,16 @@ g_delayed_settings_backend_write (GSettingsBackend *backend,
   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
   gboolean was_empty;
 
+  g_static_mutex_lock (&delayed->priv->lock);
   was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
   g_tree_insert (delayed->priv->delayed, g_strdup (key),
                  g_variant_ref_sink (value));
+  g_static_mutex_unlock (&delayed->priv->lock);
+
   g_settings_backend_changed (backend, key, origin_tag);
 
-  if (was_empty && delayed->priv->owner)
-    g_object_notify (delayed->priv->owner, "has-unapplied");
+  if (was_empty)
+    g_delayed_settings_backend_notify_unapplied (delayed);
 
   return TRUE;
 }
@@ -90,14 +145,16 @@ g_delayed_settings_backend_write_keys (GSettingsBackend *backend,
   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
   gboolean was_empty;
 
+  g_static_mutex_lock (&delayed->priv->lock);
   was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
 
   g_tree_foreach (tree, add_to_tree, delayed->priv->delayed);
+  g_static_mutex_unlock (&delayed->priv->lock);
 
   g_settings_backend_changed_tree (backend, tree, origin_tag);
 
-  if (was_empty && delayed->priv->owner)
-    g_object_notify (delayed->priv->owner, "has-unapplied");
+  if (was_empty)
+    g_delayed_settings_backend_notify_unapplied (delayed);
 
   return TRUE;
 }
@@ -145,11 +202,22 @@ g_delayed_settings_backend_unsubscribe (GSettingsBackend *backend,
   g_settings_backend_unsubscribe (delayed->priv->backend, name);
 }
 
+static GPermission *
+g_delayed_settings_backend_get_permission (GSettingsBackend *backend,
+                                           const gchar      *path)
+{
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
+
+  return g_settings_backend_get_permission (delayed->priv->backend, path);
+}
+
 
 /* method calls */
 gboolean
 g_delayed_settings_backend_get_has_unapplied (GDelayedSettingsBackend *delayed)
 {
+  /* we don't need to lock for this... */
+
   return g_tree_nnodes (delayed->priv->delayed) > 0;
 }
 
@@ -158,16 +226,23 @@ g_delayed_settings_backend_apply (GDelayedSettingsBackend *delayed)
 {
   if (g_tree_nnodes (delayed->priv->delayed) > 0)
     {
+      gboolean success;
       GTree *tmp;
 
+      g_static_mutex_lock (&delayed->priv->lock);
       tmp = delayed->priv->delayed;
       delayed->priv->delayed = g_settings_backend_create_tree ();
-      g_settings_backend_write_keys (delayed->priv->backend,
-                                     tmp, delayed->priv);
+      success = g_settings_backend_write_keys (delayed->priv->backend,
+                                               tmp, delayed->priv);
+      g_static_mutex_unlock (&delayed->priv->lock);
+
+      if (!success)
+        g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed),
+                                         tmp, NULL);
+
       g_tree_unref (tmp);
 
-      if (delayed->priv->owner)
-        g_object_notify (delayed->priv->owner, "has-unapplied");
+      g_delayed_settings_backend_notify_unapplied (delayed);
     }
 }
 
@@ -178,24 +253,25 @@ g_delayed_settings_backend_revert (GDelayedSettingsBackend *delayed)
     {
       GTree *tmp;
 
+      g_static_mutex_lock (&delayed->priv->lock);
       tmp = delayed->priv->delayed;
       delayed->priv->delayed = g_settings_backend_create_tree ();
+      g_static_mutex_unlock (&delayed->priv->lock);
       g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed), tmp, NULL);
       g_tree_unref (tmp);
 
-      if (delayed->priv->owner)
-        g_object_notify (delayed->priv->owner, "has-unapplied");
+      g_delayed_settings_backend_notify_unapplied (delayed);
     }
 }
 
 /* change notification */
 static void
 delayed_backend_changed (GSettingsBackend *backend,
+                         GObject          *target,
                          const gchar      *key,
-                         gpointer          origin_tag,
-                         gpointer          user_data)
+                         gpointer          origin_tag)
 {
-  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
 
   if (origin_tag != delayed->priv)
     g_settings_backend_changed (G_SETTINGS_BACKEND (delayed),
@@ -204,12 +280,12 @@ delayed_backend_changed (GSettingsBackend *backend,
 
 static void
 delayed_backend_keys_changed (GSettingsBackend    *backend,
+                              GObject             *target,
                               const gchar         *path,
                               const gchar * const *items,
-                              gpointer             origin_tag,
-                              gpointer             user_data)
+                              gpointer             origin_tag)
 {
-  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
 
   if (origin_tag != delayed->priv)
     g_settings_backend_keys_changed (G_SETTINGS_BACKEND (delayed),
@@ -217,12 +293,12 @@ delayed_backend_keys_changed (GSettingsBackend    *backend,
 }
 
 static void
-delayed_backend_path_changed (GSettingsBackend    *backend,
-                              const gchar         *path,
-                              gpointer             origin_tag,
-                              gpointer             user_data)
+delayed_backend_path_changed (GSettingsBackend *backend,
+                              GObject          *target,
+                              const gchar      *path,
+                              gpointer          origin_tag)
 {
-  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
 
   if (origin_tag != delayed->priv)
     g_settings_backend_path_changed (G_SETTINGS_BACKEND (delayed),
@@ -231,27 +307,93 @@ delayed_backend_path_changed (GSettingsBackend    *backend,
 
 static void
 delayed_backend_writable_changed (GSettingsBackend *backend,
-                                  const gchar      *key,
-                                  gpointer          user_data)
+                                  GObject          *target,
+                                  const gchar      *key)
 {
-  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
+  gboolean last_one = FALSE;
+
+  g_static_mutex_lock (&delayed->priv->lock);
+
+  if (g_tree_lookup (delayed->priv->delayed, key) &&
+      !g_settings_backend_get_writable (delayed->priv->backend, key))
+    {
+      /* drop the key from our changeset if it just became read-only.
+       * no need to signal since the writable change below implies it.
+       */
+      g_tree_remove (delayed->priv->delayed, key);
+
+      /* if that was the only key... */
+      last_one = g_tree_nnodes (delayed->priv->delayed) == 0;
+    }
+
+  g_static_mutex_unlock (&delayed->priv->lock);
+
+  if (last_one)
+    g_delayed_settings_backend_notify_unapplied (delayed);
 
-  /* XXX: maybe drop keys from the delayed-apply settings
-   *      if they became non-writable?
-   */
   g_settings_backend_writable_changed (G_SETTINGS_BACKEND (delayed), key);
 }
 
+/* slow method until we get foreach-with-remove in GTree
+ */
+typedef struct
+{
+  const gchar *path;
+  const gchar **keys;
+  gsize index;
+} CheckPrefixState;
+
+static gboolean
+check_prefix (gpointer key,
+              gpointer value,
+              gpointer data)
+{
+  CheckPrefixState *state = data;
+
+  if (g_str_has_prefix (key, state->path))
+    state->keys[state->index++] = key;
+
+  return FALSE;
+}
+
 static void
 delayed_backend_path_writable_changed (GSettingsBackend *backend,
-                                       const gchar      *path,
-                                       gpointer          user_data)
+                                       GObject          *target,
+                                       const gchar      *path)
 {
-  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
+  gboolean last_one = FALSE;
+  gsize n_keys;
+
+  g_static_mutex_lock (&delayed->priv->lock);
+
+  n_keys = g_tree_nnodes (delayed->priv->delayed);
+
+  if (n_keys > 0)
+    {
+      CheckPrefixState state = { path, g_new (const gchar *, n_keys) };
+      gsize i;
+
+      /* collect a list of possibly-affected keys (ie: matching the path) */
+      g_tree_foreach (delayed->priv->delayed, check_prefix, &state);
+
+      /* drop the keys that have been affected */
+      for (i = 0; i < state.index; i++)
+        if (!g_settings_backend_get_writable (delayed->priv->backend,
+                                              state.keys[i]))
+          g_tree_remove (delayed->priv->delayed, state.keys[i]);
+
+      g_free (state.keys);
+
+      last_one = g_tree_nnodes (delayed->priv->delayed) == 0;
+    }
+
+  g_static_mutex_unlock (&delayed->priv->lock);
+
+  if (last_one)
+    g_delayed_settings_backend_notify_unapplied (delayed);
 
-  /* XXX: maybe drop keys from the delayed-apply settings
-   *      if they became non-writable?
-   */
   g_settings_backend_path_writable_changed (G_SETTINGS_BACKEND (delayed),
                                             path);
 }
@@ -261,8 +403,14 @@ g_delayed_settings_backend_finalize (GObject *object)
 {
   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object);
 
-  g_settings_backend_unwatch (delayed->priv->backend, delayed);
+  g_static_mutex_free (&delayed->priv->lock);
   g_object_unref (delayed->priv->backend);
+
+  /* if our owner is still alive, why are we finalizing? */
+  g_assert (delayed->priv->owner == NULL);
+
+  G_OBJECT_CLASS (g_delayed_settings_backend_parent_class)
+    ->finalize (object);
 }
 
 static void
@@ -281,6 +429,7 @@ g_delayed_settings_backend_class_init (GDelayedSettingsBackendClass *class)
   backend_class->get_writable = g_delayed_settings_backend_get_writable;
   backend_class->subscribe = g_delayed_settings_backend_subscribe;
   backend_class->unsubscribe = g_delayed_settings_backend_unsubscribe;
+  backend_class->get_permission = g_delayed_settings_backend_get_permission;
 
   object_class->finalize = g_delayed_settings_backend_finalize;
 }
@@ -293,25 +442,41 @@ g_delayed_settings_backend_init (GDelayedSettingsBackend *delayed)
                                  GDelayedSettingsBackendPrivate);
 
   delayed->priv->delayed = g_settings_backend_create_tree ();
+  g_static_mutex_init (&delayed->priv->lock);
+}
+
+static void
+g_delayed_settings_backend_disown (gpointer  data,
+                                   GObject  *where_the_object_was)
+{
+  GDelayedSettingsBackend *delayed = data;
+
+  g_static_mutex_lock (&delayed->priv->lock);
+  delayed->priv->owner_context = NULL;
+  delayed->priv->owner = NULL;
+  g_static_mutex_unlock (&delayed->priv->lock);
 }
 
 GDelayedSettingsBackend *
 g_delayed_settings_backend_new (GSettingsBackend *backend,
-                                gpointer          owner)
+                                gpointer          owner,
+                                GMainContext     *owner_context)
 {
   GDelayedSettingsBackend *delayed;
 
   delayed = g_object_new (G_TYPE_DELAYED_SETTINGS_BACKEND, NULL);
   delayed->priv->backend = g_object_ref (backend);
+  delayed->priv->owner_context = owner_context;
   delayed->priv->owner = owner;
 
-  g_settings_backend_watch (delayed->priv->backend,
+  g_object_weak_ref (owner, g_delayed_settings_backend_disown, delayed);
+
+  g_settings_backend_watch (delayed->priv->backend, G_OBJECT (delayed), NULL,
                             delayed_backend_changed,
                             delayed_backend_path_changed,
                             delayed_backend_keys_changed,
                             delayed_backend_writable_changed,
-                            delayed_backend_path_writable_changed,
-                            delayed);
+                            delayed_backend_path_writable_changed);
 
   return delayed;
 }