1440 lines
38 KiB
C
1440 lines
38 KiB
C
/*
|
|
* mooutils-gobject.c
|
|
*
|
|
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* See COPYING file that comes with this distribution.
|
|
*/
|
|
|
|
#include "mooutils/mooutils-gobject.h"
|
|
#include <gobject/gvaluecollector.h>
|
|
#include <string.h>
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* GType type
|
|
*/
|
|
|
|
static gpointer
|
|
copy_gtype (gpointer boxed)
|
|
{
|
|
return boxed;
|
|
}
|
|
|
|
static void
|
|
free_gtype (G_GNUC_UNUSED gpointer boxed)
|
|
{
|
|
}
|
|
|
|
|
|
GType
|
|
moo_gtype_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
if (!type)
|
|
{
|
|
type = g_boxed_type_register_static ("GType",
|
|
copy_gtype, free_gtype);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
|
|
void
|
|
moo_value_set_gtype (GValue *value,
|
|
GType v_gtype)
|
|
{
|
|
g_value_set_static_boxed (value, (gconstpointer) v_gtype);
|
|
}
|
|
|
|
|
|
GType
|
|
moo_value_get_gtype (const GValue *value)
|
|
{
|
|
return (GType) g_value_get_boxed (value);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Converting values forth and back
|
|
*/
|
|
|
|
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 == 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);
|
|
g_type_class_unref (klass);
|
|
g_return_val_if_reached (TRUE);
|
|
}
|
|
|
|
g_value_set_static_string (dest, enum_value->value_nick);
|
|
g_type_class_unref (klass);
|
|
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
|
|
g_value_set_boolean (dest,
|
|
! g_ascii_strcasecmp (string, "1") ||
|
|
! g_ascii_strcasecmp (string, "yes") ||
|
|
! g_ascii_strcasecmp (string, "true"));
|
|
return TRUE;
|
|
}
|
|
|
|
if (dest_type == G_TYPE_DOUBLE)
|
|
{
|
|
if (!string || !string[0])
|
|
g_value_set_double (dest, 0);
|
|
else
|
|
g_value_set_double (dest, g_ascii_strtod (string, NULL));
|
|
return TRUE;
|
|
}
|
|
|
|
if (dest_type == G_TYPE_INT)
|
|
{
|
|
if (!string || !string[0])
|
|
g_value_set_int (dest, 0);
|
|
else
|
|
g_value_set_int (dest, g_ascii_strtod (string, NULL));
|
|
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))
|
|
{
|
|
gpointer klass;
|
|
GEnumClass *enum_class;
|
|
GEnumValue *enum_value;
|
|
int ival;
|
|
|
|
if (!string || !string[0])
|
|
{
|
|
g_value_set_enum (dest, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
klass = g_type_class_ref (dest_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)
|
|
{
|
|
ival = enum_value->value;
|
|
}
|
|
else
|
|
{
|
|
ival = g_ascii_strtod (string, NULL);
|
|
|
|
if (ival < enum_class->minimum || ival > enum_class->maximum)
|
|
{
|
|
g_value_set_enum (dest, ival);
|
|
g_type_class_unref (klass);
|
|
g_return_val_if_reached (TRUE);
|
|
}
|
|
}
|
|
|
|
g_value_set_enum (dest, ival);
|
|
g_type_class_unref (klass);
|
|
return TRUE;
|
|
}
|
|
|
|
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 (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);
|
|
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_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_DOUBLE ||
|
|
type == G_TYPE_STRING ||
|
|
type == GDK_TYPE_COLOR ||
|
|
G_TYPE_IS_ENUM (type);
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_value_convert_to_bool (const GValue *val)
|
|
{
|
|
GValue result;
|
|
result.g_type = 0;
|
|
g_value_init (&result, G_TYPE_BOOLEAN);
|
|
moo_value_convert (val, &result);
|
|
return g_value_get_boolean (&result);
|
|
}
|
|
|
|
|
|
int
|
|
moo_value_convert_to_int (const GValue *val)
|
|
{
|
|
GValue result;
|
|
result.g_type = 0;
|
|
g_value_init (&result, G_TYPE_INT);
|
|
moo_value_convert (val, &result);
|
|
return g_value_get_int (&result);
|
|
}
|
|
|
|
|
|
int
|
|
moo_value_convert_to_enum (const GValue *val,
|
|
GType enum_type)
|
|
{
|
|
GValue result;
|
|
result.g_type = 0;
|
|
g_value_init (&result, enum_type);
|
|
moo_value_convert (val, &result);
|
|
return g_value_get_enum (&result);
|
|
}
|
|
|
|
|
|
double
|
|
moo_value_convert_to_double (const GValue *val)
|
|
{
|
|
GValue result;
|
|
result.g_type = 0;
|
|
g_value_init (&result, G_TYPE_DOUBLE);
|
|
moo_value_convert (val, &result);
|
|
return g_value_get_double (&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);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
int
|
|
moo_convert_string_to_int (const char *string,
|
|
int default_val)
|
|
{
|
|
int int_val;
|
|
|
|
if (string)
|
|
{
|
|
GValue str_val;
|
|
str_val.g_type = 0;
|
|
g_value_init (&str_val, G_TYPE_STRING);
|
|
g_value_set_static_string (&str_val, string);
|
|
int_val = moo_value_convert_to_int (&str_val);
|
|
g_value_unset (&str_val);
|
|
}
|
|
else
|
|
{
|
|
int_val = default_val;
|
|
}
|
|
|
|
return int_val;
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_convert_string_to_bool (const char *string,
|
|
gboolean default_val)
|
|
{
|
|
gboolean bool_val;
|
|
|
|
if (string)
|
|
{
|
|
GValue str_val;
|
|
str_val.g_type = 0;
|
|
g_value_init (&str_val, G_TYPE_STRING);
|
|
g_value_set_static_string (&str_val, string);
|
|
bool_val = moo_value_convert_to_bool (&str_val);
|
|
g_value_unset (&str_val);
|
|
}
|
|
else
|
|
{
|
|
bool_val = default_val;
|
|
}
|
|
|
|
return bool_val;
|
|
}
|
|
|
|
|
|
const char*
|
|
moo_convert_bool_to_string (gboolean value)
|
|
{
|
|
GValue bool_val;
|
|
|
|
bool_val.g_type = 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;
|
|
|
|
int_val.g_type = 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;
|
|
gboolean result;
|
|
|
|
g_return_val_if_fail (G_IS_VALUE (val), FALSE);
|
|
g_return_val_if_fail (moo_value_type_supported (new_type), FALSE);
|
|
|
|
tmp.g_type = 0;
|
|
g_value_init (&tmp, new_type);
|
|
result = moo_value_convert (val, &tmp);
|
|
|
|
if (result)
|
|
{
|
|
g_value_unset (val);
|
|
memcpy (val, &tmp, sizeof (GValue));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* GParameter array manipulation
|
|
*/
|
|
|
|
static void copy_param (GParameter *dest,
|
|
GParameter *src);
|
|
|
|
GParameter*
|
|
moo_param_array_collect (GType type,
|
|
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, 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_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 ("%s: class '%s' doesn't have property '%s'",
|
|
G_STRLOC, 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: %s", G_STRLOC, 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_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 ("%s: invalid type id passed", G_STRLOC);
|
|
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: %s", G_STRLOC, 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,
|
|
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;
|
|
GParamSpec *pspec = g_object_class_find_property (klass, prop_name);
|
|
|
|
if (!pspec)
|
|
{
|
|
g_warning ("%s: class '%s' doesn't have property '%s'",
|
|
G_STRLOC, 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);
|
|
param.value.g_type = 0;
|
|
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: %s", G_STRLOC, 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);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Signal that does not require class method
|
|
*/
|
|
|
|
static void
|
|
void_marshal (G_GNUC_UNUSED GClosure *closure,
|
|
G_GNUC_UNUSED GValue *return_value,
|
|
G_GNUC_UNUSED guint n_param_values,
|
|
G_GNUC_UNUSED const GValue *param_values,
|
|
G_GNUC_UNUSED gpointer invocation_hint,
|
|
G_GNUC_UNUSED gpointer marshal_data)
|
|
{
|
|
}
|
|
|
|
|
|
static GClosure*
|
|
void_closure_new (void)
|
|
{
|
|
GClosure *closure = g_closure_new_simple (sizeof (GClosure), NULL);
|
|
g_closure_set_marshal (closure, void_marshal);
|
|
return closure;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
g_return_val_if_fail (signal_name != NULL, 0);
|
|
|
|
va_start (args, n_params);
|
|
|
|
if (handler)
|
|
signal_id = g_signal_new_valist (signal_name, itype, signal_flags,
|
|
g_cclosure_new (handler, NULL, NULL),
|
|
accumulator, accu_data, c_marshaller,
|
|
return_type, n_params, args);
|
|
else
|
|
signal_id = g_signal_new_valist (signal_name, itype, signal_flags,
|
|
void_closure_new (),
|
|
accumulator, accu_data, c_marshaller,
|
|
return_type, n_params, args);
|
|
|
|
va_end (args);
|
|
|
|
return signal_id;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* MooObjectFactory
|
|
*/
|
|
|
|
static void moo_object_factory_finalize (GObject *object);
|
|
|
|
static void moo_object_factory_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void moo_object_factory_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_OBJECT_TYPE,
|
|
PROP_FACTORY_FUNC,
|
|
PROP_FACTORY_FUNC_DATA
|
|
};
|
|
|
|
|
|
/* MOO_TYPE_OBJECT_FACTORY */
|
|
G_DEFINE_TYPE (MooObjectFactory, moo_object_factory, G_TYPE_OBJECT)
|
|
|
|
|
|
static void
|
|
moo_object_factory_class_init (MooObjectFactoryClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->set_property = moo_object_factory_set_property;
|
|
gobject_class->get_property = moo_object_factory_get_property;
|
|
gobject_class->finalize = moo_object_factory_finalize;
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_OBJECT_TYPE,
|
|
g_param_spec_boxed ("object_type",
|
|
"object_type",
|
|
"object_type",
|
|
MOO_TYPE_GTYPE,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_FACTORY_FUNC,
|
|
g_param_spec_pointer ("factory_func",
|
|
"factory_func",
|
|
"factory_func",
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_FACTORY_FUNC_DATA,
|
|
g_param_spec_pointer ("factory_func_data",
|
|
"factory_func_data",
|
|
"factory_func_data",
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
|
|
}
|
|
|
|
|
|
static void
|
|
moo_object_factory_init (MooObjectFactory *factory)
|
|
{
|
|
factory->factory_func = NULL;
|
|
factory->factory_func_data = NULL;
|
|
factory->object_type = 0;
|
|
factory->n_params = 0;
|
|
factory->params = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
moo_object_factory_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MooObjectFactory *factory = MOO_OBJECT_FACTORY (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_FACTORY_FUNC:
|
|
g_value_set_pointer (value, (gpointer)factory->factory_func);
|
|
break;
|
|
|
|
case PROP_FACTORY_FUNC_DATA:
|
|
g_value_set_pointer (value, factory->factory_func_data);
|
|
break;
|
|
|
|
case PROP_OBJECT_TYPE:
|
|
moo_value_set_gtype (value, factory->object_type);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
moo_object_factory_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MooObjectFactory *factory = MOO_OBJECT_FACTORY (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_FACTORY_FUNC:
|
|
factory->factory_func = (MooObjectFactoryFunc)g_value_get_pointer (value);
|
|
break;
|
|
|
|
case PROP_FACTORY_FUNC_DATA:
|
|
factory->factory_func_data = g_value_get_pointer (value);
|
|
break;
|
|
|
|
case PROP_OBJECT_TYPE:
|
|
factory->object_type = moo_value_get_gtype (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
moo_object_factory_finalize (GObject *object)
|
|
{
|
|
MooObjectFactory *factory = MOO_OBJECT_FACTORY (object);
|
|
moo_param_array_free (factory->params, factory->n_params);
|
|
G_OBJECT_CLASS (moo_object_factory_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
gpointer
|
|
moo_object_factory_create_object (MooObjectFactory *factory,
|
|
gpointer data,
|
|
const char *prop_name,
|
|
...)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_OBJECT_FACTORY (factory), NULL);
|
|
|
|
if (factory->factory_func)
|
|
{
|
|
if (factory->factory_func_data)
|
|
data = factory->factory_func_data;
|
|
return factory->factory_func (data, factory);
|
|
}
|
|
|
|
if (!prop_name)
|
|
{
|
|
return g_object_newv (factory->object_type,
|
|
factory->n_params,
|
|
factory->params);
|
|
}
|
|
else
|
|
{
|
|
GObject *object;
|
|
GParameter *params;
|
|
guint n_params;
|
|
va_list var_args;
|
|
|
|
va_start (var_args, prop_name);
|
|
params = moo_param_array_add_valist (factory->object_type,
|
|
factory->params,
|
|
factory->n_params,
|
|
&n_params,
|
|
prop_name,
|
|
var_args);
|
|
va_end (var_args);
|
|
|
|
g_return_val_if_fail (params != NULL, NULL);
|
|
object = g_object_newv (factory->object_type,
|
|
n_params,
|
|
params);
|
|
moo_param_array_free (params, n_params);
|
|
return object;
|
|
}
|
|
}
|
|
|
|
|
|
MooObjectFactory*
|
|
moo_object_factory_new_func (MooObjectFactoryFunc factory_func,
|
|
gpointer data)
|
|
{
|
|
return g_object_new (MOO_TYPE_OBJECT_FACTORY,
|
|
"factory_func", factory_func,
|
|
"factory_func_data", data,
|
|
NULL);
|
|
}
|
|
|
|
|
|
MooObjectFactory*
|
|
moo_object_factory_new (GType object_type,
|
|
const char *first_prop_name,
|
|
...)
|
|
{
|
|
MooObjectFactory *factory;
|
|
va_list var_args;
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);
|
|
|
|
va_start (var_args, first_prop_name);
|
|
factory = moo_object_factory_new_valist (object_type, first_prop_name, var_args);
|
|
va_end (var_args);
|
|
|
|
return factory;
|
|
}
|
|
|
|
|
|
MooObjectFactory*
|
|
moo_object_factory_new_valist (GType object_type,
|
|
const char *first_prop_name,
|
|
va_list var_args)
|
|
{
|
|
GParameter *params = NULL;
|
|
guint n_params = 0;
|
|
MooObjectFactory *factory;
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);
|
|
|
|
if (!first_prop_name)
|
|
return g_object_new (MOO_TYPE_OBJECT_FACTORY,
|
|
"object_type", object_type,
|
|
NULL);
|
|
|
|
params = moo_param_array_collect_valist (object_type,
|
|
&n_params,
|
|
first_prop_name,
|
|
var_args);
|
|
g_return_val_if_fail (params != NULL, NULL);
|
|
|
|
factory = g_object_new (MOO_TYPE_OBJECT_FACTORY,
|
|
"object_type", object_type,
|
|
NULL);
|
|
factory->n_params = n_params;
|
|
factory->params = params;
|
|
|
|
return factory;
|
|
}
|
|
|
|
|
|
MooObjectFactory*
|
|
moo_object_factory_new_a (GType object_type,
|
|
GParameter *params,
|
|
guint n_params)
|
|
{
|
|
MooObjectFactory *factory;
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);
|
|
g_return_val_if_fail (params != NULL, NULL);
|
|
|
|
factory = g_object_new (MOO_TYPE_OBJECT_FACTORY,
|
|
"object_type", object_type,
|
|
NULL);
|
|
|
|
factory->params = params;
|
|
factory->n_params = n_params;
|
|
|
|
return factory;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Property watch
|
|
*/
|
|
|
|
typedef struct {
|
|
GObject *source;
|
|
GObject *target;
|
|
GParamSpec *source_prop;
|
|
GParamSpec *target_prop;
|
|
guint id;
|
|
MooTransformPropFunc transform;
|
|
gpointer data;
|
|
GDestroyNotify destroy_notify;
|
|
} ObjectWatch;
|
|
|
|
|
|
static GHashTable *watches = NULL;
|
|
static guint watch_last_id = 0;
|
|
|
|
|
|
static void source_died (ObjectWatch *watch);
|
|
static void target_died (ObjectWatch *watch);
|
|
static void watch_check (ObjectWatch *watch);
|
|
static void watch_die (ObjectWatch *watch);
|
|
|
|
|
|
static ObjectWatch*
|
|
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)
|
|
{
|
|
ObjectWatch *watch;
|
|
GObjectClass *target_class, *source_class;
|
|
char *signal_name = NULL;
|
|
|
|
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));
|
|
|
|
watch = g_new0 (ObjectWatch, 1);
|
|
|
|
watch->source = source;
|
|
watch->target = target;
|
|
|
|
watch->source_prop = g_object_class_find_property (source_class, source_prop);
|
|
if (!watch->source_prop)
|
|
{
|
|
g_warning ("%s: could not find property '%s' in class '%s'",
|
|
G_STRLOC, source_prop, g_type_name (G_OBJECT_TYPE (source)));
|
|
g_free (watch);
|
|
return NULL;
|
|
}
|
|
|
|
watch->target_prop = g_object_class_find_property (target_class, target_prop);
|
|
if (!watch->target_prop)
|
|
{
|
|
g_warning ("%s: could not find property '%s' in class '%s'",
|
|
G_STRLOC, target_prop, g_type_name (G_OBJECT_TYPE (target)));
|
|
g_free (watch);
|
|
return NULL;
|
|
}
|
|
|
|
watch->transform = transform;
|
|
watch->data = transform_data;
|
|
watch->destroy_notify = destroy_notify;
|
|
|
|
if (!signal)
|
|
{
|
|
signal_name = g_strdup_printf ("notify::%s", source_prop);
|
|
signal = signal_name;
|
|
}
|
|
|
|
g_signal_connect_swapped (source, signal,
|
|
G_CALLBACK (watch_check), watch);
|
|
g_object_weak_ref (source, (GWeakNotify) source_died, watch);
|
|
g_object_weak_ref (target, (GWeakNotify) target_died, watch);
|
|
|
|
watch_last_id++;
|
|
watch->id = watch_last_id;
|
|
if (!watches)
|
|
watches = g_hash_table_new (g_direct_hash, g_direct_equal);
|
|
g_hash_table_insert (watches, GUINT_TO_POINTER (watch->id), watch);
|
|
|
|
g_free (signal_name);
|
|
return watch;
|
|
}
|
|
|
|
|
|
static void
|
|
watch_die (ObjectWatch *watch)
|
|
{
|
|
if (watch->source)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (watch->source,
|
|
(gpointer) watch_check,
|
|
watch);
|
|
g_object_weak_unref (watch->source, (GWeakNotify) source_died, watch);
|
|
}
|
|
|
|
if (watch->target)
|
|
g_object_weak_unref (watch->target, (GWeakNotify) target_died, watch);
|
|
|
|
if (watch->destroy_notify)
|
|
watch->destroy_notify (watch->data);
|
|
|
|
g_hash_table_remove (watches, GUINT_TO_POINTER (watch->id));
|
|
|
|
g_free (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)
|
|
{
|
|
ObjectWatch *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);
|
|
|
|
if (!watch)
|
|
return 0;
|
|
|
|
watch_check (watch);
|
|
return watch->id;
|
|
}
|
|
|
|
|
|
static void
|
|
source_died (ObjectWatch *watch)
|
|
{
|
|
watch->source = NULL;
|
|
watch_die (watch);
|
|
}
|
|
|
|
|
|
static void
|
|
target_died (ObjectWatch *watch)
|
|
{
|
|
watch->target = NULL;
|
|
watch_die (watch);
|
|
}
|
|
|
|
|
|
static void
|
|
watch_check (ObjectWatch *watch)
|
|
{
|
|
GValue source_val, target_val;
|
|
|
|
if (!G_IS_OBJECT (watch->source) ||
|
|
!G_IS_OBJECT (watch->target))
|
|
{
|
|
watch_die (watch);
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
source_val.g_type = 0;
|
|
target_val.g_type = 0;
|
|
|
|
g_value_init (&source_val, watch->source_prop->value_type);
|
|
g_value_init (&target_val, watch->target_prop->value_type);
|
|
|
|
g_object_get_property (watch->source,
|
|
watch->source_prop->name,
|
|
&source_val);
|
|
watch->transform (&target_val, &source_val, watch->data);
|
|
g_object_set_property (watch->target,
|
|
watch->target_prop->name,
|
|
&target_val);
|
|
|
|
g_value_unset (&source_val);
|
|
g_value_unset (&target_val);
|
|
}
|
|
|
|
|
|
void
|
|
moo_copy_boolean (GValue *target,
|
|
const GValue *source,
|
|
G_GNUC_UNUSED gpointer dummy)
|
|
{
|
|
g_value_set_boolean (target, g_value_get_boolean (source));
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
void
|
|
moo_bind_sensitive (GtkWidget *btn,
|
|
GtkWidget **dependent,
|
|
int num_dependent,
|
|
gboolean invert)
|
|
{
|
|
int i;
|
|
for (i = 0; i < num_dependent; ++i)
|
|
moo_bind_bool_property (G_OBJECT (dependent[i]), "sensitive",
|
|
G_OBJECT (btn), "active", invert);
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_watch_remove (guint watch_id)
|
|
{
|
|
ObjectWatch *watch;
|
|
|
|
if (!watches)
|
|
return FALSE;
|
|
|
|
watch = g_hash_table_lookup (watches, GUINT_TO_POINTER (watch_id));
|
|
|
|
if (!watch)
|
|
return FALSE;
|
|
|
|
watch_die (watch);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
moo_watch_add_signal (guint watch_id,
|
|
const char *source_signal)
|
|
{
|
|
ObjectWatch *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_signal_connect_swapped (watch->source, source_signal,
|
|
G_CALLBACK (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);
|
|
}
|