/* * mooui/moouiobject.c * * Copyright (C) 2004-2005 by Yevgen Muntyan * * 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 "mooui/moouiobject-impl.h" #include "mooui/mooaccel.h" #include "mooui/mootoggleaction.h" #include "mooui/moomenuaction.h" #include "mooutils/mooprefs.h" #include #include static const GQuark *get_quark (void) G_GNUC_CONST; #define MOO_UI_OBJECT_NAME_QUARK (get_quark()[0]) #define MOO_UI_OBJECT_ID_QUARK (get_quark()[1]) #define MOO_UI_OBJECT_ACTIONS_QUARK (get_quark()[2]) #define MOO_UI_OBJECT_UI_XML_QUARK (get_quark()[3]) static void moo_ui_object_iface_init (gpointer g_iface); static void moo_ui_object_set_id (MooUIObject *object, const char *id); static void moo_ui_object_add_class_actions (MooUIObject *object); static void xml_changed (MooUIObject *object); static void unref_xml (GObject *xml, gpointer object); GType moo_ui_object_get_type (void) { static GType type = 0; if (!type) { static const GTypeInfo info = { sizeof (MooUIObjectIface), /* class_size */ moo_ui_object_iface_init, /* base_init */ NULL, /* base_finalize */ NULL, /* class_init */ 0, 0, 0, 0, 0, 0 }; type = g_type_register_static (G_TYPE_INTERFACE, "MooUIObject", &info, (GTypeFlags)0); } return type; } static void moo_ui_object_iface_init (G_GNUC_UNUSED gpointer g_iface) { static gboolean done = FALSE; if (done) return; done = TRUE; g_object_interface_install_property (g_iface, g_param_spec_string ("ui-object-name", "ui-object-name", "ui-object-name", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_interface_install_property (g_iface, g_param_spec_string ("ui-object-id", "ui-object-id", "ui-object-id", NULL, G_PARAM_READABLE)); g_object_interface_install_property (g_iface, g_param_spec_object ("ui-object-actions", "ui-object-actions", "ui-object-actions", MOO_TYPE_ACTION_GROUP, G_PARAM_READABLE)); g_object_interface_install_property (g_iface, g_param_spec_object ("ui-object-xml", "ui-object-xml", "ui-object-xml", MOO_TYPE_UI_XML, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); } const char *_moo_ui_object_get_name_impl (MooUIObject *object) { g_return_val_if_fail (MOO_IS_UI_OBJECT (object), NULL); return g_object_get_qdata (G_OBJECT (object), MOO_UI_OBJECT_NAME_QUARK); } const char *_moo_ui_object_get_id_impl (MooUIObject *object) { g_return_val_if_fail (MOO_IS_UI_OBJECT (object), NULL); return g_object_get_qdata (G_OBJECT (object), MOO_UI_OBJECT_ID_QUARK); } void _moo_ui_object_set_name_impl (MooUIObject *object, const char *name) { g_return_if_fail (MOO_IS_UI_OBJECT (object) && name != NULL); g_object_set_qdata_full (G_OBJECT (object), MOO_UI_OBJECT_NAME_QUARK, g_strdup (name), (GDestroyNotify) g_free); moo_action_group_set_name (moo_ui_object_get_actions (object), name); g_object_notify (G_OBJECT (object), "ui-object-name"); } static void moo_ui_object_set_id (MooUIObject *object, const char *id) { g_return_if_fail (MOO_IS_UI_OBJECT (object) && id != NULL); g_object_set_qdata_full (G_OBJECT (object), MOO_UI_OBJECT_ID_QUARK, g_strdup (id), (GDestroyNotify) g_free); } MooActionGroup *_moo_ui_object_get_actions_impl(MooUIObject *object) { MooActionGroup *group; g_return_val_if_fail (MOO_IS_UI_OBJECT (object), NULL); group = g_object_get_qdata (G_OBJECT (object), MOO_UI_OBJECT_ACTIONS_QUARK); if (!group) { group = moo_action_group_new (moo_ui_object_get_name (object)); g_object_set_qdata_full (G_OBJECT (object), MOO_UI_OBJECT_ACTIONS_QUARK, group, (GDestroyNotify) g_object_unref); g_object_notify (G_OBJECT (object), "ui-object-actions"); } return group; } MooUIXML *_moo_ui_object_get_ui_xml_impl (MooUIObject *object) { MooUIXML *xml; g_return_val_if_fail (MOO_IS_UI_OBJECT (object), NULL); xml = g_object_get_qdata (G_OBJECT (object), MOO_UI_OBJECT_UI_XML_QUARK); if (!xml) { xml = moo_ui_xml_new (); g_signal_connect_swapped (xml, "changed", G_CALLBACK (xml_changed), object); g_object_set_qdata_full (G_OBJECT (object), MOO_UI_OBJECT_UI_XML_QUARK, xml, (GDestroyNotify) unref_xml); g_object_notify (G_OBJECT (object), "ui-object-xml"); } return xml; } void _moo_ui_object_set_ui_xml_impl (MooUIObject *object, MooUIXML *xml) { MooUIXML *old; g_return_if_fail (MOO_IS_UI_OBJECT (object)); old = g_object_get_qdata (G_OBJECT (object), MOO_UI_OBJECT_UI_XML_QUARK); if (old == xml) return; if (xml) { g_object_ref (G_OBJECT (xml)); g_signal_connect_swapped (xml, "changed", G_CALLBACK (xml_changed), object); g_object_set_qdata_full (G_OBJECT (object), MOO_UI_OBJECT_UI_XML_QUARK, xml, (GDestroyNotify) unref_xml); } else { g_object_set_qdata (G_OBJECT (object), MOO_UI_OBJECT_UI_XML_QUARK, NULL); } g_object_notify (G_OBJECT (object), "ui-object-xml"); } static void xml_changed (MooUIObject *object) { g_object_notify (G_OBJECT (object), "ui-object-xml"); } static void unref_xml (GObject *xml, gpointer object) { g_signal_handlers_disconnect_by_func (xml, (gpointer)xml_changed, object); g_object_unref (xml); } /****************************************************************************/ typedef struct { MooObjectFactory *action; MooObjectFactory *closure; } FactoryData; static MooAction *create_action (FactoryData *data, MooUIObject *object) { MooClosure *closure = NULL; MooAction *action; const char *class_id; g_return_val_if_fail (data != NULL && data->action != NULL, NULL); class_id = moo_ui_object_class_get_id (G_OBJECT_GET_CLASS (object)); action = MOO_ACTION (moo_object_factory_create_object (data->action, object, "group-id", class_id, NULL)); g_return_val_if_fail (action != NULL, NULL); if (g_type_is_a (data->action->object_type, MOO_TYPE_TOGGLE_ACTION)) { g_object_set (action, "toggled-data", object, NULL); } if (g_type_is_a (data->action->object_type, MOO_TYPE_MENU_ACTION)) { g_object_set (action, "create-menu-data", object, "no-accel", TRUE, NULL); } if (data->closure) { closure = MOO_CLOSURE (moo_object_factory_create_object (data->closure, NULL, NULL)); g_return_val_if_fail (closure != NULL, action); g_object_set (closure, "data", object, NULL); g_object_set (action, "closure", closure, NULL); } return action; } void moo_ui_object_class_init (GObjectClass *klass, const char *id, const char *name) { GType type; g_return_if_fail (G_IS_OBJECT_CLASS (klass)); g_return_if_fail (id != NULL && name != NULL); type = G_OBJECT_CLASS_TYPE (klass); g_return_if_fail (g_type_get_qdata (type, MOO_UI_OBJECT_ID_QUARK) == NULL); g_type_set_qdata (type, MOO_UI_OBJECT_ID_QUARK, g_strdup (id)); g_return_if_fail (g_type_get_qdata (type, MOO_UI_OBJECT_NAME_QUARK) == NULL); g_type_set_qdata (type, MOO_UI_OBJECT_NAME_QUARK, g_strdup (name)); } const char *moo_ui_object_class_get_id (GObjectClass *klass) { GType type; g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), NULL); type = G_OBJECT_CLASS_TYPE (klass); return g_type_get_qdata (type, MOO_UI_OBJECT_ID_QUARK); } const char *moo_ui_object_class_get_name (GObjectClass *klass) { GType type; g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), NULL); type = G_OBJECT_CLASS_TYPE (klass); return g_type_get_qdata (type, MOO_UI_OBJECT_NAME_QUARK); } void moo_ui_object_class_install_action (GObjectClass *klass, MooObjectFactory *action, MooObjectFactory *closure) { GArray *array; FactoryData d = {action, closure}; GType type; g_return_if_fail (G_IS_OBJECT_CLASS (klass)); g_return_if_fail (MOO_IS_OBJECT_FACTORY (action)); g_return_if_fail (closure == NULL || MOO_IS_OBJECT_FACTORY (closure)); type = G_OBJECT_CLASS_TYPE (klass); array = g_type_get_qdata (type, MOO_UI_OBJECT_ACTIONS_QUARK); if (!array) { array = g_array_new (FALSE, FALSE, sizeof (FactoryData)); g_type_set_qdata (type, MOO_UI_OBJECT_ACTIONS_QUARK, array); } g_array_append_val (array, d); } void moo_ui_object_init (MooUIObject *object) { GType type; GObjectClass *klass; type = G_OBJECT_TYPE (object); klass = g_type_class_ref (type); g_return_if_fail (klass != NULL); moo_ui_object_set_id (object, moo_ui_object_class_get_id (klass)); moo_ui_object_set_name (object, moo_ui_object_class_get_name (klass)); moo_ui_object_add_class_actions (object); } static void moo_ui_object_add_class_actions (MooUIObject *object) { GType type; g_return_if_fail (G_IS_OBJECT (object)); type = G_OBJECT_TYPE (object); do { GArray *array; guint i; array = g_type_get_qdata (type, MOO_UI_OBJECT_ACTIONS_QUARK); if (array) for (i = 0; i < array->len; ++i) { FactoryData *d = &g_array_index (array, FactoryData, i); MooAction *action = create_action (d, object); if (action) moo_ui_object_add_action (object, action); } type = g_type_parent (type); g_return_if_fail (type != 0); } while (type != G_TYPE_OBJECT); } void moo_ui_object_add_action (MooUIObject *object, MooAction *action) { MooActionGroup *group; g_return_if_fail (MOO_IS_UI_OBJECT (object)); g_return_if_fail (MOO_IS_ACTION (action)); group = moo_ui_object_get_actions (object); moo_action_group_add_action (group, action); if (!action->dead) { const char *accel; accel = moo_prefs_get_accel (moo_action_get_accel_path (action)); if (accel) moo_action_set_accel (action, accel); } } void moo_ui_object_class_new_action (GObjectClass *klass, const char *first_prop_name, ...) { va_list args; va_start (args, first_prop_name); moo_ui_object_class_new_actionv (klass, first_prop_name, args); va_end (args); } void moo_ui_object_class_new_actionv(GObjectClass *object_class, const char *first_prop_name, va_list var_args) { const char *name; GType action_type = 0, closure_type = 0; GObjectClass *action_class = NULL; GObjectClass *closure_class = NULL; GArray *action_params = NULL; GArray *closure_params = NULL; g_return_if_fail (G_IS_OBJECT_CLASS (object_class)); g_return_if_fail (first_prop_name != NULL); action_params = g_array_new (FALSE, TRUE, sizeof (GParameter)); closure_params = g_array_new (FALSE, TRUE, sizeof (GParameter)); name = first_prop_name; while (name) { GParameter param = {0}; GParamSpec *pspec; char *err = NULL; if (strstr (name, "::") && strncmp (name, "closure::", strlen ("closure::"))) { if (!strcmp (name, "action-type::") || !strcmp (name, "action_type::")) { g_value_init (¶m.value, G_TYPE_POINTER); G_VALUE_COLLECT (¶m.value, var_args, 0, &err); if (err) { g_warning ("%s: %s", G_STRLOC, err); g_free (err); goto error; } action_type = (GType) param.value.data[0].v_pointer; if (!g_type_is_a (action_type, MOO_TYPE_ACTION)) { g_warning ("%s: invalid action type", G_STRLOC); goto error; } action_class = g_type_class_ref (action_type); } else if (!strcmp (name, "closure-type::") || !strcmp (name, "closure_type::")) { g_value_init (¶m.value, G_TYPE_POINTER); G_VALUE_COLLECT (¶m.value, var_args, 0, &err); if (err) { g_warning ("%s: %s", G_STRLOC, err); g_free (err); goto error; } closure_type = (GType) param.value.data[0].v_pointer; if (!g_type_is_a (closure_type, MOO_TYPE_CLOSURE)) { g_warning ("%s: invalid closure type", G_STRLOC); goto error; } closure_class = g_type_class_ref (closure_type); } else { g_warning ("%s: invalid argument %s", G_STRLOC, name); goto error; } } else if (!strstr (name, "::")) { /* action property */ if (!action_class) { if (!action_type) action_type = MOO_TYPE_ACTION; action_class = g_type_class_ref (action_type); } pspec = g_object_class_find_property (action_class, name); if (!pspec) { g_warning ("%s: object class `%s' has no property named `%s'", G_STRLOC, g_type_name (action_type), name); goto error; } g_value_init (¶m.value, G_PARAM_SPEC_VALUE_TYPE (pspec)); G_VALUE_COLLECT (¶m.value, var_args, 0, &err); if (err) { g_warning ("%s: %s", G_STRLOC, err); g_free (err); g_value_unset (¶m.value); goto error; } param.name = g_strdup (name); g_array_append_val (action_params, param); } else { /* closure property */ const char *suffix = strstr (name, "::"); if (!suffix || !suffix[1] || !suffix[2]) { g_warning ("%s: invalid property name '%s'", G_STRLOC, name); goto error; } name = suffix + 2; if (!closure_class) { if (!closure_type) closure_type = MOO_TYPE_CLOSURE; closure_class = g_type_class_ref (closure_type); } pspec = g_object_class_find_property (closure_class, name); if (!pspec) { g_warning ("%s: object class `%s' has no property named `%s'", G_STRLOC, g_type_name (closure_type), name); goto error; } g_value_init (¶m.value, G_PARAM_SPEC_VALUE_TYPE (pspec)); G_VALUE_COLLECT (¶m.value, var_args, 0, &err); if (err) { g_warning ("%s: %s", G_STRLOC, err); g_free (err); g_value_unset (¶m.value); goto error; } param.name = g_strdup (name); g_array_append_val (closure_params, param); } name = va_arg (var_args, gchar*); } { MooObjectFactory *action_factory = NULL; MooObjectFactory *closure_factory = NULL; if (closure_params->len) { closure_factory = moo_object_factory_new_a (closure_type, (GParameter*) closure_params->data, closure_params->len); if (!closure_factory) { g_warning ("%s: error in moo_object_factory_new_a()", G_STRLOC); goto error; } g_array_free (closure_params, FALSE); closure_params = NULL; } else { g_array_free (closure_params, TRUE); closure_params = NULL; } action_factory = moo_object_factory_new_a (action_type, (GParameter*) action_params->data, action_params->len); if (!action_factory) { g_warning ("%s: error in moo_object_factory_new_a()", G_STRLOC); g_object_unref (closure_factory); goto error; } g_array_free (action_params, FALSE); action_params = NULL; moo_ui_object_class_install_action (object_class, action_factory, closure_factory); if (action_class) g_type_class_unref (action_class); if (closure_class) g_type_class_unref (closure_class); return; } error: if (action_params) { guint i; GParameter *params = (GParameter*) action_params->data; for (i = 0; i < action_params->len; ++i) { g_value_unset (¶ms[i].value); g_free ((char*) params[i].name); } g_array_free (action_params, TRUE); } if (closure_params) { guint i; GParameter *params = (GParameter*) closure_params->data; for (i = 0; i < closure_params->len; ++i) { g_value_unset (¶ms[i].value); g_free ((char*) params[i].name); } g_array_free (closure_params, TRUE); } if (action_class) g_type_class_unref (action_class); if (closure_class) g_type_class_unref (closure_class); } static const GQuark *get_quark (void) { static GQuark q[4] = {0, 0, 0, 0}; if (!q[0]) { q[0] = g_quark_from_static_string ("moo_ui_object_actions"); q[1] = g_quark_from_static_string ("moo_ui_object_ui_xml"); q[2] = g_quark_from_static_string ("moo_ui_object_id"); q[3] = g_quark_from_static_string ("moo_ui_object_name"); } return q; } MooUIXML *moo_ui_object_get_ui_xml (MooUIObject *object) { MooUIXML *xml; g_return_val_if_fail (MOO_IS_UI_OBJECT (object), NULL); g_object_get (G_OBJECT (object), "ui-object-xml", &xml, NULL); if (xml) g_object_unref (xml); return xml; } void moo_ui_object_set_ui_xml (MooUIObject *object, MooUIXML *xml) { g_return_if_fail (MOO_IS_UI_OBJECT (object)); g_return_if_fail (!xml || MOO_IS_UI_XML (xml)); g_object_set (G_OBJECT (object), "ui-object-xml", xml, NULL); } MooActionGroup *moo_ui_object_get_actions (MooUIObject *object) { MooActionGroup *actions; g_return_val_if_fail (MOO_IS_UI_OBJECT (object), NULL); g_object_get (G_OBJECT (object), "ui-object-actions", &actions, NULL); if (actions) g_object_unref (actions); return actions; } const char *moo_ui_object_get_name (MooUIObject *object) { static char *name = NULL; g_free (name); g_return_val_if_fail (MOO_IS_UI_OBJECT (object), NULL); g_object_get (G_OBJECT (object), "ui-object-name", &name, NULL); return name; } const char *moo_ui_object_get_id (MooUIObject *object) { static char *id = NULL; g_free (id); g_return_val_if_fail (MOO_IS_UI_OBJECT (object), NULL); g_object_get (G_OBJECT (object), "ui-object-id", &id, NULL); return id; } void moo_ui_object_set_name (MooUIObject *object, const char *name) { g_return_if_fail (MOO_IS_UI_OBJECT (object)); g_object_set (G_OBJECT (object), "ui-object-name", name, NULL); }