summaryrefslogtreecommitdiffstats
path: root/libgimpconfig/gimpconfig-utils.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libgimpconfig/gimpconfig-utils.c482
1 files changed, 482 insertions, 0 deletions
diff --git a/libgimpconfig/gimpconfig-utils.c b/libgimpconfig/gimpconfig-utils.c
new file mode 100644
index 0000000..7cef219
--- /dev/null
+++ b/libgimpconfig/gimpconfig-utils.c
@@ -0,0 +1,482 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * Utility functions for GimpConfig.
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ *
+ * 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 3 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
+ * Library 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
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpconfigtypes.h"
+
+#include "gimpconfigwriter.h"
+#include "gimpconfig-iface.h"
+#include "gimpconfig-params.h"
+#include "gimpconfig-utils.h"
+
+
+/**
+ * SECTION: gimpconfig-utils
+ * @title: GimpConfig-utils
+ * @short_description: Miscellaneous utility functions for libgimpconfig.
+ *
+ * Miscellaneous utility functions for libgimpconfig.
+ **/
+
+
+static gboolean
+gimp_config_diff_property (GObject *a,
+ GObject *b,
+ GParamSpec *prop_spec)
+{
+ GValue a_value = G_VALUE_INIT;
+ GValue b_value = G_VALUE_INIT;
+ gboolean retval = FALSE;
+
+ g_value_init (&a_value, prop_spec->value_type);
+ g_value_init (&b_value, prop_spec->value_type);
+
+ g_object_get_property (a, prop_spec->name, &a_value);
+ g_object_get_property (b, prop_spec->name, &b_value);
+
+ if (g_param_values_cmp (prop_spec, &a_value, &b_value))
+ {
+ if ((prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE) &&
+ G_IS_PARAM_SPEC_OBJECT (prop_spec) &&
+ g_type_interface_peek (g_type_class_peek (prop_spec->value_type),
+ GIMP_TYPE_CONFIG))
+ {
+ if (! gimp_config_is_equal_to (g_value_get_object (&a_value),
+ g_value_get_object (&b_value)))
+ {
+ retval = TRUE;
+ }
+ }
+ else
+ {
+ retval = TRUE;
+ }
+ }
+
+ g_value_unset (&a_value);
+ g_value_unset (&b_value);
+
+ return retval;
+}
+
+static GList *
+gimp_config_diff_same (GObject *a,
+ GObject *b,
+ GParamFlags flags)
+{
+ GParamSpec **param_specs;
+ guint n_param_specs;
+ gint i;
+ GList *list = NULL;
+
+ param_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a),
+ &n_param_specs);
+
+ for (i = 0; i < n_param_specs; i++)
+ {
+ GParamSpec *prop_spec = param_specs[i];
+
+ if (! flags || ((prop_spec->flags & flags) == flags))
+ {
+ if (gimp_config_diff_property (a, b, prop_spec))
+ list = g_list_prepend (list, prop_spec);
+ }
+ }
+
+ g_free (param_specs);
+
+ return list;
+}
+
+static GList *
+gimp_config_diff_other (GObject *a,
+ GObject *b,
+ GParamFlags flags)
+{
+ GParamSpec **param_specs;
+ guint n_param_specs;
+ gint i;
+ GList *list = NULL;
+
+ param_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a),
+ &n_param_specs);
+
+ for (i = 0; i < n_param_specs; i++)
+ {
+ GParamSpec *a_spec = param_specs[i];
+ GParamSpec *b_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (b),
+ a_spec->name);
+
+ if (b_spec &&
+ (a_spec->value_type == b_spec->value_type) &&
+ (! flags || (a_spec->flags & b_spec->flags & flags) == flags))
+ {
+ if (gimp_config_diff_property (a, b, b_spec))
+ list = g_list_prepend (list, b_spec);
+ }
+ }
+
+ g_free (param_specs);
+
+ return list;
+}
+
+
+/**
+ * gimp_config_diff:
+ * @a: a #GObject
+ * @b: another #GObject object
+ * @flags: a mask of GParamFlags
+ *
+ * Compares all properties of @a and @b that have all @flags set. If
+ * @flags is 0, all properties are compared.
+ *
+ * If the two objects are not of the same type, only properties that
+ * exist in both object classes and are of the same value_type are
+ * compared.
+ *
+ * Return value: a GList of differing GParamSpecs.
+ *
+ * Since: 2.4
+ **/
+GList *
+gimp_config_diff (GObject *a,
+ GObject *b,
+ GParamFlags flags)
+{
+ GList *diff;
+
+ g_return_val_if_fail (G_IS_OBJECT (a), NULL);
+ g_return_val_if_fail (G_IS_OBJECT (b), NULL);
+
+ if (G_TYPE_FROM_INSTANCE (a) == G_TYPE_FROM_INSTANCE (b))
+ diff = gimp_config_diff_same (a, b, flags);
+ else
+ diff = gimp_config_diff_other (a, b, flags);
+
+ return g_list_reverse (diff);
+}
+
+/**
+ * gimp_config_sync:
+ * @src: a #GObject
+ * @dest: another #GObject
+ * @flags: a mask of GParamFlags
+ *
+ * Compares all read- and write-able properties from @src and @dest
+ * that have all @flags set. Differing values are then copied from
+ * @src to @dest. If @flags is 0, all differing read/write properties.
+ *
+ * Properties marked as "construct-only" are not touched.
+ *
+ * If the two objects are not of the same type, only properties that
+ * exist in both object classes and are of the same value_type are
+ * synchronized
+ *
+ * Return value: %TRUE if @dest was modified, %FALSE otherwise
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_sync (GObject *src,
+ GObject *dest,
+ GParamFlags flags)
+{
+ GList *diff;
+ GList *list;
+
+ g_return_val_if_fail (G_IS_OBJECT (src), FALSE);
+ g_return_val_if_fail (G_IS_OBJECT (dest), FALSE);
+
+ /* we use the internal versions here for a number of reasons:
+ * - it saves a g_list_reverse()
+ * - it avoids duplicated parameter checks
+ */
+ if (G_TYPE_FROM_INSTANCE (src) == G_TYPE_FROM_INSTANCE (dest))
+ diff = gimp_config_diff_same (src, dest, (flags | G_PARAM_READWRITE));
+ else
+ diff = gimp_config_diff_other (src, dest, flags);
+
+ if (!diff)
+ return FALSE;
+
+ g_object_freeze_notify (G_OBJECT (dest));
+
+ for (list = diff; list; list = list->next)
+ {
+ GParamSpec *prop_spec = list->data;
+
+ if (! (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY))
+ {
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, prop_spec->value_type);
+
+ g_object_get_property (src, prop_spec->name, &value);
+ g_object_set_property (dest, prop_spec->name, &value);
+
+ g_value_unset (&value);
+ }
+ }
+
+ g_object_thaw_notify (G_OBJECT (dest));
+
+ g_list_free (diff);
+
+ return TRUE;
+}
+
+/**
+ * gimp_config_reset_properties:
+ * @object: a #GObject
+ *
+ * Resets all writable properties of @object to the default values as
+ * defined in their #GParamSpec. Properties marked as "construct-only"
+ * are not touched.
+ *
+ * If you want to reset a #GimpConfig object, please use gimp_config_reset().
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_reset_properties (GObject *object)
+{
+ GObjectClass *klass;
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+
+ g_return_if_fail (G_IS_OBJECT (object));
+
+ klass = G_OBJECT_GET_CLASS (object);
+
+ property_specs = g_object_class_list_properties (klass, &n_property_specs);
+ if (!property_specs)
+ return;
+
+ g_object_freeze_notify (object);
+
+ for (i = 0; i < n_property_specs; i++)
+ {
+ GParamSpec *prop_spec;
+ GValue value = G_VALUE_INIT;
+
+ prop_spec = property_specs[i];
+
+ if ((prop_spec->flags & G_PARAM_WRITABLE) &&
+ ! (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY))
+ {
+ if (G_IS_PARAM_SPEC_OBJECT (prop_spec))
+ {
+ if ((prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE) &&
+ (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE) &&
+ g_type_interface_peek (g_type_class_peek (prop_spec->value_type),
+ GIMP_TYPE_CONFIG))
+ {
+ g_value_init (&value, prop_spec->value_type);
+
+ g_object_get_property (object, prop_spec->name, &value);
+
+ gimp_config_reset (g_value_get_object (&value));
+
+ g_value_unset (&value);
+ }
+ }
+ else
+ {
+ GValue default_value = G_VALUE_INIT;
+
+ g_value_init (&default_value, prop_spec->value_type);
+ g_value_init (&value, prop_spec->value_type);
+
+ g_param_value_set_default (prop_spec, &default_value);
+ g_object_get_property (object, prop_spec->name, &value);
+
+ if (g_param_values_cmp (prop_spec, &default_value, &value))
+ {
+ g_object_set_property (object, prop_spec->name,
+ &default_value);
+ }
+
+ g_value_unset (&value);
+ g_value_unset (&default_value);
+ }
+ }
+ }
+
+ g_object_thaw_notify (object);
+
+ g_free (property_specs);
+}
+
+
+/**
+ * gimp_config_reset_property:
+ * @object: a #GObject
+ * @property_name: name of the property to reset
+ *
+ * Resets the property named @property_name to its default value. The
+ * property must be writable and must not be marked as "construct-only".
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_reset_property (GObject *object,
+ const gchar *property_name)
+{
+ GObjectClass *klass;
+ GParamSpec *prop_spec;
+
+ g_return_if_fail (G_IS_OBJECT (object));
+ g_return_if_fail (property_name != NULL);
+
+ klass = G_OBJECT_GET_CLASS (object);
+
+ prop_spec = g_object_class_find_property (klass, property_name);
+
+ if (!prop_spec)
+ return;
+
+ if ((prop_spec->flags & G_PARAM_WRITABLE) &&
+ ! (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY))
+ {
+ GValue value = G_VALUE_INIT;
+
+ if (G_IS_PARAM_SPEC_OBJECT (prop_spec))
+ {
+ if ((prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE) &&
+ (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE) &&
+ g_type_interface_peek (g_type_class_peek (prop_spec->value_type),
+ GIMP_TYPE_CONFIG))
+ {
+ g_value_init (&value, prop_spec->value_type);
+
+ g_object_get_property (object, prop_spec->name, &value);
+
+ gimp_config_reset (g_value_get_object (&value));
+
+ g_value_unset (&value);
+ }
+ }
+ else
+ {
+ g_value_init (&value, prop_spec->value_type);
+ g_param_value_set_default (prop_spec, &value);
+
+ g_object_set_property (object, prop_spec->name, &value);
+
+ g_value_unset (&value);
+ }
+ }
+}
+
+
+/*
+ * GimpConfig string utilities
+ */
+
+/**
+ * gimp_config_string_append_escaped:
+ * @string: pointer to a #GString
+ * @val: a string to append or %NULL
+ *
+ * Escapes and quotes @val and appends it to @string. The escape
+ * algorithm is different from the one used by g_strescape() since it
+ * leaves non-ASCII characters intact and thus preserves UTF-8
+ * strings. Only control characters and quotes are being escaped.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_string_append_escaped (GString *string,
+ const gchar *val)
+{
+ g_return_if_fail (string != NULL);
+
+ if (val)
+ {
+ const guchar *p;
+ gchar buf[4] = { '\\', 0, 0, 0 };
+ gint len;
+
+ g_string_append_c (string, '\"');
+
+ for (p = (const guchar *) val, len = 0; *p; p++)
+ {
+ if (*p < ' ' || *p == '\\' || *p == '\"')
+ {
+ g_string_append_len (string, val, len);
+
+ len = 2;
+ switch (*p)
+ {
+ case '\b':
+ buf[1] = 'b';
+ break;
+ case '\f':
+ buf[1] = 'f';
+ break;
+ case '\n':
+ buf[1] = 'n';
+ break;
+ case '\r':
+ buf[1] = 'r';
+ break;
+ case '\t':
+ buf[1] = 't';
+ break;
+ case '\\':
+ case '"':
+ buf[1] = *p;
+ break;
+
+ default:
+ len = 4;
+ buf[1] = '0' + (((*p) >> 6) & 07);
+ buf[2] = '0' + (((*p) >> 3) & 07);
+ buf[3] = '0' + ((*p) & 07);
+ break;
+ }
+
+ g_string_append_len (string, buf, len);
+
+ val = (const gchar *) p + 1;
+ len = 0;
+ }
+ else
+ {
+ len++;
+ }
+ }
+
+ g_string_append_len (string, val, len);
+ g_string_append_c (string, '\"');
+ }
+ else
+ {
+ g_string_append_len (string, "\"\"", 2);
+ }
+}