[gnome-builder] libide/core: crib action muxer updates from libpanel



commit c3e69f4d8180535936f108fdbc331b555423523d
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jul 27 18:02:44 2022 -0700

    libide/core: crib action muxer updates from libpanel
    
    We will need this object here too, so bring all of todays changes into
    Builder as well to be used where we can.

 src/libide/core/gsettings-mapping.c | 592 ++++++++++++++++++++++++++++++++++++
 src/libide/core/gsettings-mapping.h |  34 +++
 src/libide/core/ide-action-muxer.c  | 298 +++++++++++++++++-
 src/libide/core/ide-action-muxer.h  |  46 ++-
 src/libide/core/meson.build         |   2 +
 5 files changed, 949 insertions(+), 23 deletions(-)
---
diff --git a/src/libide/core/gsettings-mapping.c b/src/libide/core/gsettings-mapping.c
new file mode 100644
index 000000000..8e22e1397
--- /dev/null
+++ b/src/libide/core/gsettings-mapping.c
@@ -0,0 +1,592 @@
+/*
+ * Copyright © 2010 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vincent Untz <vuntz gnome org>
+ */
+
+#include "config.h"
+
+#include "gsettings-mapping.h"
+
+static GVariant *
+g_settings_set_mapping_int (const GValue       *value,
+                            const GVariantType *expected_type)
+{
+  GVariant *variant = NULL;
+  gint64 l;
+
+  if (G_VALUE_HOLDS_INT (value))
+    l = g_value_get_int (value);
+  else if (G_VALUE_HOLDS_INT64 (value))
+    l = g_value_get_int64 (value);
+  else
+    return NULL;
+
+  if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT16))
+    {
+      if (G_MININT16 <= l && l <= G_MAXINT16)
+        variant = g_variant_new_int16 ((gint16) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT16))
+    {
+      if (0 <= l && l <= G_MAXUINT16)
+        variant = g_variant_new_uint16 ((guint16) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT32))
+    {
+      if (G_MININT32 <= l && l <= G_MAXINT32)
+        variant = g_variant_new_int32 ((int) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT32))
+    {
+      if (0 <= l && l <= G_MAXUINT32)
+        variant = g_variant_new_uint32 ((guint) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT64))
+    {
+      if (G_MININT64 <= l && l <= G_MAXINT64)
+        variant = g_variant_new_int64 ((gint64) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT64))
+    {
+      if (0 <= l && l <= G_MAXUINT64)
+        variant = g_variant_new_uint64 ((guint64) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_HANDLE))
+    {
+      if (0 <= l && l <= G_MAXUINT32)
+        variant = g_variant_new_handle ((guint) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_DOUBLE))
+    variant = g_variant_new_double ((double) l);
+
+  return variant;
+}
+
+static GVariant *
+g_settings_set_mapping_float (const GValue       *value,
+                              const GVariantType *expected_type)
+{
+  GVariant *variant = NULL;
+  double d;
+  gint64 l;
+
+  if (G_VALUE_HOLDS_DOUBLE (value))
+    d = g_value_get_double (value);
+  else
+    return NULL;
+
+  l = (gint64) d;
+  if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT16))
+    {
+      if (G_MININT16 <= l && l <= G_MAXINT16)
+        variant = g_variant_new_int16 ((gint16) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT16))
+    {
+      if (0 <= l && l <= G_MAXUINT16)
+        variant = g_variant_new_uint16 ((guint16) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT32))
+    {
+      if (G_MININT32 <= l && l <= G_MAXINT32)
+        variant = g_variant_new_int32 ((int) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT32))
+    {
+      if (0 <= l && l <= G_MAXUINT32)
+        variant = g_variant_new_uint32 ((guint) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT64))
+    {
+      if (G_MININT64 <= l && l <= G_MAXINT64)
+        variant = g_variant_new_int64 ((gint64) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT64))
+    {
+      if (0 <= l && l <= G_MAXUINT64)
+        variant = g_variant_new_uint64 ((guint64) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_HANDLE))
+    {
+      if (0 <= l && l <= G_MAXUINT32)
+        variant = g_variant_new_handle ((guint) l);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_DOUBLE))
+    variant = g_variant_new_double ((double) d);
+
+  return variant;
+}
+static GVariant *
+g_settings_set_mapping_unsigned_int (const GValue       *value,
+                                     const GVariantType *expected_type)
+{
+  GVariant *variant = NULL;
+  guint64 u;
+
+  if (G_VALUE_HOLDS_UINT (value))
+    u = g_value_get_uint (value);
+  else if (G_VALUE_HOLDS_UINT64 (value))
+    u = g_value_get_uint64 (value);
+  else
+    return NULL;
+
+  if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT16))
+    {
+      if (u <= G_MAXINT16)
+        variant = g_variant_new_int16 ((gint16) u);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT16))
+    {
+      if (u <= G_MAXUINT16)
+        variant = g_variant_new_uint16 ((guint16) u);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT32))
+    {
+      if (u <= G_MAXINT32)
+        variant = g_variant_new_int32 ((int) u);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT32))
+    {
+      if (u <= G_MAXUINT32)
+        variant = g_variant_new_uint32 ((guint) u);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT64))
+    {
+      if (u <= G_MAXINT64)
+        variant = g_variant_new_int64 ((gint64) u);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT64))
+    {
+      if (u <= G_MAXUINT64)
+        variant = g_variant_new_uint64 ((guint64) u);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_HANDLE))
+    {
+      if (u <= G_MAXUINT32)
+        variant = g_variant_new_handle ((guint) u);
+    }
+  else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_DOUBLE))
+    variant = g_variant_new_double ((double) u);
+
+  return variant;
+}
+
+static gboolean
+g_settings_get_mapping_int (GValue   *value,
+                            GVariant *variant)
+{
+  const GVariantType *type;
+  gint64 l;
+
+  type = g_variant_get_type (variant);
+
+  if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
+    l = g_variant_get_int16 (variant);
+  else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
+    l = g_variant_get_int32 (variant);
+  else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
+    l = g_variant_get_int64 (variant);
+  else
+    return FALSE;
+
+  if (G_VALUE_HOLDS_INT (value))
+    {
+      g_value_set_int (value, l);
+      return (G_MININT32 <= l && l <= G_MAXINT32);
+    }
+  else if (G_VALUE_HOLDS_UINT (value))
+    {
+      g_value_set_uint (value, l);
+      return (0 <= l && l <= G_MAXUINT32);
+    }
+  else if (G_VALUE_HOLDS_INT64 (value))
+    {
+      g_value_set_int64 (value, l);
+      return (G_MININT64 <= l && l <= G_MAXINT64);
+    }
+  else if (G_VALUE_HOLDS_UINT64 (value))
+    {
+      g_value_set_uint64 (value, l);
+      return (0 <= l && l <= G_MAXUINT64);
+    }
+  else if (G_VALUE_HOLDS_DOUBLE (value))
+    {
+      g_value_set_double (value, l);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+g_settings_get_mapping_float (GValue   *value,
+                              GVariant *variant)
+{
+  const GVariantType *type;
+  double d;
+  gint64 l;
+
+  type = g_variant_get_type (variant);
+
+  if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
+    d = g_variant_get_double (variant);
+  else
+    return FALSE;
+
+  l = (gint64)d;
+  if (G_VALUE_HOLDS_INT (value))
+    {
+      g_value_set_int (value, l);
+      return (G_MININT32 <= l && l <= G_MAXINT32);
+    }
+  else if (G_VALUE_HOLDS_UINT (value))
+    {
+      g_value_set_uint (value, l);
+      return (0 <= l && l <= G_MAXUINT32);
+    }
+  else if (G_VALUE_HOLDS_INT64 (value))
+    {
+      g_value_set_int64 (value, l);
+      return (G_MININT64 <= l && l <= G_MAXINT64);
+    }
+  else if (G_VALUE_HOLDS_UINT64 (value))
+    {
+      g_value_set_uint64 (value, l);
+      return (0 <= l && l <= G_MAXUINT64);
+    }
+  else if (G_VALUE_HOLDS_DOUBLE (value))
+    {
+      g_value_set_double (value, d);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+static gboolean
+g_settings_get_mapping_unsigned_int (GValue   *value,
+                                     GVariant *variant)
+{
+  const GVariantType *type;
+  guint64 u;
+
+  type = g_variant_get_type (variant);
+
+  if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
+    u = g_variant_get_uint16 (variant);
+  else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
+    u = g_variant_get_uint32 (variant);
+  else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
+    u = g_variant_get_uint64 (variant);
+  else if (g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
+    u = g_variant_get_handle (variant);
+  else
+    return FALSE;
+
+  if (G_VALUE_HOLDS_INT (value))
+    {
+      g_value_set_int (value, u);
+      return (u <= G_MAXINT32);
+    }
+  else if (G_VALUE_HOLDS_UINT (value))
+    {
+      g_value_set_uint (value, u);
+      return (u <= G_MAXUINT32);
+    }
+  else if (G_VALUE_HOLDS_INT64 (value))
+    {
+      g_value_set_int64 (value, u);
+      return (u <= G_MAXINT64);
+    }
+  else if (G_VALUE_HOLDS_UINT64 (value))
+    {
+      g_value_set_uint64 (value, u);
+      return (u <= G_MAXUINT64);
+    }
+  else if (G_VALUE_HOLDS_DOUBLE (value))
+    {
+      g_value_set_double (value, u);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+GVariant *
+g_settings_set_mapping (const GValue       *value,
+                        const GVariantType *expected_type,
+                        gpointer            user_data)
+{
+  char *type_string;
+
+  if (G_VALUE_HOLDS_BOOLEAN (value))
+    {
+      if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_BOOLEAN))
+        return g_variant_new_boolean (g_value_get_boolean (value));
+    }
+
+  else if (G_VALUE_HOLDS_CHAR (value)  ||
+           G_VALUE_HOLDS_UCHAR (value))
+    {
+      if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_BYTE))
+        {
+          if (G_VALUE_HOLDS_CHAR (value))
+            return g_variant_new_byte (g_value_get_schar (value));
+          else
+            return g_variant_new_byte (g_value_get_uchar (value));
+        }
+    }
+
+  else if (G_VALUE_HOLDS_INT (value)   ||
+           G_VALUE_HOLDS_INT64 (value))
+    return g_settings_set_mapping_int (value, expected_type);
+
+  else if (G_VALUE_HOLDS_DOUBLE (value))
+    return g_settings_set_mapping_float (value, expected_type);
+
+  else if (G_VALUE_HOLDS_UINT (value)  ||
+           G_VALUE_HOLDS_UINT64 (value))
+    return g_settings_set_mapping_unsigned_int (value, expected_type);
+
+  else if (G_VALUE_HOLDS_STRING (value))
+    {
+      if (g_value_get_string (value) == NULL)
+        return NULL;
+      else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_STRING))
+        return g_variant_new_string (g_value_get_string (value));
+      else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_BYTESTRING))
+        return g_variant_new_bytestring (g_value_get_string (value));
+      else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_OBJECT_PATH))
+        return g_variant_new_object_path (g_value_get_string (value));
+      else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_SIGNATURE))
+        return g_variant_new_signature (g_value_get_string (value));
+    }
+
+  else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
+    {
+      if (g_value_get_boxed (value) == NULL)
+        return NULL;
+      return g_variant_new_strv ((const char **) g_value_get_boxed (value),
+                                 -1);
+    }
+
+  else if (G_VALUE_HOLDS_ENUM (value))
+    {
+      GEnumValue *enumval;
+      GEnumClass *eclass;
+
+      /* GParamSpecEnum holds a ref on the class so we just peek... */
+      eclass = g_type_class_peek (G_VALUE_TYPE (value));
+      enumval = g_enum_get_value (eclass, g_value_get_enum (value));
+
+      if (enumval)
+        return g_variant_new_string (enumval->value_nick);
+      else
+        return NULL;
+    }
+
+  else if (G_VALUE_HOLDS_FLAGS (value))
+    {
+      GVariantBuilder builder;
+      GFlagsValue *flagsval;
+      GFlagsClass *fclass;
+      guint flags;
+
+      fclass = g_type_class_peek (G_VALUE_TYPE (value));
+      flags = g_value_get_flags (value);
+
+      g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+      while (flags)
+        {
+          flagsval = g_flags_get_first_value (fclass, flags);
+
+          if (flagsval == NULL)
+            {
+              g_variant_builder_clear (&builder);
+              return NULL;
+            }
+
+          g_variant_builder_add (&builder, "s", flagsval->value_nick);
+          flags &= ~flagsval->value;
+        }
+
+      return g_variant_builder_end (&builder);
+    }
+
+  type_string = g_variant_type_dup_string (expected_type);
+  g_critical ("No GSettings bind handler for type \"%s\".", type_string);
+  g_free (type_string);
+
+  return NULL;
+}
+
+gboolean
+g_settings_get_mapping (GValue   *value,
+                        GVariant *variant,
+                        gpointer  user_data)
+{
+  if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN))
+    {
+      if (!G_VALUE_HOLDS_BOOLEAN (value))
+        return FALSE;
+      g_value_set_boolean (value, g_variant_get_boolean (variant));
+      return TRUE;
+    }
+
+  else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTE))
+    {
+      if (G_VALUE_HOLDS_UCHAR (value))
+        g_value_set_uchar (value, g_variant_get_byte (variant));
+      else if (G_VALUE_HOLDS_CHAR (value))
+        g_value_set_schar (value, (gint8)g_variant_get_byte (variant));
+      else
+        return FALSE;
+      return TRUE;
+    }
+
+  else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT16)  ||
+           g_variant_is_of_type (variant, G_VARIANT_TYPE_INT32)  ||
+           g_variant_is_of_type (variant, G_VARIANT_TYPE_INT64))
+    return g_settings_get_mapping_int (value, variant);
+
+  else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_DOUBLE))
+    return g_settings_get_mapping_float (value, variant);
+
+  else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT16) ||
+           g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32) ||
+           g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT64) ||
+           g_variant_is_of_type (variant, G_VARIANT_TYPE_HANDLE))
+    return g_settings_get_mapping_unsigned_int (value, variant);
+
+  else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)      ||
+           g_variant_is_of_type (variant, G_VARIANT_TYPE_OBJECT_PATH) ||
+           g_variant_is_of_type (variant, G_VARIANT_TYPE_SIGNATURE))
+    {
+      if (G_VALUE_HOLDS_STRING (value))
+        {
+          g_value_set_string (value, g_variant_get_string (variant, NULL));
+          return TRUE;
+        }
+
+      else if (G_VALUE_HOLDS_ENUM (value))
+        {
+          GEnumClass *eclass;
+          GEnumValue *evalue;
+          const char *nick;
+
+          /* GParamSpecEnum holds a ref on the class so we just peek... */
+          eclass = g_type_class_peek (G_VALUE_TYPE (value));
+          nick = g_variant_get_string (variant, NULL);
+          evalue = g_enum_get_value_by_nick (eclass, nick);
+
+          if (evalue)
+            {
+             g_value_set_enum (value, evalue->value);
+             return TRUE;
+            }
+
+          g_warning ("Unable to look up enum nick ‘%s’ via GType", nick);
+          return FALSE;
+        }
+    }
+  else if (g_variant_is_of_type (variant, G_VARIANT_TYPE ("as")))
+    {
+      if (G_VALUE_HOLDS (value, G_TYPE_STRV))
+        {
+          g_value_take_boxed (value, g_variant_dup_strv (variant, NULL));
+          return TRUE;
+        }
+
+      else if (G_VALUE_HOLDS_FLAGS (value))
+        {
+          GFlagsClass *fclass;
+          GFlagsValue *fvalue;
+          const char *nick;
+          GVariantIter iter;
+          guint flags = 0;
+
+          fclass = g_type_class_peek (G_VALUE_TYPE (value));
+
+          g_variant_iter_init (&iter, variant);
+          while (g_variant_iter_next (&iter, "&s", &nick))
+            {
+              fvalue = g_flags_get_value_by_nick (fclass, nick);
+
+              if (fvalue)
+                flags |= fvalue->value;
+
+              else
+                {
+                  g_warning ("Unable to lookup flags nick '%s' via GType",
+                             nick);
+                  return FALSE;
+                }
+            }
+
+          g_value_set_flags (value, flags);
+          return TRUE;
+        }
+    }
+  else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTESTRING))
+    {
+      g_value_set_string (value, g_variant_get_bytestring (variant));
+      return TRUE;
+    }
+
+  g_critical ("No GSettings bind handler for type \"%s\".",
+              g_variant_get_type_string (variant));
+
+  return FALSE;
+}
+
+gboolean
+g_settings_mapping_is_compatible (GType               gvalue_type,
+                                  const GVariantType *variant_type)
+{
+  gboolean ok = FALSE;
+
+  if (gvalue_type == G_TYPE_BOOLEAN)
+    ok = g_variant_type_equal (variant_type, G_VARIANT_TYPE_BOOLEAN);
+  else if (gvalue_type == G_TYPE_CHAR  ||
+           gvalue_type == G_TYPE_UCHAR)
+    ok = g_variant_type_equal (variant_type, G_VARIANT_TYPE_BYTE);
+  else if (gvalue_type == G_TYPE_INT    ||
+           gvalue_type == G_TYPE_UINT   ||
+           gvalue_type == G_TYPE_INT64  ||
+           gvalue_type == G_TYPE_UINT64 ||
+           gvalue_type == G_TYPE_DOUBLE)
+    ok = (g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT16)  ||
+          g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT16) ||
+          g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT32)  ||
+          g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT32) ||
+          g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT64)  ||
+          g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT64) ||
+          g_variant_type_equal (variant_type, G_VARIANT_TYPE_HANDLE) ||
+          g_variant_type_equal (variant_type, G_VARIANT_TYPE_DOUBLE));
+  else if (gvalue_type == G_TYPE_STRING)
+    ok = (g_variant_type_equal (variant_type, G_VARIANT_TYPE_STRING)      ||
+          g_variant_type_equal (variant_type, G_VARIANT_TYPE ("ay")) ||
+          g_variant_type_equal (variant_type, G_VARIANT_TYPE_OBJECT_PATH) ||
+          g_variant_type_equal (variant_type, G_VARIANT_TYPE_SIGNATURE));
+  else if (gvalue_type == G_TYPE_STRV)
+    ok = g_variant_type_equal (variant_type, G_VARIANT_TYPE ("as"));
+  else if (G_TYPE_IS_ENUM (gvalue_type))
+    ok = g_variant_type_equal (variant_type, G_VARIANT_TYPE_STRING);
+  else if (G_TYPE_IS_FLAGS (gvalue_type))
+    ok = g_variant_type_equal (variant_type, G_VARIANT_TYPE ("as"));
+
+  return ok;
+}
diff --git a/src/libide/core/gsettings-mapping.h b/src/libide/core/gsettings-mapping.h
new file mode 100644
index 000000000..8a26684bb
--- /dev/null
+++ b/src/libide/core/gsettings-mapping.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2010 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vincent Untz <vuntz gnome org>
+ */
+
+#ifndef __G_SETTINGS_MAPPING_H__
+#define __G_SETTINGS_MAPPING_H__
+
+#include <glib-object.h>
+
+GVariant *              g_settings_set_mapping                          (const GValue       *value,
+                                                                         const GVariantType *expected_type,
+                                                                         gpointer            user_data);
+gboolean                g_settings_get_mapping                          (GValue             *value,
+                                                                         GVariant           *variant,
+                                                                         gpointer            user_data);
+gboolean                g_settings_mapping_is_compatible                (GType               gvalue_type,
+                                                                         const GVariantType *variant_type);
+
+#endif /* __G_SETTINGS_MAPPING_H__ */
diff --git a/src/libide/core/ide-action-muxer.c b/src/libide/core/ide-action-muxer.c
index 0ffd5a6b2..b3e60b8b8 100644
--- a/src/libide/core/ide-action-muxer.c
+++ b/src/libide/core/ide-action-muxer.c
@@ -18,18 +18,23 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#define G_LOG_DOMAIN "ide-action-muxer"
-
 #include "config.h"
 
+#include <gtk/gtk.h>
+
+#include "gsettings-mapping.h"
 #include "ide-action-muxer.h"
-#include "ide-macros.h"
 
 struct _IdeActionMuxer
 {
-  GObject    parent_instance;
-  GPtrArray *action_groups;
-  guint      n_recurse;
+  GObject          parent_instance;
+  GPtrArray       *action_groups;
+  const IdeAction *actions;
+  GtkBitset       *actions_disabled;
+  GHashTable      *pspec_name_to_action;
+  gpointer         instance;
+  gulong           instance_notify_handler;
+  guint            n_recurse;
 };
 
 typedef struct
@@ -77,14 +82,43 @@ prefixed_action_group_ref (PrefixedActionGroup *pag)
   return g_rc_box_acquire (pag);
 }
 
+static GVariant *
+get_property_state (gpointer            instance,
+                    GParamSpec         *pspec,
+                    const GVariantType *state_type)
+{
+  GValue value = G_VALUE_INIT;
+  GVariant *ret;
+
+  g_assert (G_IS_OBJECT (instance));
+  g_assert (pspec != NULL);
+  g_assert (state_type != NULL);
+
+  g_value_init (&value, pspec->value_type);
+  g_object_get_property (instance, pspec->name, &value);
+  ret = g_settings_set_mapping (&value, state_type, NULL);
+  g_value_unset (&value);
+
+  return g_variant_ref_sink (ret);
+}
+
 static void
 ide_action_muxer_dispose (GObject *object)
 {
   IdeActionMuxer *self = (IdeActionMuxer *)object;
 
+  if (self->instance != NULL)
+    {
+      g_clear_signal_handler (&self->instance_notify_handler, self->instance);
+      g_clear_weak_pointer (&self->instance);
+    }
+
   if (self->action_groups->len > 0)
     g_ptr_array_remove_range (self->action_groups, 0, self->action_groups->len);
 
+  self->actions = NULL;
+  g_clear_pointer (&self->actions_disabled, gtk_bitset_unref);
+
   G_OBJECT_CLASS (ide_action_muxer_parent_class)->finalize (object);
 }
 
@@ -111,13 +145,12 @@ static void
 ide_action_muxer_init (IdeActionMuxer *self)
 {
   self->action_groups = g_ptr_array_new_with_free_func ((GDestroyNotify)prefixed_action_group_drop);
+  self->actions_disabled = gtk_bitset_new_empty ();
 }
 
 IdeActionMuxer *
 ide_action_muxer_new (void)
 {
-  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
-
   return g_object_new (IDE_TYPE_ACTION_MUXER, NULL);
 }
 
@@ -135,7 +168,6 @@ ide_action_muxer_list_groups (IdeActionMuxer *self)
 {
   GArray *ar;
 
-  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
   g_return_val_if_fail (IDE_IS_ACTION_MUXER (self), NULL);
 
   ar = g_array_new (TRUE, FALSE, sizeof (char *));
@@ -163,7 +195,6 @@ ide_action_muxer_action_group_action_added_cb (GActionGroup        *action_group
 {
   g_autofree char *full_name = NULL;
 
-  g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (G_IS_ACTION_GROUP (action_group));
   g_assert (action_name != NULL);
   g_assert (pag != NULL);
@@ -181,7 +212,6 @@ ide_action_muxer_action_group_action_removed_cb (GActionGroup        *action_gro
 {
   g_autofree char *full_name = NULL;
 
-  g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (G_IS_ACTION_GROUP (action_group));
   g_assert (action_name != NULL);
   g_assert (pag != NULL);
@@ -200,7 +230,6 @@ ide_action_muxer_action_group_action_enabled_changed_cb (GActionGroup        *ac
 {
   g_autofree char *full_name = NULL;
 
-  g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (G_IS_ACTION_GROUP (action_group));
   g_assert (action_name != NULL);
   g_assert (pag != NULL);
@@ -219,7 +248,6 @@ ide_action_muxer_action_group_action_state_changed_cb (GActionGroup        *acti
 {
   g_autofree char *full_name = NULL;
 
-  g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (G_IS_ACTION_GROUP (action_group));
   g_assert (action_name != NULL);
   g_assert (pag != NULL);
@@ -237,7 +265,6 @@ ide_action_muxer_insert_action_group (IdeActionMuxer *self,
 {
   g_autofree char *prefix_dot = NULL;
 
-  g_return_if_fail (IDE_IS_MAIN_THREAD ());
   g_return_if_fail (IDE_IS_ACTION_MUXER (self));
   g_return_if_fail (self->n_recurse == 0);
   g_return_if_fail (prefix != NULL);
@@ -343,7 +370,6 @@ void
 ide_action_muxer_remove_action_group (IdeActionMuxer *self,
                                       const char     *prefix)
 {
-  g_return_if_fail (IDE_IS_MAIN_THREAD ());
   g_return_if_fail (IDE_IS_ACTION_MUXER (self));
   g_return_if_fail (prefix != NULL);
 
@@ -390,6 +416,12 @@ ide_action_muxer_has_action (GActionGroup *group,
 {
   IdeActionMuxer *self = IDE_ACTION_MUXER (group);
 
+  for (const IdeAction *iter = self->actions; iter; iter = iter->next)
+    {
+      if (g_strcmp0 (iter->name, action_name) == 0)
+        return TRUE;
+    }
+
   for (guint i = 0; i < self->action_groups->len; i++)
     {
       const PrefixedActionGroup *pag = g_ptr_array_index (self->action_groups, i);
@@ -412,6 +444,12 @@ ide_action_muxer_list_actions (GActionGroup *group)
   IdeActionMuxer *self = IDE_ACTION_MUXER (group);
   GArray *ar = g_array_new (TRUE, FALSE, sizeof (char *));
 
+  for (const IdeAction *iter = self->actions; iter; iter = iter->next)
+    {
+      char *name = g_strdup (iter->name);
+      g_array_append_val (ar, name);
+    }
+
   for (guint i = 0; i < self->action_groups->len; i++)
     {
       const PrefixedActionGroup *pag = g_ptr_array_index (self->action_groups, i);
@@ -433,6 +471,12 @@ ide_action_muxer_get_action_enabled (GActionGroup *group,
 {
   IdeActionMuxer *self = IDE_ACTION_MUXER (group);
 
+  for (const IdeAction *iter = self->actions; iter; iter = iter->next)
+    {
+      if (g_strcmp0 (action_name, iter->name) == 0)
+        return !gtk_bitset_contains (self->actions_disabled, iter->position);
+    }
+
   for (guint i = 0; i < self->action_groups->len; i++)
     {
       const PrefixedActionGroup *pag = g_ptr_array_index (self->action_groups, i);
@@ -455,6 +499,16 @@ ide_action_muxer_get_action_state (GActionGroup *group,
 {
   IdeActionMuxer *self = IDE_ACTION_MUXER (group);
 
+  for (const IdeAction *iter = self->actions; iter; iter = iter->next)
+    {
+      if (g_strcmp0 (iter->name, action_name) == 0)
+        {
+          if (iter->pspec != NULL && self->instance != NULL)
+            return get_property_state (self->instance, iter->pspec, iter->state_type);
+          return NULL;
+        }
+    }
+
   for (guint i = 0; i < self->action_groups->len; i++)
     {
       const PrefixedActionGroup *pag = g_ptr_array_index (self->action_groups, i);
@@ -477,6 +531,38 @@ ide_action_muxer_get_action_state_hint (GActionGroup *group,
 {
   IdeActionMuxer *self = IDE_ACTION_MUXER (group);
 
+  for (const IdeAction *iter = self->actions; iter; iter = iter->next)
+    {
+      if (g_strcmp0 (iter->name, action_name) == 0)
+        {
+          if (iter->pspec != NULL)
+            {
+              if (iter->pspec->value_type == G_TYPE_INT)
+                {
+                  GParamSpecInt *pspec = (GParamSpecInt *)iter->pspec;
+                  return g_variant_new ("(ii)", pspec->minimum, pspec->maximum);
+                }
+              else if (iter->pspec->value_type == G_TYPE_UINT)
+                {
+                  GParamSpecUInt *pspec = (GParamSpecUInt *)iter->pspec;
+                  return g_variant_new ("(uu)", pspec->minimum, pspec->maximum);
+                }
+              else if (iter->pspec->value_type == G_TYPE_FLOAT)
+                {
+                  GParamSpecFloat *pspec = (GParamSpecFloat *)iter->pspec;
+                  return g_variant_new ("(dd)", (double)pspec->minimum, (double)pspec->maximum);
+                }
+              else if (iter->pspec->value_type == G_TYPE_DOUBLE)
+                {
+                  GParamSpecDouble *pspec = (GParamSpecDouble *)iter->pspec;
+                  return g_variant_new ("(dd)", pspec->minimum, pspec->maximum);
+                }
+            }
+
+          return NULL;
+        }
+    }
+
   for (guint i = 0; i < self->action_groups->len; i++)
     {
       const PrefixedActionGroup *pag = g_ptr_array_index (self->action_groups, i);
@@ -500,6 +586,23 @@ ide_action_muxer_change_action_state (GActionGroup *group,
 {
   IdeActionMuxer *self = IDE_ACTION_MUXER (group);
 
+  for (const IdeAction *iter = self->actions; iter; iter = iter->next)
+    {
+      if (g_strcmp0 (iter->name, action_name) == 0)
+        {
+          if (iter->pspec != NULL && self->instance != NULL)
+            {
+              GValue gvalue = G_VALUE_INIT;
+              g_value_init (&gvalue, iter->pspec->value_type);
+              g_settings_get_mapping (&gvalue, value, NULL);
+              g_object_set_property (self->instance, iter->pspec->name, &gvalue);
+              g_value_unset (&gvalue);
+            }
+
+          return;
+        }
+    }
+
   for (guint i = 0; i < self->action_groups->len; i++)
     {
       const PrefixedActionGroup *pag = g_ptr_array_index (self->action_groups, i);
@@ -523,6 +626,12 @@ ide_action_muxer_get_action_state_type (GActionGroup *group,
 {
   IdeActionMuxer *self = IDE_ACTION_MUXER (group);
 
+  for (const IdeAction *iter = self->actions; iter; iter = iter->next)
+    {
+      if (g_strcmp0 (iter->name, action_name) == 0)
+        return iter->state_type;
+    }
+
   for (guint i = 0; i < self->action_groups->len; i++)
     {
       const PrefixedActionGroup *pag = g_ptr_array_index (self->action_groups, i);
@@ -546,6 +655,39 @@ ide_action_muxer_activate_action (GActionGroup *group,
 {
   IdeActionMuxer *self = IDE_ACTION_MUXER (group);
 
+  for (const IdeAction *iter = self->actions; iter; iter = iter->next)
+    {
+      if (g_strcmp0 (iter->name, action_name) == 0)
+        {
+          if (iter->pspec != NULL)
+            {
+              if (iter->pspec->value_type == G_TYPE_BOOLEAN)
+                {
+                  gboolean value;
+
+                  g_return_if_fail (parameter == NULL);
+
+                  g_object_get (self->instance, iter->pspec->name, &value, NULL);
+                  value = !value;
+                  g_object_set (self->instance, iter->pspec->name, value, NULL);
+                }
+              else
+                {
+                  g_return_if_fail (parameter != NULL && g_variant_is_of_type (parameter, iter->state_type));
+
+                  ide_action_muxer_change_action_state (group, action_name, parameter);
+                }
+
+            }
+          else
+            {
+              iter->activate (self->instance, iter->name, parameter);
+            }
+
+          return;
+        }
+    }
+
   for (guint i = 0; i < self->action_groups->len; i++)
     {
       const PrefixedActionGroup *pag = g_ptr_array_index (self->action_groups, i);
@@ -598,3 +740,129 @@ action_group_iface_init (GActionGroupInterface *iface)
   iface->change_action_state = ide_action_muxer_change_action_state;
   iface->activate_action = ide_action_muxer_activate_action;
 }
+
+void
+ide_action_muxer_remove_all (IdeActionMuxer *self)
+{
+  g_auto(GStrv) action_groups = NULL;
+
+  g_return_if_fail (IDE_IS_ACTION_MUXER (self));
+
+  if ((action_groups = ide_action_muxer_list_actions (G_ACTION_GROUP (self))))
+    {
+      for (guint i = 0; action_groups[i]; i++)
+        ide_action_muxer_remove_action_group (self, action_groups[i]);
+    }
+}
+
+void
+ide_action_muxer_set_enabled (IdeActionMuxer  *self,
+                              const IdeAction *action,
+                              gboolean         enabled)
+{
+  gboolean disabled = !enabled;
+
+  g_return_if_fail (IDE_IS_ACTION_MUXER (self));
+  g_return_if_fail (action != NULL);
+
+  if (disabled != gtk_bitset_contains (self->actions_disabled, action->position))
+    {
+      if (disabled)
+        gtk_bitset_add (self->actions_disabled, action->position);
+      else
+        gtk_bitset_remove (self->actions_disabled, action->position);
+
+      g_action_group_action_enabled_changed (G_ACTION_GROUP (self), action->name, !disabled);
+    }
+}
+
+static void
+ide_action_muxer_property_action_notify_cb (IdeActionMuxer *self,
+                                            GParamSpec     *pspec,
+                                            gpointer        instance)
+{
+  g_autoptr(GVariant) state = NULL;
+  const IdeAction *action;
+
+  g_assert (IDE_IS_ACTION_MUXER (self));
+  g_assert (pspec != NULL);
+  g_assert (G_IS_OBJECT (instance));
+
+  if (!(action = g_hash_table_lookup (self->pspec_name_to_action, pspec->name)))
+    return;
+
+  state = get_property_state (instance, action->pspec, action->state_type);
+
+  g_action_group_action_state_changed (G_ACTION_GROUP (self), action->name, state);
+}
+
+static void
+ide_action_muxer_add_property_action (IdeActionMuxer  *self,
+                                      gpointer         instance,
+                                      const IdeAction *action)
+{
+  g_assert (IDE_IS_ACTION_MUXER (self));
+  g_assert (G_IS_OBJECT (instance));
+  g_assert (action != NULL);
+  g_assert (action->pspec != NULL);
+  g_assert (g_type_is_a (G_OBJECT_TYPE (instance), action->owner));
+
+  if (self->pspec_name_to_action == NULL)
+    self->pspec_name_to_action = g_hash_table_new (NULL, NULL);
+
+  g_hash_table_insert (self->pspec_name_to_action,
+                       (gpointer)action->pspec->name,
+                       (gpointer)action);
+
+  if (self->instance_notify_handler == 0)
+    self->instance_notify_handler =
+      g_signal_connect_object (instance,
+                               "notify",
+                               G_CALLBACK (ide_action_muxer_property_action_notify_cb),
+                               self,
+                               G_CONNECT_SWAPPED);
+
+  g_action_group_action_added (G_ACTION_GROUP (self), action->name);
+}
+
+static void
+ide_action_muxer_add_action (IdeActionMuxer  *self,
+                             gpointer         instance,
+                             const IdeAction *action)
+{
+  g_assert (IDE_IS_ACTION_MUXER (self));
+  g_assert (G_IS_OBJECT (instance));
+  g_assert (action != NULL);
+  g_assert (g_type_is_a (G_OBJECT_TYPE (instance), action->owner));
+
+  g_action_group_action_added (G_ACTION_GROUP (self), action->name);
+}
+
+void
+ide_action_muxer_connect_actions (IdeActionMuxer  *self,
+                                  gpointer         instance,
+                                  const IdeAction *actions)
+{
+  g_return_if_fail (IDE_IS_ACTION_MUXER (self));
+  g_return_if_fail (G_IS_OBJECT (instance));
+  g_return_if_fail (self->instance == NULL);
+
+  if (actions == NULL)
+    return;
+
+  g_set_weak_pointer (&self->instance, instance);
+
+  self->actions = actions;
+
+  for (const IdeAction *iter = actions; iter; iter = iter->next)
+    {
+      g_assert (iter->next == NULL ||
+                iter->position == iter->next->position + 1);
+      g_assert (iter->pspec != NULL || iter->activate != NULL);
+
+      if (iter->pspec != NULL)
+        ide_action_muxer_add_property_action (self, instance, iter);
+      else
+        ide_action_muxer_add_action (self, instance, iter);
+    }
+}
diff --git a/src/libide/core/ide-action-muxer.h b/src/libide/core/ide-action-muxer.h
index 0f95ce538..b7a258488 100644
--- a/src/libide/core/ide-action-muxer.h
+++ b/src/libide/core/ide-action-muxer.h
@@ -20,12 +20,32 @@
 
 #pragma once
 
+#if !defined (IDE_CORE_INSIDE) && !defined (IDE_CORE_COMPILATION)
+# error "Only <libide-core.h> can be included directly."
+#endif
+
 #include <gio/gio.h>
 
 #include "ide-version-macros.h"
 
 G_BEGIN_DECLS
 
+typedef void (*IdeActionActivateFunc) (gpointer    instance,
+                                       const char *action_name,
+                                       GVariant   *param);
+
+typedef struct _IdeAction
+{
+  const struct _IdeAction *next;
+  const char                *name;
+  GType                      owner;
+  const GVariantType        *parameter_type;
+  const GVariantType        *state_type;
+  GParamSpec                *pspec;
+  IdeActionActivateFunc    activate;
+  guint                      position;
+} IdeAction;
+
 #define IDE_TYPE_ACTION_MUXER (ide_action_muxer_get_type())
 
 IDE_AVAILABLE_IN_ALL
@@ -34,16 +54,26 @@ G_DECLARE_FINAL_TYPE (IdeActionMuxer, ide_action_muxer, IDE, ACTION_MUXER, GObje
 IDE_AVAILABLE_IN_ALL
 IdeActionMuxer  *ide_action_muxer_new                 (void);
 IDE_AVAILABLE_IN_ALL
-void             ide_action_muxer_insert_action_group (IdeActionMuxer *self,
-                                                       const char     *prefix,
-                                                       GActionGroup   *action_group);
+void             ide_action_muxer_remove_all          (IdeActionMuxer  *self);
+IDE_AVAILABLE_IN_ALL
+void             ide_action_muxer_insert_action_group (IdeActionMuxer  *self,
+                                                       const char      *prefix,
+                                                       GActionGroup    *action_group);
+IDE_AVAILABLE_IN_ALL
+void             ide_action_muxer_remove_action_group (IdeActionMuxer  *self,
+                                                       const char      *prefix);
+IDE_AVAILABLE_IN_ALL
+char           **ide_action_muxer_list_groups         (IdeActionMuxer  *self);
 IDE_AVAILABLE_IN_ALL
-void             ide_action_muxer_remove_action_group (IdeActionMuxer *self,
-                                                       const char     *prefix);
+GActionGroup    *ide_action_muxer_get_action_group    (IdeActionMuxer  *self,
+                                                       const char      *prefix);
 IDE_AVAILABLE_IN_ALL
-char           **ide_action_muxer_list_groups         (IdeActionMuxer *self);
+void             ide_action_muxer_set_enabled         (IdeActionMuxer  *self,
+                                                       const IdeAction *action,
+                                                       gboolean         enabled);
 IDE_AVAILABLE_IN_ALL
-GActionGroup    *ide_action_muxer_get_action_group    (IdeActionMuxer *self,
-                                                       const char     *prefix);
+void             ide_action_muxer_connect_actions     (IdeActionMuxer  *self,
+                                                       gpointer         instance,
+                                                       const IdeAction *actions);
 
 G_END_DECLS
diff --git a/src/libide/core/meson.build b/src/libide/core/meson.build
index f4859b3bc..385b191f9 100644
--- a/src/libide/core/meson.build
+++ b/src/libide/core/meson.build
@@ -74,6 +74,7 @@ libide_core_public_headers = [
 ]
 
 libide_core_private_headers = [
+  'gsettings-mapping.h',
   'ide-layered-settings-private.h',
   'ide-transfer-manager-private.h',
 ]
@@ -103,6 +104,7 @@ libide_core_public_sources = [
 ]
 
 libide_core_private_sources = [
+  'gsettings-mapping.c',
   'ide-layered-settings.c',
 ]
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]