/* * mooutils-gobject.c * * Copyright (C) 2004-2010 by Yevgen Muntyan * * This file is part of medit. medit 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. * * You should have received a copy of the GNU Lesser General Public * License along with medit. If not, see . */ #include "mooutils/mooutils-gobject-private.h" #include "mooutils/mooclosure.h" #include "mooutils/mootype-macros.h" #include #include #include #include #include /*****************************************************************************/ /* GType type */ #define MOO_GTYPE_PEEK(val_) (val_)->data[0].v_pointer static void moo_gtype_value_init (G_GNUC_UNUSED GValue *value) { } static void moo_gtype_value_copy (const GValue *src, GValue *dest) { MOO_GTYPE_PEEK(dest) = MOO_GTYPE_PEEK(src); } static char* moo_gtype_collect_value (GValue *value, G_GNUC_UNUSED guint n_collect_values, GTypeCValue *collect_values, G_GNUC_UNUSED guint collect_flags) { MOO_GTYPE_PEEK(value) = collect_values->v_pointer; return NULL; } static char* moo_gtype_lcopy_value (const GValue *value, G_GNUC_UNUSED guint n_collect_values, GTypeCValue *collect_values, G_GNUC_UNUSED guint collect_flags) { GType *ptr = collect_values->v_pointer; *ptr = _moo_value_get_gtype (value); return NULL; } GType _moo_gtype_get_type (void) { static GType type = 0; if (G_UNLIKELY (!type)) { static GTypeValueTable val_table = { moo_gtype_value_init, NULL, moo_gtype_value_copy, NULL, (char*) "p", moo_gtype_collect_value, (char*) "p", moo_gtype_lcopy_value }; static GTypeInfo info = { /* interface types, classed types, instantiated types */ 0, /*class_size*/ NULL, /*base_init*/ NULL, /*base_finalize*/ NULL,/*class_init*/ NULL,/*class_finalize*/ NULL,/*class_data*/ 0,/*instance_size*/ 0,/*n_preallocs*/ NULL,/*instance_init*/ /* value handling */ &val_table }; static GTypeFundamentalInfo finfo = { 0 }; type = g_type_register_fundamental (g_type_fundamental_next (), "MooGType", &info, &finfo, 0); } return type; } static void _moo_value_set_gtype (GValue *value, GType v_gtype) { MOO_GTYPE_PEEK(value) = (gpointer) v_gtype; } GType _moo_value_get_gtype (const GValue *value) { return (GType) MOO_GTYPE_PEEK(value); } static void param_gtype_set_default (GParamSpec *pspec, GValue *value) { MooParamSpecGType *mspec = MOO_PARAM_SPEC_GTYPE (pspec); _moo_value_set_gtype (value, mspec->base); } static gboolean param_gtype_validate (GParamSpec *pspec, GValue *value) { MooParamSpecGType *mspec = MOO_PARAM_SPEC_GTYPE (pspec); GType t = _moo_value_get_gtype (value); gboolean changed = FALSE; if (G_TYPE_IS_DERIVABLE (mspec->base)) { if (!g_type_is_a (t, mspec->base)) { _moo_value_set_gtype (value, mspec->base); changed = TRUE; } } else if (!g_type_name (t)) { _moo_value_set_gtype (value, mspec->base); changed = TRUE; } return changed; } static int param_gtype_cmp (G_GNUC_UNUSED GParamSpec *pspec, const GValue *value1, const GValue *value2) { GType t1 = _moo_value_get_gtype (value1); GType t2 = _moo_value_get_gtype (value2); return t1 == t2 ? 0 : (t1 < t2 ? -1 : 1); } GType _moo_param_gtype_get_type (void) { static GType type = 0; if (G_UNLIKELY (!type)) { static GParamSpecTypeInfo info = { sizeof (MooParamSpecGType), /* instance_size */ 16, /* n_preallocs */ NULL, /* instance_init */ 0, /* value_type */ NULL, /* finalize */ param_gtype_set_default, /* value_set_default */ param_gtype_validate, /* value_validate */ param_gtype_cmp, /* values_cmp */ }; info.value_type = MOO_TYPE_GTYPE; type = g_param_type_register_static ("MooParamGType", &info); } return type; } #if 0 GParamSpec* _moo_param_spec_gtype (const char *name, const char *nick, const char *blurb, GType base, GParamFlags flags) { MooParamSpecGType *pspec; g_return_val_if_fail (g_type_name (base) != NULL, NULL); pspec = g_param_spec_internal (MOO_TYPE_PARAM_GTYPE, name, nick, blurb, flags); pspec->base = base; return G_PARAM_SPEC (pspec); } #endif /*****************************************************************************/ /* Converting values forth and back */ static char * flags_to_string (int flags) { if (flags) return g_strdup_printf ("%d", flags); else return g_strdup (""); } static gboolean string_to_int (const char *string, int *dest) { char *end; long val; errno = 0; val = strtol (string, &end, 10); if (errno || !end || *end) return FALSE; #if G_MAXINT != G_MAXLONG if (val > G_MAXINT || val < G_MININT) return FALSE; #endif *dest = val; return TRUE; } static gboolean string_to_uint (const char *string, guint *dest) { char *end; guint64 val; mgw_errno_t err; val = mgw_ascii_strtoull (string, &end, 10, &err); if (mgw_errno_is_set (err) || !end || *end) return FALSE; if (val > G_MAXUINT) return FALSE; *dest = val; return TRUE; } static gboolean string_to_flags (const char *string, GType flags_type, guint *dest) { gpointer klass; GFlagsClass *flags_class; guint ival = 0; gboolean seen_something = FALSE; gboolean error = FALSE; const char *end; if (!string || !string[0]) { *dest = 0; return TRUE; } if (string_to_uint (string, dest)) return TRUE; klass = g_type_class_ref (flags_type); g_return_val_if_fail (G_IS_FLAGS_CLASS (klass), FALSE); flags_class = G_FLAGS_CLASS (klass); while (*string && !error) { GFlagsValue *flags_value; char *single; guint tmp; while (g_ascii_isspace (string[0]) || string[0] == '|') string++; if (!string[0]) break; end = string; while (end[0] && !g_ascii_isspace (end[0]) && end[0] != '|') end++; single = g_strndup (string, end - string); flags_value = g_flags_get_value_by_name (flags_class, single); if (!flags_value) flags_value = g_flags_get_value_by_nick (flags_class, single); if (flags_value) ival |= flags_value->value; else if (string_to_uint (single, &tmp)) ival |= tmp; else error = TRUE; string = end; seen_something = TRUE; g_free (single); } if (!seen_something) error = TRUE; if (!error) *dest = ival; g_type_class_unref (flags_class); return !error; } static gboolean string_to_enum (const char *string, GType enum_type, int *dest) { gpointer klass; GEnumClass *enum_class; GEnumValue *enum_value; *dest = 0; if (!string || !string[0] || string_to_int (string, dest)) return TRUE; klass = g_type_class_ref (enum_type); g_return_val_if_fail (G_IS_ENUM_CLASS (klass), FALSE); enum_class = G_ENUM_CLASS (klass); enum_value = g_enum_get_value_by_name (enum_class, string); if (!enum_value) enum_value = g_enum_get_value_by_nick (enum_class, string); if (enum_value) { *dest = enum_value->value; g_type_class_unref (enum_class); return TRUE; } g_type_class_unref (enum_class); return FALSE; } gboolean _moo_value_convert (const GValue *src, GValue *dest) { GType src_type, dest_type; g_return_val_if_fail (G_IS_VALUE (src) && G_IS_VALUE (dest), FALSE); src_type = G_VALUE_TYPE (src); dest_type = G_VALUE_TYPE (dest); g_return_val_if_fail (_moo_value_type_supported (src_type), FALSE); g_return_val_if_fail (_moo_value_type_supported (dest_type), FALSE); if (src_type == dest_type) { g_value_copy (src, dest); return TRUE; } if (dest_type == G_TYPE_STRING) { if (src_type == G_TYPE_BOOLEAN) { const char *string = g_value_get_boolean (src) ? "TRUE" : "FALSE"; g_value_set_static_string (dest, string); return TRUE; } if (src_type == G_TYPE_DOUBLE) { char *string = g_strdup_printf ("%f", g_value_get_double (src)); g_value_take_string (dest, string); return TRUE; } if (src_type == G_TYPE_INT) { char *string = g_strdup_printf ("%d", g_value_get_int (src)); g_value_take_string (dest, string); return TRUE; } if (src_type == G_TYPE_UINT) { char *string = g_strdup_printf ("%u", g_value_get_uint (src)); g_value_take_string (dest, string); return TRUE; } if (src_type == GDK_TYPE_COLOR) { char string[14]; const GdkColor *color = g_value_get_boxed (src); if (!color) { g_value_set_string (dest, NULL); return TRUE; } else { g_snprintf (string, 8, "#%02x%02x%02x", color->red >> 8, color->green >> 8, color->blue >> 8); g_value_set_string (dest, string); return TRUE; } } if (G_TYPE_IS_ENUM (src_type)) { gpointer klass; GEnumClass *enum_class; GEnumValue *enum_value; klass = g_type_class_ref (src_type); g_return_val_if_fail (G_IS_ENUM_CLASS (klass), FALSE); enum_class = G_ENUM_CLASS (klass); enum_value = g_enum_get_value (enum_class, g_value_get_enum (src)); if (!enum_value) { char *string = g_strdup_printf ("%d", g_value_get_enum (src)); g_value_take_string (dest, string); } else { g_value_set_string (dest, enum_value->value_nick); } g_type_class_unref (klass); return TRUE; } if (G_TYPE_IS_FLAGS (src_type)) { char *string = flags_to_string (g_value_get_flags (src)); g_value_take_string (dest, string); return TRUE; } g_return_val_if_reached (FALSE); } if (src_type == G_TYPE_STRING) { const char *string = g_value_get_string (src); if (dest_type == G_TYPE_BOOLEAN) { if (!string || !string[0]) g_value_set_boolean (dest, FALSE); else if (g_ascii_strcasecmp (string, "1") == 0 || g_ascii_strcasecmp (string, "yes") == 0 || g_ascii_strcasecmp (string, "true") == 0) g_value_set_boolean (dest, TRUE); else if (g_ascii_strcasecmp (string, "0") == 0 || g_ascii_strcasecmp (string, "no") == 0 || g_ascii_strcasecmp (string, "false") == 0) g_value_set_boolean (dest, FALSE); else return FALSE; return TRUE; } if (dest_type == G_TYPE_DOUBLE) { double val = 0.; char *end; if (string && string[0]) { mgw_errno_t err; val = mgw_ascii_strtod (string, &end, &err); if (mgw_errno_is_set (err) || !end || *end) return FALSE; } g_value_set_double (dest, val); return TRUE; } if (dest_type == G_TYPE_INT) { int val = 0; if (string && string[0] && !string_to_int (string, &val)) return FALSE; g_value_set_int (dest, val); return TRUE; } if (dest_type == G_TYPE_UINT) { guint val = 0; if (string && string[0] && !string_to_uint (string, &val)) return FALSE; g_value_set_uint (dest, val); return TRUE; } if (dest_type == GDK_TYPE_COLOR) { GdkColor color; if (!string || !string[0]) { g_value_set_boxed (dest, NULL); return TRUE; } g_return_val_if_fail (gdk_color_parse (string, &color), FALSE); g_value_set_boxed (dest, &color); return TRUE; } if (G_TYPE_IS_ENUM (dest_type)) { int ival; if (string_to_enum (string, dest_type, &ival)) { g_value_set_enum (dest, ival); return TRUE; } return FALSE; } if (G_TYPE_IS_FLAGS (dest_type)) { guint flags; if (string_to_flags (string, dest_type, &flags)) { g_value_set_flags (dest, flags); return TRUE; } return FALSE; } g_return_val_if_reached (FALSE); } if (G_TYPE_IS_ENUM (src_type) && dest_type == G_TYPE_INT) { g_value_set_int (dest, g_value_get_enum (src)); return TRUE; } if (G_TYPE_IS_ENUM (dest_type) && src_type == G_TYPE_INT) { g_value_set_enum (dest, g_value_get_int (src)); return TRUE; } if (G_TYPE_IS_FLAGS (src_type) && dest_type == G_TYPE_INT) { g_value_set_int (dest, g_value_get_flags (src)); return TRUE; } if (G_TYPE_IS_FLAGS (dest_type) && src_type == G_TYPE_INT) { g_value_set_flags (dest, g_value_get_int (src)); return TRUE; } if (src_type == G_TYPE_DOUBLE && dest_type == G_TYPE_INT) { g_value_set_int (dest, g_value_get_double (src)); return TRUE; } if (dest_type == G_TYPE_DOUBLE && src_type == G_TYPE_INT) { g_value_set_double (dest, g_value_get_int (src)); return TRUE; } g_return_val_if_reached (FALSE); } gboolean _moo_value_equal (const GValue *a, const GValue *b) { GType type; g_return_val_if_fail (G_IS_VALUE (a) && G_IS_VALUE (b), a == b); g_return_val_if_fail (G_VALUE_TYPE (a) == G_VALUE_TYPE (b), a == b); type = G_VALUE_TYPE (a); if (type == G_TYPE_BOOLEAN) { gboolean ba = g_value_get_boolean (a); gboolean bb = g_value_get_boolean (b); return (ba && bb) || (!ba && !bb); } if (type == G_TYPE_INT) return g_value_get_int (a) == g_value_get_int (b); if (type == G_TYPE_UINT) return g_value_get_uint (a) == g_value_get_uint (b); if (type == G_TYPE_DOUBLE) return g_value_get_double (a) == g_value_get_double (b); if (type == G_TYPE_STRING) { const char *sa, *sb; sa = g_value_get_string (a); sb = g_value_get_string (b); if (!sa || !sb) return sa == sb; else return !strcmp (sa, sb); } if (type == GDK_TYPE_COLOR) { const GdkColor *ca, *cb; ca = g_value_get_boxed (a); cb = g_value_get_boxed (b); if (!ca || !cb) return ca == cb; else return ca->red == cb->red && ca->green == cb->green && ca->blue == cb->blue; } if (G_TYPE_IS_ENUM (type)) return g_value_get_enum (a) == g_value_get_enum (b); if (G_TYPE_IS_FLAGS (type)) return g_value_get_flags (a) == g_value_get_flags (b); g_return_val_if_reached (a == b); } gboolean _moo_value_type_supported (GType type) { return type == G_TYPE_BOOLEAN || type == G_TYPE_INT || type == G_TYPE_UINT || type == G_TYPE_DOUBLE || type == G_TYPE_STRING || type == GDK_TYPE_COLOR || G_TYPE_IS_ENUM (type) || G_TYPE_IS_FLAGS (type); } static gboolean _moo_value_convert_to_bool (const GValue *val, gboolean *dest) { GValue result = {0}; g_value_init (&result, G_TYPE_BOOLEAN); if (_moo_value_convert (val, &result)) { *dest = g_value_get_boolean (&result); return TRUE; } return FALSE; } static gboolean _moo_value_convert_to_int (const GValue *val, int *dest) { GValue result = {0}; g_value_init (&result, G_TYPE_INT); if (_moo_value_convert (val, &result)) { *dest = g_value_get_int (&result); return TRUE; } return FALSE; } static gboolean _moo_value_convert_to_uint (const GValue *val, guint *dest) { GValue result = {0}; g_value_init (&result, G_TYPE_UINT); if (_moo_value_convert (val, &result)) { *dest = g_value_get_uint (&result); return TRUE; } return FALSE; } double _moo_value_convert_to_double (const GValue *val) { GValue result = {0}; g_value_init (&result, G_TYPE_DOUBLE); if (!_moo_value_convert (val, &result)) g_warning ("%s: could not convert value to double", G_STRFUNC); return g_value_get_double (&result); } #if 0 int _moo_value_convert_to_flags (const GValue *val, GType flags_type) { GValue result = {0}; g_value_init (&result, flags_type); _moo_value_convert (val, &result); return g_value_get_flags (&result); } int _moo_value_convert_to_enum (const GValue *val, GType enum_type) { GValue result = {0}; g_value_init (&result, enum_type); _moo_value_convert (val, &result); return g_value_get_enum (&result); } const GdkColor* _moo_value_convert_to_color (const GValue *val) { static GValue result; if (G_IS_VALUE (&result)) g_value_unset (&result); g_value_init (&result, GDK_TYPE_COLOR); if (!_moo_value_convert (val, &result)) return NULL; else return g_value_get_boxed (&result); } #endif const char* _moo_value_convert_to_string (const GValue *val) { static GValue result; if (G_IS_VALUE (&result)) g_value_unset (&result); g_value_init (&result, G_TYPE_STRING); if (!_moo_value_convert (val, &result)) return NULL; else return g_value_get_string (&result); } gboolean _moo_value_convert_from_string (const char *string, GValue *val) { GValue str_val = {0}; gboolean result; g_return_val_if_fail (G_IS_VALUE (val), FALSE); g_return_val_if_fail (string != NULL, FALSE); g_value_init (&str_val, G_TYPE_STRING); g_value_set_static_string (&str_val, string); result = _moo_value_convert (&str_val, val); g_value_unset (&str_val); return result; } int _moo_convert_string_to_int (const char *string, int default_val) { int int_val = default_val; if (string && string[0]) { GValue str_val = {0}; g_value_init (&str_val, G_TYPE_STRING); g_value_set_static_string (&str_val, string); if (!_moo_value_convert_to_int (&str_val, &int_val)) g_warning ("%s: could not convert string '%s' to int", G_STRFUNC, string); g_value_unset (&str_val); } return int_val; } guint _moo_convert_string_to_uint (const char *string, guint default_val) { guint int_val = default_val; if (string && string[0]) { GValue str_val = {0}; g_value_init (&str_val, G_TYPE_STRING); g_value_set_static_string (&str_val, string); if (!_moo_value_convert_to_uint (&str_val, &int_val)) g_warning ("%s: could not convert string '%s' to uint", G_STRFUNC, string); g_value_unset (&str_val); } return int_val; } gboolean _moo_convert_string_to_bool (const char *string, gboolean default_val) { gboolean bool_val = default_val; if (string && string[0]) { GValue str_val = {0}; g_value_init (&str_val, G_TYPE_STRING); g_value_set_static_string (&str_val, string); if (!_moo_value_convert_to_bool (&str_val, &bool_val)) g_warning ("%s: could not convert string '%s' to boolean", G_STRFUNC, string); g_value_unset (&str_val); } return bool_val; } const char* _moo_convert_bool_to_string (gboolean value) { GValue bool_val = {0}; g_value_init (&bool_val, G_TYPE_BOOLEAN); g_value_set_boolean (&bool_val, value); return _moo_value_convert_to_string (&bool_val); } const char * _moo_convert_int_to_string (int value) { GValue int_val = {0}; g_value_init (&int_val, G_TYPE_INT); g_value_set_int (&int_val, value); return _moo_value_convert_to_string (&int_val); } gboolean _moo_value_change_type (GValue *val, GType new_type) { GValue tmp = {0}; gboolean result; g_return_val_if_fail (G_IS_VALUE (val), FALSE); g_return_val_if_fail (_moo_value_type_supported (new_type), FALSE); g_value_init (&tmp, new_type); result = _moo_value_convert (val, &tmp); if (result) { g_value_unset (val); *val = tmp; } return result; } static double string_to_double (const char *string) { double ret; GValue gval = {0}; g_value_init (&gval, G_TYPE_STRING); g_value_set_static_string (&gval, string); ret = _moo_value_convert_to_double (&gval); g_value_unset (&gval); return ret; } static void test_one_enum (GType enum_type, const char *string, int value, gboolean bad) { GValue eval = {0}, sval = {0}; g_value_init (&eval, enum_type); g_value_set_enum (&eval, value); g_value_init (&sval, G_TYPE_STRING); g_value_set_static_string (&sval, string); TEST_EXPECT_WARNING (0, "%s", "string to enum"); if ((!bad) != _moo_value_convert (&sval, &eval)) { if (!bad) TEST_FAILED_MSG ("could not convert string '%s' to enum '%s'", TEST_FMT_STR (string), g_type_name (enum_type)); else TEST_FAILED_MSG ("erroneously converted string '%s' to enum '%s'", TEST_FMT_STR (string), g_type_name (enum_type)); } else { TEST_ASSERT_MSG (g_value_get_enum (&eval) == value, "string_to_enum('%s'): expected %d, got %d", TEST_FMT_STR (string), value, g_value_get_enum (&eval)); } g_value_set_enum (&eval, value); if (!_moo_value_convert (&eval, &sval)) TEST_FAILED_MSG ("could not convert enum value %d of type '%s' to string", value, g_type_name (enum_type)); else if (!_moo_value_convert (&sval, &eval)) TEST_FAILED_MSG ("could not convert string '%s' to enum type '%s'", TEST_FMT_STR (g_value_get_string (&sval)), g_type_name (enum_type)); else TEST_ASSERT_MSG (g_value_get_enum (&eval) == value, "string_to_enum(enum_to_string('%d')): got %d", value, g_value_get_enum (&eval)); g_value_unset (&sval); TEST_CHECK_WARNING (); } static void test_one_flags (GType flags_type, const char *string, guint value, gboolean bad) { GValue eval = {0}, sval = {0}; g_value_init (&eval, flags_type); g_value_set_flags (&eval, value); g_value_init (&sval, G_TYPE_STRING); g_value_set_static_string (&sval, string); TEST_EXPECT_WARNING (0, "%s", "string to flags"); if ((!bad) != _moo_value_convert (&sval, &eval)) { if (!bad) TEST_FAILED_MSG ("could not convert string '%s' to flags type '%s'", TEST_FMT_STR (string), g_type_name (flags_type)); else TEST_FAILED_MSG ("erroneously converted string '%s' to flags type '%s'", TEST_FMT_STR (string), g_type_name (flags_type)); } else { TEST_ASSERT_MSG (g_value_get_flags (&eval) == value, "string_to_flags('%s'): expected %u, got %u", TEST_FMT_STR (string), value, g_value_get_flags (&eval)); } g_value_set_flags (&eval, value); if (!_moo_value_convert (&eval, &sval)) TEST_FAILED_MSG ("could not convert flags value %u of type '%s' to string", value, g_type_name (flags_type)); else if (!_moo_value_convert (&sval, &eval)) TEST_FAILED_MSG ("could not convert string '%s' to flags type '%s'", TEST_FMT_STR (g_value_get_string (&sval)), g_type_name (flags_type)); else TEST_ASSERT_MSG (g_value_get_flags (&eval) == value, "string_to_flags(flags_to_string('%u')): got %u", value, g_value_get_flags (&eval)); g_value_unset (&sval); TEST_CHECK_WARNING (); } static void test_enums (void) { test_one_enum (GTK_TYPE_WINDOW_TYPE, NULL, GTK_WINDOW_TOPLEVEL, FALSE); test_one_enum (GTK_TYPE_WINDOW_TYPE, "", GTK_WINDOW_TOPLEVEL, FALSE); test_one_enum (GTK_TYPE_WINDOW_TYPE, "0", GTK_WINDOW_TOPLEVEL, FALSE); test_one_enum (GTK_TYPE_WINDOW_TYPE, "17", 17, FALSE); test_one_enum (GTK_TYPE_WINDOW_TYPE, "-123", -123, FALSE); test_one_enum (GTK_TYPE_WINDOW_TYPE, "GTK_WINDOW_TOPLEVEL", GTK_WINDOW_TOPLEVEL, FALSE); test_one_enum (GTK_TYPE_WINDOW_TYPE, "toplevel", GTK_WINDOW_TOPLEVEL, FALSE); test_one_enum (GTK_TYPE_WINDOW_TYPE, "foobar", GTK_WINDOW_TOPLEVEL, TRUE); } static void test_flags (void) { test_one_flags (GTK_TYPE_ACCEL_FLAGS, NULL, 0, FALSE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "", 0, FALSE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "18", 18, FALSE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "0", 0, FALSE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "GTK_ACCEL_VISIBLE", GTK_ACCEL_VISIBLE, FALSE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "visible", GTK_ACCEL_VISIBLE, FALSE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED", GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED, FALSE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "GTK_ACCEL_VISIBLE |GTK_ACCEL_LOCKED", GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED, FALSE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "GTK_ACCEL_VISIBLE|", GTK_ACCEL_VISIBLE, FALSE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "visible | GTK_ACCEL_LOCKED", GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED, FALSE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "blah | GTK_ACCEL_LOCKED", 0, TRUE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "asd", 0, TRUE); test_one_flags (GTK_TYPE_ACCEL_FLAGS, "-12", 0, TRUE); } static void test_misc (void) { TEST_EXPECT_WARNING (0, "%s", "_moo_convert_string_to_bool"); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool (NULL, TRUE), TRUE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool (NULL, FALSE), FALSE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("", TRUE), TRUE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("", FALSE), FALSE); TEST_CHECK_WARNING (); TEST_EXPECT_WARNING (0, "%s", "_moo_convert_string_to_bool"); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("true", FALSE), TRUE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("yes", FALSE), TRUE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("1", FALSE), TRUE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("YES", FALSE), TRUE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("false", TRUE), FALSE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("no", TRUE), FALSE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("0", TRUE), FALSE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("NO", TRUE), FALSE); TEST_CHECK_WARNING (); TEST_EXPECT_WARNING (3, "%s", "_moo_convert_string_to_bool"); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("blah blah blah", TRUE), TRUE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("12.23", TRUE), TRUE); TEST_ASSERT_INT_EQ(_moo_convert_string_to_bool ("12.23", FALSE), FALSE); TEST_CHECK_WARNING (); TEST_EXPECT_WARNING (0, "%s", "_moo_convert_string_to_int"); TEST_ASSERT_INT_EQ(_moo_convert_string_to_int (NULL, G_MAXINT), G_MAXINT); TEST_ASSERT_INT_EQ(_moo_convert_string_to_int ("", G_MAXINT), G_MAXINT); TEST_ASSERT_INT_EQ(_moo_convert_string_to_int ("1", G_MAXINT), 1); TEST_ASSERT_INT_EQ(_moo_convert_string_to_int ("-12312414", G_MAXINT), -12312414); TEST_CHECK_WARNING (); TEST_EXPECT_WARNING (2, "%s", "_moo_convert_string_to_int"); TEST_ASSERT_INT_EQ(_moo_convert_string_to_int ("abcd", 65), 65); TEST_ASSERT_INT_EQ(_moo_convert_string_to_int ("12.23", 65), 65); TEST_CHECK_WARNING (); TEST_EXPECT_WARNING (0, "%s", "string_to_double"); TEST_ASSERT_DBL_EQ(string_to_double (NULL), .0); TEST_ASSERT_DBL_EQ(string_to_double (""), .0); TEST_ASSERT_DBL_EQ(string_to_double (".0"), .0); TEST_ASSERT_DBL_EQ(string_to_double ("0."), .0); TEST_ASSERT_DBL_EQ(string_to_double ("0.0"), .0); TEST_ASSERT_DBL_EQ(string_to_double ("12.21312"), 12.21312); TEST_ASSERT_DBL_EQ(string_to_double ("12"), 12.); TEST_ASSERT_DBL_EQ(string_to_double ("-123"), -123.); TEST_CHECK_WARNING (); TEST_EXPECT_WARNING (5, "%s", "string_to_double"); TEST_ASSERT_DBL_EQ (string_to_double ("blah"), 0.); TEST_ASSERT_DBL_EQ (string_to_double (".2323eee"), 0.); TEST_ASSERT_DBL_EQ (string_to_double ("23.2323.23"), 0.); TEST_ASSERT_DBL_EQ (string_to_double ("true"), 0.); TEST_ASSERT_DBL_EQ (string_to_double ("+-213"), 0.); TEST_CHECK_WARNING (); test_enums (); test_flags (); } void moo_test_gobject (void) { MooTestSuite *suite; suite = moo_test_suite_new ("mooutils-gobject", "mooutils/mooutils-gobject.c", NULL, NULL, NULL); moo_test_suite_add_test (suite, "convert", "test of converting values", (MooTestFunc) test_misc, NULL); } /*****************************************************************************/ /* GParameter array manipulation */ #if 0 static void copy_param (GParameter *dest, GParameter *src); typedef GParamSpec* (*MooLookupProperty) (GObjectClass *klass, const char *prop_name); GParameter* _moo_param_array_collect (GType type, MooLookupProperty lookup_func, guint *len, const char *first_prop_name, ...) { GParameter *array; va_list var_args; va_start (var_args, first_prop_name); array = _moo_param_array_collect_valist (type, lookup_func, len, first_prop_name, var_args); va_end (var_args); return array; } GParameter* _moo_param_array_add (GType type, GParameter *src, guint len, guint *new_len, const char *first_prop_name, ...) { GParameter *copy; va_list var_args; va_start (var_args, first_prop_name); copy = _moo_param_array_add_valist (type, src, len, new_len, first_prop_name, var_args); va_end (var_args); return copy; } GParameter* _moo_param_array_add_type (GParameter *src, guint len, guint *new_len, const char *first_prop_name, ...) { GParameter *copy; va_list var_args; va_start (var_args, first_prop_name); copy = _moo_param_array_add_type_valist (src, len, new_len, first_prop_name, var_args); va_end (var_args); return copy; } GParameter* _moo_param_array_add_type_valist (GParameter *src, guint len, guint *new_len, const char *first_prop_name, va_list var_args) { const char *name; GArray *copy; guint i; g_return_val_if_fail (src != NULL, NULL); copy = g_array_new (FALSE, TRUE, sizeof (GParameter)); for (i = 0; i < len; ++i) { GParameter param = {NULL, {0, {{0}, {0}}}}; copy_param (¶m, &src[i]); g_array_append_val (copy, param); } name = first_prop_name; while (name) { char *error = NULL; GParameter param = {NULL, {0, {{0}, {0}}}}; GType type; type = va_arg (var_args, GType); if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_INVALID) { g_warning ("invalid type id passed"); _moo_param_array_free ((GParameter*)copy->data, copy->len); g_array_free (copy, FALSE); return NULL; } param.name = g_strdup (name); g_value_init (¶m.value, type); G_VALUE_COLLECT (¶m.value, var_args, 0, &error); if (error) { g_critical ("%s", error); g_free (error); g_value_unset (¶m.value); g_free ((char*)param.name); _moo_param_array_free ((GParameter*)copy->data, copy->len); g_array_free (copy, FALSE); return NULL; } g_array_append_val (copy, param); name = va_arg (var_args, char*); } *new_len = copy->len; return (GParameter*) g_array_free (copy, FALSE); } GParameter* _moo_param_array_add_valist (GType type, GParameter *src, guint len, guint *new_len, const char *first_prop_name, va_list var_args) { const char *name; GArray *copy; guint i; GObjectClass *klass = g_type_class_ref (type); /* TODO: unref */ g_return_val_if_fail (klass != NULL, NULL); g_return_val_if_fail (src != NULL, NULL); copy = g_array_new (FALSE, TRUE, sizeof (GParameter)); for (i = 0; i < len; ++i) { GParameter param = {NULL, {0, {{0}, {0}}}}; copy_param (¶m, &src[i]); g_array_append_val (copy, param); } name = first_prop_name; while (name) { char *error = NULL; GParameter param = {NULL, {0, {{0}, {0}}}}; GParamSpec *pspec = g_object_class_find_property (klass, name); if (!pspec) { g_warning ("class '%s' doesn't have property '%s'", g_type_name (type), name); _moo_param_array_free ((GParameter*)copy->data, copy->len); g_array_free (copy, FALSE); return NULL; } param.name = g_strdup (name); g_value_init (¶m.value, G_PARAM_SPEC_VALUE_TYPE (pspec)); G_VALUE_COLLECT (¶m.value, var_args, 0, &error); if (error) { g_critical ("%s", error); g_free (error); g_value_unset (¶m.value); g_free ((char*)param.name); _moo_param_array_free ((GParameter*)copy->data, copy->len); g_array_free (copy, FALSE); return NULL; } g_array_append_val (copy, param); name = va_arg (var_args, char*); } *new_len = copy->len; return (GParameter*) g_array_free (copy, FALSE); } GParameter* _moo_param_array_collect_valist (GType type, MooLookupProperty lookup_func, guint *len, const char *first_prop_name, va_list var_args) { GObjectClass *klass; GArray *params = NULL; const char *prop_name; g_return_val_if_fail (G_TYPE_IS_OBJECT (type), NULL); g_return_val_if_fail (first_prop_name != NULL, NULL); klass = g_type_class_ref (type); params = g_array_new (FALSE, TRUE, sizeof (GParameter)); prop_name = first_prop_name; while (prop_name) { char *error = NULL; GParameter param = {0}; GParamSpec *pspec; pspec = lookup_func ? lookup_func (klass, prop_name) : g_object_class_find_property (klass, prop_name); if (!pspec) { g_warning ("class '%s' doesn't have property '%s'", g_type_name (type), prop_name); _moo_param_array_free ((GParameter*)params->data, params->len); g_array_free (params, FALSE); g_type_class_unref (klass); return NULL; } param.name = g_strdup (prop_name); g_value_init (¶m.value, G_PARAM_SPEC_VALUE_TYPE (pspec)); G_VALUE_COLLECT (¶m.value, var_args, 0, &error); if (error) { g_critical ("%s", error); g_free (error); g_value_unset (¶m.value); g_free ((char*)param.name); _moo_param_array_free ((GParameter*)params->data, params->len); g_array_free (params, FALSE); g_type_class_unref (klass); return NULL; } g_array_append_val (params, param); prop_name = va_arg (var_args, char*); } g_type_class_unref (klass); *len = params->len; return (GParameter*) g_array_free (params, FALSE); } static void copy_param (GParameter *dest, GParameter *src) { dest->name = g_strdup (src->name); g_value_init (&dest->value, G_VALUE_TYPE (&src->value)); g_value_copy (&src->value, &dest->value); } #endif void _moo_param_array_free (GParameter *array, guint len) { guint i; for (i = 0; i < len; ++i) { g_value_unset (&array[i].value); g_free ((char*)array[i].name); } g_free (array); } /*****************************************************************************/ /* Signal that does not require class method */ guint _moo_signal_new_cb (const gchar *signal_name, GType itype, GSignalFlags signal_flags, GCallback handler, GSignalAccumulator accumulator, gpointer accu_data, GSignalCMarshaller c_marshaller, GType return_type, guint n_params, ...) { va_list args; guint signal_id; GClosure *closure = NULL; g_return_val_if_fail (signal_name != NULL, 0); va_start (args, n_params); if (handler) closure = g_cclosure_new (handler, NULL, NULL); signal_id = g_signal_new_valist (signal_name, itype, signal_flags, closure, accumulator, accu_data, c_marshaller, return_type, n_params, args); va_end (args); return signal_id; } /****************************************************************************/ /* Property watch */ static GHashTable *watches = NULL; static guint watch_last_id = 0; #define Watch MooObjectWatch #define WatchClass MooObjectWatchClass #define watch_new _moo_object_watch_new #define watch_alloc _moo_object_watch_alloc static void watch_destroy (Watch *w) { if (w) { if (w->klass->destroy) w->klass->destroy (w); if (w->notify) w->notify (w->notify_data); _moo_object_ptr_free (w->source); _moo_object_ptr_free (w->target); g_free (w); } } static void watch_source_died (Watch *w) { if (w->klass->source_notify) w->klass->source_notify (w); _moo_object_ptr_free (w->source); w->source = NULL; g_hash_table_remove (watches, GUINT_TO_POINTER (w->id)); } static void watch_target_died (Watch *w) { if (w->klass->target_notify) w->klass->target_notify (w); _moo_object_ptr_free (w->target); w->target = NULL; g_hash_table_remove (watches, GUINT_TO_POINTER (w->id)); } Watch * watch_alloc (gsize size, WatchClass *klass, gpointer source, gpointer target, GDestroyNotify notify, gpointer notify_data) { Watch *w; g_return_val_if_fail (size >= sizeof (Watch), NULL); g_return_val_if_fail (G_IS_OBJECT (source), NULL); g_return_val_if_fail (G_IS_OBJECT (target), NULL); w = g_malloc0 (size); w->source = _moo_object_ptr_new (source, (GWeakNotify) watch_source_died, w); w->target = _moo_object_ptr_new (target, (GWeakNotify) watch_target_died, w); w->klass = klass; w->notify = notify; w->notify_data = notify_data; w->id = ++watch_last_id; if (!watches) watches = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) watch_destroy); g_hash_table_insert (watches, GUINT_TO_POINTER (w->id), w); return w; } typedef struct { Watch parent; GParamSpec *source_pspec; GParamSpec *target_pspec; MooTransformPropFunc transform; gpointer transform_data; } PropWatch; static void prop_watch_check (PropWatch *watch); static void prop_watch_destroy (Watch *watch); static WatchClass PropWatchClass = {NULL, NULL, prop_watch_destroy}; static PropWatch* prop_watch_new (GObject *target, const char *target_prop, GObject *source, const char *source_prop, const char *signal, MooTransformPropFunc transform, gpointer transform_data, GDestroyNotify destroy_notify, gpointer destroy_notify_data) { PropWatch *watch; GObjectClass *target_class, *source_class; char *signal_name = NULL; GParamSpec *source_pspec; GParamSpec *target_pspec; g_return_val_if_fail (G_IS_OBJECT (target), NULL); g_return_val_if_fail (G_IS_OBJECT (source), NULL); g_return_val_if_fail (target_prop != NULL, NULL); g_return_val_if_fail (source_prop != NULL, NULL); g_return_val_if_fail (transform != NULL, NULL); target_class = g_type_class_peek (G_OBJECT_TYPE (target)); source_class = g_type_class_peek (G_OBJECT_TYPE (source)); source_pspec = g_object_class_find_property (source_class, source_prop); target_pspec = g_object_class_find_property (target_class, target_prop); if (!source_pspec || !target_pspec) { if (!source_pspec) g_warning ("no property '%s' in class '%s'", source_prop, g_type_name (G_OBJECT_TYPE (source))); if (!target_pspec) g_warning ("no property '%s' in class '%s'", target_prop, g_type_name (G_OBJECT_TYPE (target))); return NULL; } watch = watch_new (PropWatch, &PropWatchClass, source, target, destroy_notify, destroy_notify_data); watch->source_pspec = source_pspec; watch->target_pspec = target_pspec; watch->transform = transform; watch->transform_data = transform_data; if (!signal) { signal_name = g_strdup_printf ("notify::%s", source_prop); signal = signal_name; } g_signal_connect_swapped (source, signal, G_CALLBACK (prop_watch_check), watch); g_free (signal_name); return watch; } static void prop_watch_destroy (Watch *watch) { if (MOO_OBJECT_PTR_GET (watch->source)) g_signal_handlers_disconnect_by_func (MOO_OBJECT_PTR_GET (watch->source), (gpointer) prop_watch_check, watch); } guint _moo_add_property_watch (gpointer target, const char *target_prop, gpointer source, const char *source_prop, MooTransformPropFunc transform, gpointer transform_data, GDestroyNotify destroy_notify) { PropWatch *watch; g_return_val_if_fail (G_IS_OBJECT (target), 0); g_return_val_if_fail (G_IS_OBJECT (source), 0); g_return_val_if_fail (target_prop != NULL, 0); g_return_val_if_fail (source_prop != NULL, 0); g_return_val_if_fail (transform != NULL, 0); watch = prop_watch_new (target, target_prop, source, source_prop, NULL, transform, transform_data, destroy_notify, transform_data); if (!watch) return 0; prop_watch_check (watch); return watch->parent.id; } static void prop_watch_check (PropWatch *watch) { GValue source_val = {0}, target_val = {0}, old_target_val = {0}; GObject *source, *target; source = MOO_OBJECT_PTR_GET (watch->parent.source); target = MOO_OBJECT_PTR_GET (watch->parent.target); g_return_if_fail (source && target); g_value_init (&source_val, watch->source_pspec->value_type); g_value_init (&target_val, watch->target_pspec->value_type); g_value_init (&old_target_val, watch->target_pspec->value_type); g_object_ref (source); g_object_ref (target); g_object_get_property (source, watch->source_pspec->name, &source_val); g_object_get_property (target, watch->target_pspec->name, &old_target_val); watch->transform (&target_val, &source_val, watch->transform_data); if (g_param_values_cmp (watch->target_pspec, &target_val, &old_target_val)) g_object_set_property (target, watch->target_pspec->name, &target_val); g_object_unref (source); g_object_unref (target); g_value_unset (&source_val); g_value_unset (&target_val); g_value_unset (&old_target_val); } static void _moo_copy_boolean (GValue *target, const GValue *source, G_GNUC_UNUSED gpointer dummy) { g_value_set_boolean (target, g_value_get_boolean (source) ? TRUE : FALSE); } static void _moo_invert_boolean (GValue *target, const GValue *source, G_GNUC_UNUSED gpointer dummy) { g_value_set_boolean (target, !g_value_get_boolean (source)); } guint moo_bind_bool_property (gpointer target, const char *target_prop, gpointer source, const char *source_prop, gboolean invert) { g_return_val_if_fail (G_IS_OBJECT (target), 0); g_return_val_if_fail (G_IS_OBJECT (source), 0); g_return_val_if_fail (target_prop && source_prop, 0); if (invert) return _moo_add_property_watch (target, target_prop, source, source_prop, _moo_invert_boolean, NULL, NULL); else return _moo_add_property_watch (target, target_prop, source, source_prop, _moo_copy_boolean, NULL, NULL); } #if 0 gboolean _moo_sync_bool_property (gpointer slave, const char *slave_prop, gpointer master, const char *master_prop, gboolean invert) { guint id1 = 0, id2 = 0; id1 = moo_bind_bool_property (slave, slave_prop, master, master_prop, invert); if (id1) { id2 = moo_bind_bool_property (master, master_prop, slave, slave_prop, invert); if (!id2) _moo_watch_remove (id1); } return id1 && id2; } #endif void moo_bind_sensitive (GtkWidget *btn, GtkWidget *dependent, gboolean invert) { g_return_if_fail (G_IS_OBJECT (btn)); g_return_if_fail (G_IS_OBJECT (dependent)); moo_bind_bool_property (dependent, "sensitive", btn, "active", invert); } #if 0 gboolean _moo_watch_remove (guint watch_id) { Watch *watch; if (!watches) return FALSE; watch = g_hash_table_lookup (watches, GUINT_TO_POINTER (watch_id)); if (!watch) return FALSE; g_hash_table_remove (watches, GUINT_TO_POINTER (watch_id)); return TRUE; } void _moo_watch_add_signal (guint watch_id, const char *source_signal) { Watch *watch; g_return_if_fail (watches != NULL); g_return_if_fail (source_signal != NULL); watch = g_hash_table_lookup (watches, GUINT_TO_POINTER (watch_id)); g_return_if_fail (watch != NULL); g_return_if_fail (watch->klass == &PropWatchClass); g_signal_connect_swapped (MOO_OBJECT_PTR_GET (watch->source), source_signal, G_CALLBACK (prop_watch_check), watch); } void _moo_watch_add_property (guint watch_id, const char *source_prop) { char *signal; g_return_if_fail (source_prop != NULL); signal = g_strdup_printf ("notify::%s", source_prop); _moo_watch_add_signal (watch_id, signal); g_free (signal); } /************************************************************/ /* SignalWatch */ typedef struct { Watch parent; guint signal_id; GQuark detail; } SignalWatch; static void signal_watch_invoke (SignalWatch *watch); static void signal_watch_destroy (Watch *watch); static WatchClass SignalWatchClass = {NULL, NULL, signal_watch_destroy}; static gboolean check_signal (GObject *obj, const char *signal, guint *signal_id, GQuark *detail, gboolean any_return) { GSignalQuery query; if (!g_signal_parse_name (signal, G_OBJECT_TYPE (obj), signal_id, detail, FALSE)) { g_warning ("could not parse signal '%s' of '%s' object", signal, g_type_name (G_OBJECT_TYPE (obj))); return FALSE; } g_signal_query (*signal_id, &query); if (query.n_params > 0) { g_warning ("implement me"); return FALSE; } switch (query.return_type) { case G_TYPE_NONE: break; case G_TYPE_BOOLEAN: case G_TYPE_INT: case G_TYPE_UINT: if (!any_return) g_warning ("implement me"); return FALSE; default: g_warning ("implement me"); return FALSE; } return TRUE; } static SignalWatch* signal_watch_new (GObject *target, const char *target_signal, GObject *source, const char *source_signal) { SignalWatch *watch; guint t_id, s_id; GQuark t_detail, s_detail; g_return_val_if_fail (G_IS_OBJECT (target), NULL); g_return_val_if_fail (G_IS_OBJECT (source), NULL); g_return_val_if_fail (target_signal != NULL, NULL); g_return_val_if_fail (source_signal != NULL, NULL); if (!check_signal (target, target_signal, &t_id, &t_detail, TRUE)) return NULL; if (!check_signal (source, source_signal, &s_id, &s_detail, FALSE)) return NULL; watch = watch_new (SignalWatch, &SignalWatchClass, source, target, NULL, NULL); watch->signal_id = t_id; watch->detail = t_detail; g_signal_connect_swapped (source, source_signal, G_CALLBACK (signal_watch_invoke), watch); return watch; } static void signal_watch_destroy (Watch *watch) { if (MOO_OBJECT_PTR_GET (watch->source)) g_signal_handlers_disconnect_by_func (MOO_OBJECT_PTR_GET (watch->source), (gpointer) signal_watch_invoke, watch); } static void signal_watch_invoke (SignalWatch *watch) { int ret[4]; g_signal_emit (MOO_OBJECT_PTR_GET (watch->parent.target), watch->signal_id, watch->detail, ret); } guint _moo_bind_signal (gpointer target, const char *target_signal, gpointer source, const char *source_signal) { SignalWatch *watch; watch = signal_watch_new (target, target_signal, source, source_signal); if (!watch) return 0; return watch->parent.id; } #endif /*****************************************************************************/ /* Data store */ struct _MooData { GHashTable *hash; guint ref_count; GHashFunc hash_func; GEqualFunc key_equal_func; GDestroyNotify key_destroy_func; }; static MooPtr * _moo_ptr_ref (MooPtr *ptr) { if (ptr) ptr->ref_count++; return ptr; } static void _moo_ptr_unref (MooPtr *ptr) { if (ptr && !--(ptr->ref_count)) { if (ptr->free_func) ptr->free_func (ptr->data); g_free (ptr); } } MOO_DEFINE_BOXED_TYPE_R (MooPtr, _moo_ptr) static MooPtr * _moo_ptr_new (gpointer data, GDestroyNotify free_func) { MooPtr *ptr; g_return_val_if_fail (data != NULL, NULL); ptr = g_new (MooPtr, 1); ptr->data = data; ptr->free_func = free_func; ptr->ref_count = 1; return ptr; } static MooData * _moo_data_ref (MooData *data) { if (data) data->ref_count++; return data; } void _moo_data_unref (MooData *data) { if (data && !--(data->ref_count)) { g_hash_table_destroy (data->hash); g_free (data); } } MOO_DEFINE_BOXED_TYPE_R (MooData, _moo_data) static void free_gvalue (GValue *val) { if (val) { g_value_unset (val); g_free (val); } } static GValue * copy_gvalue (const GValue *val) { GValue *copy; g_return_val_if_fail (G_IS_VALUE (val), NULL); copy = g_new (GValue, 1); copy->g_type = 0; g_value_init (copy, G_VALUE_TYPE (val)); g_value_copy (val, copy); return copy; } MooData * _moo_data_new (GHashFunc hash_func, GEqualFunc key_equal_func, GDestroyNotify key_destroy_func) { MooData *data; g_return_val_if_fail (hash_func != NULL, NULL); g_return_val_if_fail (key_equal_func != NULL, NULL); data = g_new0 (MooData, 1); data->hash = g_hash_table_new_full (hash_func, key_equal_func, key_destroy_func, (GDestroyNotify) free_gvalue); data->ref_count = 1; data->hash_func = hash_func; data->key_equal_func = key_equal_func; data->key_destroy_func = key_destroy_func; return data; } void _moo_data_insert_value (MooData *data, gpointer key, const GValue *value) { g_return_if_fail (data != NULL); g_return_if_fail (G_IS_VALUE (value)); g_hash_table_insert (data->hash, key, copy_gvalue (value)); } void _moo_data_insert_ptr (MooData *data, gpointer key, gpointer value, GDestroyNotify destroy) { MooPtr *ptr; GValue gval = {0}; g_return_if_fail (data != NULL); g_return_if_fail (value != NULL); ptr = _moo_ptr_new (value, destroy); g_value_init (&gval, MOO_TYPE_PTR); g_value_set_boxed (&gval, ptr); _moo_data_insert_value (data, key, &gval); g_value_unset (&gval); _moo_ptr_unref (ptr); } void _moo_data_remove (MooData *data, gpointer key) { g_return_if_fail (data != NULL); g_hash_table_remove (data->hash, key); } void _moo_data_clear (MooData *data) { g_return_if_fail (data != NULL); g_hash_table_destroy (data->hash); data->hash = g_hash_table_new_full (data->hash_func, data->key_equal_func, data->key_destroy_func, (GDestroyNotify) free_gvalue); } #if 0 guint _moo_data_size (MooData *data) { g_return_val_if_fail (data != NULL, 0); return g_hash_table_size (data->hash); } gboolean _moo_data_has_key (MooData *data, gpointer key) { g_return_val_if_fail (data != NULL, FALSE); return g_hash_table_lookup (data->hash, key) != NULL; } GType _moo_data_get_value_type (MooData *data, gpointer key) { GValue *value; g_return_val_if_fail (data != NULL, 0); value = g_hash_table_lookup (data->hash, key); return value ? G_VALUE_TYPE (value) : G_TYPE_NONE; } #endif gboolean _moo_data_get_value (MooData *data, gpointer key, GValue *dest) { GValue *value; g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail (!dest || dest->g_type == 0, FALSE); value = g_hash_table_lookup (data->hash, key); if (value && dest) { g_value_init (dest, G_VALUE_TYPE (value)); g_value_copy (value, dest); } return value != NULL; } gpointer _moo_data_get_ptr (MooData *data, gpointer key) { MooPtr *ptr; GValue *value; g_return_val_if_fail (data != NULL, NULL); value = g_hash_table_lookup (data->hash, key); if (value) { g_return_val_if_fail (G_VALUE_TYPE (value) == MOO_TYPE_PTR, NULL); ptr = g_value_get_boxed (value); return ptr->data; } else { return NULL; } }