/* * Copyright (C) 2008 Tristan Van Berkom * Copyright (C) 2004 Joaquin Cuenca Abela * Copyright (C) 2001, 2002, 2003 Ximian, Inc. * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Authors: * Joaquin Cuenca Abela * Chema Celorio * Tristan Van Berkom */ #ifdef HAVE_CONFIG_H #include #endif /** * SECTION:glade-widget * @Short_Description: An object wrapper for the Glade runtime environment. * * #GladeWidget is the proxy between the instantiated runtime object and * the Glade core metadata. This api will be mostly usefull for its * convenience api for getting and setting properties (mostly from the plugin). */ #include #include #include #include #include "glade.h" #include "glade-accumulators.h" #include "glade-project.h" #include "glade-widget-adaptor.h" #include "glade-private.h" #include "glade-marshallers.h" #include "glade-property.h" #include "glade-property-class.h" #include "glade-placeholder.h" #include "glade-signal.h" #include "glade-popup.h" #include "glade-editor.h" #include "glade-app.h" #include "glade-design-view.h" #include "glade-widget-action.h" #include "glade-signal-model.h" #include "glade-object-stub.h" #include "glade-dnd.h" static void glade_widget_set_adaptor (GladeWidget *widget, GladeWidgetAdaptor *adaptor); static void glade_widget_set_properties (GladeWidget *widget, GList *properties); static void glade_widget_set_object (GladeWidget *gwidget, GObject *new_object); struct _GladeWidgetPrivate { GladeWidgetAdaptor *adaptor; /* An adaptor class for the object type */ GladeProject *project; /* A pointer to the project that this widget currently belongs to. */ GladeWidget *parent; /* A pointer to the parent widget in the hierarchy */ gchar *name; /* The name of the widget. For example window1 or * button2. This is a unique name and is the one * used when loading widget with libglade */ gchar *support_warning; /* A warning message for version incompatabilities * in this widget */ gchar *internal; /* If the widget is an internal child of * another widget this is the name of the * internal child, otherwise is NULL. * Internal children cannot be deleted. */ gboolean anarchist; /* Some composite widgets have internal children * that are not part of the same hierarchy; hence 'anarchists', * typicly a popup window or its child (we need to mark * them so we can avoid bookkeeping packing props on them etc.). */ GObject *object; /* A pointer to the object that was created. * if it is a GtkWidget; it is shown as a "view" * of the GladeWidget. This object is updated as * the properties are modified for the GladeWidget. */ GList *properties; /* A list of GladeProperty. A GladeProperty is an * instance of a GladePropertyClass. If a * GladePropertyClass for a gtkbutton is label, its * property is "Ok". */ GList *packing_properties; /* A list of GladeProperty. Note that these * properties are related to the container * of the widget, thus they change after * pasting the widget to a different * container. Toplevels widget do not have * packing properties. * See also child_properties of * GladeWidgetClass. */ GHashTable *props_hash; /* A Quick reference table to speed up calls to glade_widget_get_property() */ GHashTable *pack_props_hash; /* A Quick reference table to speed up calls to glade_widget_get_pack_property() */ GHashTable *signals; /* A table with a GPtrArray of GladeSignals (signal handlers), * indexed by its name */ GList *prop_refs; /* List of properties in the project who's value are `this object' * (this is used to set/unset those properties when the object is * added/removed from the project). */ gint width; /* Current size used in the UI, this is only */ gint height; /* usefull for parentless widgets in the * GladeDesignLayout */ GList *actions; /* A GladeWidgetAction list */ GList *packing_actions; /* A GladeWidgetAction list, this actions are * related to the container and they are not always present. */ GladeWidget *lock; /* The glade widget that has locked this widget down. */ GList *locked_widgets; /* A list of widgets this widget has locked down. */ GtkTreeModel *signal_model; /* Signal model (or NULL if not yet requested) */ /* Construct parameters: */ GladeWidget *construct_template; GladeCreateReason construct_reason; gchar *construct_internal; guint construct_exact : 1; guint in_project : 1; guint visible : 1; /* Local copy of widget visibility, we need to keep track of this * since the objects copy may be invalid due to a rebuild. */ guint rebuilding : 1; guint composite : 1; }; enum { ADD_SIGNAL_HANDLER, REMOVE_SIGNAL_HANDLER, CHANGE_SIGNAL_HANDLER, BUTTON_PRESS_EVENT, BUTTON_RELEASE_EVENT, MOTION_NOTIFY_EVENT, SUPPORT_CHANGED, LAST_SIGNAL }; enum { PROP_0, PROP_NAME, PROP_INTERNAL, PROP_ANARCHIST, PROP_ADAPTOR, PROP_OBJECT, PROP_PROJECT, PROP_PROPERTIES, PROP_PARENT, PROP_INTERNAL_NAME, PROP_TEMPLATE, PROP_TEMPLATE_EXACT, PROP_REASON, PROP_TOPLEVEL_WIDTH, PROP_TOPLEVEL_HEIGHT, PROP_SUPPORT_WARNING, PROP_VISIBLE, PROP_COMPOSITE, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES]; static guint glade_widget_signals[LAST_SIGNAL] = { 0 }; static GQuark glade_widget_name_quark = 0; static void glade_widget_drag_init (_GladeDragInterface *iface); G_DEFINE_TYPE_WITH_CODE (GladeWidget, glade_widget, G_TYPE_INITIALLY_UNOWNED, G_ADD_PRIVATE (GladeWidget) G_IMPLEMENT_INTERFACE (GLADE_TYPE_DRAG, glade_widget_drag_init)) /******************************************************************************* GladeWidget class methods *******************************************************************************/ static void glade_widget_set_packing_actions (GladeWidget *widget, GladeWidget *parent) { if (widget->priv->packing_actions) { g_list_foreach (widget->priv->packing_actions, (GFunc) g_object_unref, NULL); g_list_free (widget->priv->packing_actions); widget->priv->packing_actions = NULL; } widget->priv->packing_actions = glade_widget_adaptor_pack_actions_new (parent->priv->adaptor); } static void glade_widget_add_child_impl (GladeWidget *widget, GladeWidget *child, gboolean at_mouse) { g_object_ref (child); /* Safe to set the parent first... setting it afterwards * creates packing properties, and that is not always * desirable. */ glade_widget_set_parent (child, widget); /* Set packing actions first so we have access from the plugin */ glade_widget_set_packing_actions (child, widget); glade_widget_adaptor_add (widget->priv->adaptor, widget->priv->object, child->priv->object); /* XXX FIXME: * We have a fundamental flaw here, we set packing props * after parenting the widget so that we can introspect the * values setup by the runtime widget, in which case the plugin * cannot access its packing properties and set them sensitive * or connect to thier signals etc. maybe its not so important * but its a flaw worthy of note, some kind of double pass api * would be needed to accomadate this. */ /* Setup packing properties here so we can introspect the new * values from the backend. */ glade_widget_set_packing_properties (child, widget); } static void glade_widget_remove_child_impl (GladeWidget *widget, GladeWidget *child) { glade_widget_adaptor_remove (widget->priv->adaptor, widget->priv->object, child->priv->object); child->priv->parent = NULL; g_object_unref (child); } static void glade_widget_replace_child_impl (GladeWidget *widget, GObject *old_object, GObject *new_object) { GladeWidget *gnew_widget = glade_widget_get_from_gobject (new_object); GladeWidget *gold_widget = glade_widget_get_from_gobject (old_object); if (gnew_widget) { g_object_ref (gnew_widget); gnew_widget->priv->parent = widget; /* Set packing actions first so we have access from the plugin */ glade_widget_set_packing_actions (gnew_widget, widget); } if (gold_widget) { g_object_unref (gold_widget); if (gold_widget != gnew_widget) gold_widget->priv->parent = NULL; } glade_widget_adaptor_replace_child (widget->priv->adaptor, widget->priv->object, old_object, new_object); /* Setup packing properties here so we can introspect the new * values from the backend. */ if (gnew_widget) glade_widget_set_packing_properties (gnew_widget, widget); } /** * glade_widget_add_signal_handler: * @widget: A #GladeWidget * @signal_handler: The #GladeSignal * * Adds a signal handler for @widget */ void glade_widget_add_signal_handler (GladeWidget *widget, const GladeSignal *signal_handler) { GPtrArray *signals; GladeSignal *new_signal_handler; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_SIGNAL (signal_handler)); signals = glade_widget_list_signal_handlers (widget, glade_signal_get_name (signal_handler)); if (!signals) { signals = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); g_hash_table_insert (widget->priv->signals, g_strdup (glade_signal_get_name (signal_handler)), signals); } new_signal_handler = glade_signal_clone (signal_handler); g_ptr_array_add (signals, new_signal_handler); g_signal_emit (widget, glade_widget_signals[ADD_SIGNAL_HANDLER], 0, new_signal_handler); glade_project_verify_signal (widget, new_signal_handler); if (glade_signal_get_support_warning (new_signal_handler)) glade_widget_verify (widget); } /** * glade_widget_remove_signal_handler: * @widget: A #GladeWidget * @signal_handler: The #GladeSignal * * Removes a signal handler from @widget */ void glade_widget_remove_signal_handler (GladeWidget *widget, const GladeSignal *signal_handler) { GPtrArray *signals; GladeSignal *tmp_signal_handler; guint i; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_SIGNAL (signal_handler)); signals = glade_widget_list_signal_handlers (widget, glade_signal_get_name (signal_handler)); /* trying to remove an inexistent signal? */ g_assert (signals); for (i = 0; i < signals->len; i++) { tmp_signal_handler = g_ptr_array_index (signals, i); if (glade_signal_equal (tmp_signal_handler, signal_handler)) { g_signal_emit (widget, glade_widget_signals[REMOVE_SIGNAL_HANDLER], 0, tmp_signal_handler); g_ptr_array_remove_index (signals, i); if (glade_signal_get_support_warning (tmp_signal_handler)) glade_widget_verify (widget); g_object_unref (tmp_signal_handler); break; } } } /** * glade_widget_change_signal_handler: * @widget: A #GladeWidget * @old_signal_handler: the old #GladeSignal * @new_signal_handler: the new #GladeSignal * * Changes a #GladeSignal on @widget */ void glade_widget_change_signal_handler (GladeWidget *widget, const GladeSignal *old_signal_handler, const GladeSignal *new_signal_handler) { GPtrArray *signals; GladeSignal *signal_handler_iter; guint i; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_SIGNAL (old_signal_handler)); g_return_if_fail (GLADE_IS_SIGNAL (new_signal_handler)); g_return_if_fail (strcmp (glade_signal_get_name (old_signal_handler), glade_signal_get_name (new_signal_handler)) == 0); signals = glade_widget_list_signal_handlers (widget, glade_signal_get_name (old_signal_handler)); /* trying to remove an inexistent signal? */ g_assert (signals); for (i = 0; i < signals->len; i++) { signal_handler_iter = g_ptr_array_index (signals, i); if (glade_signal_equal (signal_handler_iter, old_signal_handler)) { /* Detail */ glade_signal_set_detail (signal_handler_iter, glade_signal_get_detail (new_signal_handler)); /* Handler */ glade_signal_set_handler (signal_handler_iter, glade_signal_get_handler (new_signal_handler)); /* Object */ glade_signal_set_userdata (signal_handler_iter, glade_signal_get_userdata (new_signal_handler)); /* Flags */ glade_signal_set_after (signal_handler_iter, glade_signal_get_after (new_signal_handler)); glade_signal_set_swapped (signal_handler_iter, glade_signal_get_swapped (new_signal_handler)); g_signal_emit (widget, glade_widget_signals[CHANGE_SIGNAL_HANDLER], 0, signal_handler_iter); break; } } } static gboolean glade_widget_button_press_event_impl (GladeWidget *gwidget, GdkEvent *base_event) { GtkWidget *widget; GdkEventButton *event = (GdkEventButton *) base_event; gboolean handled = FALSE; /* make sure to grab focus, since we may stop default handlers */ widget = GTK_WIDGET (glade_widget_get_object (gwidget)); if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget)) gtk_widget_grab_focus (widget); /* if it's already selected don't stop default handlers, e.g. toggle button */ if (event->button == 1) { if (event->state & GDK_CONTROL_MASK) { if (glade_project_is_selected (gwidget->priv->project, gwidget->priv->object)) glade_project_selection_remove (gwidget->priv->project, gwidget->priv->object, TRUE); else glade_project_selection_add (gwidget->priv->project, gwidget->priv->object, TRUE); handled = TRUE; } else if (glade_project_is_selected (gwidget->priv->project, gwidget->priv->object) == FALSE) { glade_project_selection_set (gwidget->priv->project, gwidget->priv->object, TRUE); /* Add selection without interrupting event flow * when shift is down, this allows better behaviour * for GladeFixed children */ handled = !(event->state & GDK_SHIFT_MASK); } } /* Give some kind of access in case of missing right button */ if (!handled && glade_popup_is_popup_event (event)) { glade_popup_widget_pop (gwidget, event, TRUE); handled = TRUE; } return handled; } static gboolean glade_widget_event_impl (GladeWidget *gwidget, GdkEvent *event) { gboolean handled = FALSE; g_return_val_if_fail (GLADE_IS_WIDGET (gwidget), FALSE); switch (event->type) { case GDK_BUTTON_PRESS: g_signal_emit (gwidget, glade_widget_signals[BUTTON_PRESS_EVENT], 0, event, &handled); break; case GDK_BUTTON_RELEASE: g_signal_emit (gwidget, glade_widget_signals[BUTTON_RELEASE_EVENT], 0, event, &handled); break; case GDK_MOTION_NOTIFY: g_signal_emit (gwidget, glade_widget_signals[MOTION_NOTIFY_EVENT], 0, event, &handled); break; default: break; } return handled; } /** * glade_widget_event: * @event: A #GdkEvent * * Feed an event to be handled on the project GladeWidget * hierarchy. * * Returns: whether the event was handled or not. */ gboolean glade_widget_event (GladeWidget *gwidget, GdkEvent *event) { gboolean handled = FALSE; /* Lets just avoid some synthetic events (like focus-change) */ if (((GdkEventAny *) event)->window == NULL) return FALSE; handled = GLADE_WIDGET_GET_CLASS (gwidget)->event (gwidget, event); #ifdef GLADE_ENABLE_DEBUG if (event->type != GDK_EXPOSE) GLADE_NOTE (WIDGET_EVENTS, g_print ("event widget '%s' handled '%d' event '%d'\n", gwidget->priv->name, handled, event->type)); #endif return handled; } /******************************************************************************* GObjectClass & Object Construction *******************************************************************************/ /* * This function creates new GObject parameters based on the GType of the * GladeWidgetAdaptor and its default values. * * If a GladeWidget is specified, it will be used to apply the * values currently in use. */ static GParameter * glade_widget_template_params (GladeWidget *widget, gboolean construct, guint *n_params) { GladeWidgetAdaptor *adaptor; GArray *params; GObjectClass *oclass; GParamSpec **pspec; GladeProperty *glade_property; GladePropertyClass *pclass; guint n_props, i; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); g_return_val_if_fail (n_params != NULL, NULL); adaptor = widget->priv->adaptor; /* As a slight optimization, we never unref the class */ oclass = g_type_class_ref (glade_widget_adaptor_get_object_type (adaptor)); pspec = g_object_class_list_properties (oclass, &n_props); params = g_array_new (FALSE, FALSE, sizeof (GParameter)); for (i = 0; i < n_props; i++) { GParameter parameter = { 0, }; if ((glade_property = glade_widget_get_property (widget, pspec[i]->name)) == NULL) continue; pclass = glade_property_get_class (glade_property); /* Ignore properties based on some criteria */ if (!glade_property_get_enabled (glade_property) || pclass == NULL || /* Unaccounted for in the builder */ glade_property_class_get_virtual (pclass) || /* should not be set before GladeWidget wrapper exists */ glade_property_class_get_ignore (pclass)) /* Catalog explicitly ignores the object */ continue; if (construct && (pspec[i]->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0) continue; else if (!construct && (pspec[i]->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) != 0) continue; if (g_value_type_compatible (G_VALUE_TYPE (glade_property_class_get_default (pclass)), pspec[i]->value_type) == FALSE) { g_critical ("Type mismatch on %s property of %s", parameter.name, glade_widget_adaptor_get_name (adaptor)); continue; } /* We only check equality on properties introduced by the same class because * others properties could change its default in a derivated class * so its is better to transfer every property and reset them. */ if (pspec[i]->owner_type == glade_widget_adaptor_get_object_type (adaptor) && g_param_values_cmp (pspec[i], glade_property_inline_value (glade_property), glade_property_class_get_original_default (pclass)) == 0) continue; /* Not sure if it's safe to use glade_property_get_value() instead as the * value type might differ than the real pspec */ parameter.name = pspec[i]->name; /* These are not copied/freed */ g_value_init (¶meter.value, pspec[i]->value_type); g_value_copy (glade_property_inline_value (glade_property), ¶meter.value); g_array_append_val (params, parameter); } g_free (pspec); *n_params = params->len; return (GParameter *) g_array_free (params, FALSE); } static void free_params (GParameter *params, guint n_params) { gint i; for (i = 0; i < n_params; i++) g_value_unset (&(params[i].value)); g_free (params); } static GObject * glade_widget_build_object (GladeWidget *widget, GladeWidget *template, GladeCreateReason reason) { GParameter *params; GObject *object; guint n_params, i; if (reason == GLADE_CREATE_LOAD) { object = glade_widget_adaptor_construct_object (widget->priv->adaptor, 0, NULL); glade_widget_set_object (widget, object); return object; } if (template) params = glade_widget_template_params (widget, TRUE, &n_params); else params = glade_widget_adaptor_default_params (widget->priv->adaptor, TRUE, &n_params); /* Create the new object with the correct parameters. */ object = glade_widget_adaptor_construct_object (widget->priv->adaptor, n_params, params); free_params (params, n_params); glade_widget_set_object (widget, object); if (template) params = glade_widget_template_params (widget, FALSE, &n_params); else params = glade_widget_adaptor_default_params (widget->priv->adaptor, FALSE, &n_params); for (i = 0; i < n_params; i++) glade_widget_adaptor_set_property (widget->priv->adaptor, object, params[i].name, &(params[i].value)); free_params (params, n_params); return object; } /** * glade_widget_dup_properties: * @dest_widget: the widget we are copying properties for * @template_props: the #GladeProperty list to copy * @as_load: whether to behave as if loading the project * @copy_parentless: whether to copy reffed widgets at all * @exact: whether to copy reffed widgets exactly * * Copies a list of properties, if @as_load is specified, then * properties that are not saved to the glade file are ignored. * * Returns: A newly allocated #GList of new #GladeProperty objects. */ GList * glade_widget_dup_properties (GladeWidget *dest_widget, GList *template_props, gboolean as_load, gboolean copy_parentless, gboolean exact) { GList *list, *properties = NULL; for (list = template_props; list && list->data; list = list->next) { GladeProperty *prop = list->data; GladePropertyClass *pclass = glade_property_get_class (prop); if (glade_property_class_save (pclass) == FALSE && as_load) continue; if (glade_property_class_parentless_widget (pclass) && copy_parentless) { GObject *object = NULL; GladeWidget *parentless; glade_property_get (prop, &object); prop = glade_property_dup (prop, NULL); if (object) { parentless = glade_widget_get_from_gobject (object); parentless = glade_widget_dup (parentless, exact); glade_widget_set_project (parentless, dest_widget->priv->project); glade_property_set (prop, parentless->priv->object); } } else prop = glade_property_dup (prop, NULL); properties = g_list_prepend (properties, prop); } return g_list_reverse (properties); } /** * glade_widget_remove_property: * @widget: A #GladeWidget * @id_property: the name of the property * * Removes the #GladeProperty indicated by @id_property * from @widget (this is intended for use in the plugin, to * remove properties from composite children that dont make * sence to allow the user to specify, notably - properties * that are proxied through the composite widget's properties or * style properties). */ void glade_widget_remove_property (GladeWidget *widget, const gchar *id_property) { GladeProperty *prop; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (id_property); /* XXX FIXME: currently we arent calling this on packing properties, * but doing so could cause crashes because the hash table is not * managed properly */ if ((prop = glade_widget_get_property (widget, id_property)) != NULL) { widget->priv->properties = g_list_remove (widget->priv->properties, prop); g_hash_table_remove (widget->priv->props_hash, id_property); g_object_unref (prop); } else g_critical ("Couldnt find property %s on widget %s\n", id_property, widget->priv->name); } static void glade_widget_set_catalog_defaults (GList *list) { GList *l; for (l = list; l && l->data; l = l->next) { GladeProperty *prop = l->data; GladePropertyClass *klass = glade_property_get_class (prop); GParamSpec *pspec = glade_property_class_get_pspec (klass); if (glade_property_equals_value (prop, glade_property_class_get_original_default (klass)) && g_param_values_cmp (pspec, glade_property_class_get_original_default (klass), glade_property_class_get_default (klass))) glade_property_reset (prop); } } static void glade_widget_sync_custom_props (GladeWidget *widget) { GList *l; for (l = widget->priv->properties; l && l->data; l = l->next) { GladeProperty *prop = GLADE_PROPERTY (l->data); GladePropertyClass *pclass = glade_property_get_class (prop); if (glade_property_class_get_virtual (pclass) || glade_property_class_needs_sync (pclass)) glade_property_sync (prop); } } static void glade_widget_sync_packing_props (GladeWidget *widget) { GList *l; for (l = widget->priv->packing_properties; l && l->data; l = l->next) { GladeProperty *prop = GLADE_PROPERTY (l->data); glade_property_sync (prop); } } static GObject * glade_widget_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GladeWidget *gwidget; GObject *ret_obj; GList *properties = NULL, *list; ret_obj = G_OBJECT_CLASS (glade_widget_parent_class)->constructor (type, n_construct_properties, construct_properties); gwidget = GLADE_WIDGET (ret_obj); if (gwidget->priv->name == NULL) { if (gwidget->priv->project) gwidget->priv->name = glade_project_new_widget_name (gwidget->priv->project, gwidget, GLADE_UNNAMED_PREFIX); else gwidget->priv->name = g_strdup (GLADE_UNNAMED_PREFIX); } if (gwidget->priv->construct_template) { properties = glade_widget_dup_properties (gwidget, gwidget->priv->construct_template->priv->properties, FALSE, TRUE, gwidget->priv->construct_exact); glade_widget_set_properties (gwidget, properties); } if (gwidget->priv->object == NULL) glade_widget_build_object (gwidget, gwidget->priv->construct_template, gwidget->priv->construct_reason); /* Copy sync parentless widget props here after a dup */ if (gwidget->priv->construct_reason == GLADE_CREATE_COPY) { for (list = gwidget->priv->properties; list; list = list->next) { GladeProperty *property = list->data; GladePropertyClass *pclass = glade_property_get_class (property); if (glade_property_class_parentless_widget (pclass)) glade_property_sync (property); } } /* Setup width/height */ gwidget->priv->width = GWA_DEFAULT_WIDTH (gwidget->priv->adaptor); gwidget->priv->height = GWA_DEFAULT_HEIGHT (gwidget->priv->adaptor); /* Introspect object properties before passing it to post_create, * but only when its freshly created (depend on glade file at * load time and copying properties at dup time). */ if (gwidget->priv->construct_reason == GLADE_CREATE_USER) for (list = gwidget->priv->properties; list; list = list->next) glade_property_load (GLADE_PROPERTY (list->data)); /* We only use catalog defaults when the widget was created by the user! * and or is not an internal widget. */ if (gwidget->priv->construct_reason == GLADE_CREATE_USER && gwidget->priv->internal == NULL) glade_widget_set_catalog_defaults (gwidget->priv->properties); /* Only call this once the GladeWidget is completely built * (but before calling custom handlers...) */ glade_widget_adaptor_post_create (gwidget->priv->adaptor, gwidget->priv->object, gwidget->priv->construct_reason); /* Virtual properties need to be explicitly synchronized. */ if (gwidget->priv->construct_reason == GLADE_CREATE_USER) glade_widget_sync_custom_props (gwidget); if (gwidget->priv->parent && gwidget->priv->packing_properties == NULL) glade_widget_set_packing_properties (gwidget, gwidget->priv->parent); if (GTK_IS_WIDGET (gwidget->priv->object) && !gtk_widget_is_toplevel (GTK_WIDGET (gwidget->priv->object))) { gwidget->priv->visible = TRUE; gtk_widget_show_all (GTK_WIDGET (gwidget->priv->object)); } else if (GTK_IS_WIDGET (gwidget->priv->object) == FALSE) gwidget->priv->visible = TRUE; /* Verify support warnings to start off */ glade_widget_verify (gwidget); return ret_obj; } static void glade_widget_finalize (GObject *object) { GladeWidget *widget = GLADE_WIDGET (object); g_return_if_fail (GLADE_IS_WIDGET (object)); GLADE_NOTE (REF_COUNTS, g_print ("Finalizing widget %s\n", widget->priv->name)); g_free (widget->priv->name); g_free (widget->priv->internal); g_free (widget->priv->construct_internal); g_free (widget->priv->support_warning); g_hash_table_destroy (widget->priv->signals); if (widget->priv->props_hash) g_hash_table_destroy (widget->priv->props_hash); if (widget->priv->pack_props_hash) g_hash_table_destroy (widget->priv->pack_props_hash); G_OBJECT_CLASS (glade_widget_parent_class)->finalize (object); } static void reset_object_property (GladeProperty *property, GladeProject *project) { GladePropertyClass *pclass = glade_property_get_class (property); if (glade_property_class_is_object (pclass)) glade_property_reset (property); } static void glade_widget_dispose (GObject *object) { GladeWidget *widget = GLADE_WIDGET (object); GList *children, *l; glade_widget_push_superuser (); /* Remove all children at dispose */ children = glade_widget_get_children (widget); for (l = children; l; l = l->next) { GladeWidget *child = glade_widget_get_from_gobject (l->data); if (glade_widget_get_internal (child) == NULL) glade_widget_remove_child (widget, child); } g_list_free (children); /* Release references by way of object properties... */ while (widget->priv->prop_refs) { GladeProperty *property = GLADE_PROPERTY (widget->priv->prop_refs->data); glade_property_set (property, NULL); } if (widget->priv->properties) g_list_foreach (widget->priv->properties, (GFunc) reset_object_property, widget->priv->project); /* We have to make sure properties release thier references on other widgets first * hence the reset (for object properties) */ if (widget->priv->properties) { g_list_foreach (widget->priv->properties, (GFunc) g_object_unref, NULL); g_list_free (widget->priv->properties); widget->priv->properties = NULL; } if (widget->priv->props_hash) { g_hash_table_destroy (widget->priv->props_hash); widget->priv->props_hash = NULL; } glade_widget_set_object (widget, NULL); if (widget->priv->packing_properties) { g_list_foreach (widget->priv->packing_properties, (GFunc) g_object_unref, NULL); g_list_free (widget->priv->packing_properties); widget->priv->packing_properties = NULL; } if (widget->priv->actions) { g_list_foreach (widget->priv->actions, (GFunc) g_object_unref, NULL); g_list_free (widget->priv->actions); widget->priv->actions = NULL; } if (widget->priv->packing_actions) { g_list_foreach (widget->priv->packing_actions, (GFunc) g_object_unref, NULL); g_list_free (widget->priv->packing_actions); widget->priv->packing_actions = NULL; } if (widget->priv->signal_model) { g_object_unref (widget->priv->signal_model); widget->priv->signal_model = NULL; } glade_widget_pop_superuser (); G_OBJECT_CLASS (glade_widget_parent_class)->dispose (object); } static void glade_widget_set_real_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GladeWidget *widget; widget = GLADE_WIDGET (object); switch (prop_id) { case PROP_NAME: glade_widget_set_name (widget, g_value_get_string (value)); break; case PROP_INTERNAL: glade_widget_set_internal (widget, g_value_get_string (value)); break; case PROP_ANARCHIST: widget->priv->anarchist = g_value_get_boolean (value); break; case PROP_OBJECT: if (g_value_get_object (value)) glade_widget_set_object (widget, g_value_get_object (value)); break; case PROP_PROJECT: glade_widget_set_project (widget, GLADE_PROJECT (g_value_get_object (value))); break; case PROP_ADAPTOR: glade_widget_set_adaptor (widget, GLADE_WIDGET_ADAPTOR (g_value_get_object (value))); break; case PROP_PROPERTIES: glade_widget_set_properties (widget, (GList *) g_value_get_pointer (value)); break; case PROP_PARENT: glade_widget_set_parent (widget, GLADE_WIDGET (g_value_get_object (value))); break; case PROP_INTERNAL_NAME: if (g_value_get_string (value)) widget->priv->construct_internal = g_value_dup_string (value); break; case PROP_TEMPLATE: widget->priv->construct_template = g_value_get_object (value); break; case PROP_TEMPLATE_EXACT: widget->priv->construct_exact = g_value_get_boolean (value); break; case PROP_REASON: widget->priv->construct_reason = g_value_get_int (value); break; case PROP_TOPLEVEL_WIDTH: widget->priv->width = g_value_get_int (value); break; case PROP_TOPLEVEL_HEIGHT: widget->priv->height = g_value_get_int (value); break; case PROP_COMPOSITE: glade_widget_set_is_composite (widget, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void glade_widget_get_real_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GladeWidget *widget; widget = GLADE_WIDGET (object); switch (prop_id) { case PROP_NAME: g_value_set_string (value, widget->priv->name); break; case PROP_INTERNAL: g_value_set_string (value, widget->priv->internal); break; case PROP_ANARCHIST: g_value_set_boolean (value, widget->priv->anarchist); break; case PROP_ADAPTOR: g_value_set_object (value, widget->priv->adaptor); break; case PROP_PROJECT: g_value_set_object (value, G_OBJECT (widget->priv->project)); break; case PROP_OBJECT: g_value_set_object (value, widget->priv->object); break; case PROP_PROPERTIES: g_value_set_pointer (value, widget->priv->properties); break; case PROP_PARENT: g_value_set_object (value, widget->priv->parent); break; case PROP_TOPLEVEL_WIDTH: g_value_set_int (value, widget->priv->width); break; case PROP_TOPLEVEL_HEIGHT: g_value_set_int (value, widget->priv->height); break; case PROP_SUPPORT_WARNING: g_value_set_string (value, widget->priv->support_warning); break; case PROP_VISIBLE: g_value_set_boolean (value, widget->priv->visible); break; case PROP_REASON: g_value_set_int (value, widget->priv->construct_reason); break; case PROP_COMPOSITE: g_value_set_boolean (value, glade_widget_get_is_composite (widget)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void free_signals (GPtrArray *signals) { g_assert (signals); g_ptr_array_free (signals, TRUE); } static void glade_widget_init (GladeWidget *widget) { widget->priv = glade_widget_get_instance_private (widget); widget->priv->adaptor = NULL; widget->priv->project = NULL; widget->priv->name = NULL; widget->priv->internal = NULL; widget->priv->object = NULL; widget->priv->properties = NULL; widget->priv->packing_properties = NULL; widget->priv->prop_refs = NULL; widget->priv->signals = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) free_signals); /* Initial invalid values */ widget->priv->width = -1; widget->priv->height = -1; } static gboolean glade_widget_drag_can_drag (_GladeDrag *source) { g_return_val_if_fail (GLADE_IS_DRAG (source), FALSE); return GLADE_WIDGET (source)->priv->internal == NULL; } static gboolean glade_widget_drag_can_drop (_GladeDrag *dest, gint x, gint y, GObject *data) { GObject *object; g_return_val_if_fail (GLADE_IS_DRAG (dest), FALSE); object = GLADE_WIDGET (dest)->priv->object; if (!(GTK_IS_FIXED (object) || GTK_IS_LAYOUT (object) || GTK_IS_OVERLAY (object))) return FALSE; if (GLADE_IS_WIDGET_ADAPTOR (data)) { GType otype = glade_widget_adaptor_get_object_type (GLADE_WIDGET_ADAPTOR (data)); if (g_type_is_a (otype, GTK_TYPE_WIDGET) && !GWA_IS_TOPLEVEL (data)) return TRUE; } else { GladeWidget *new_child, *parent = GLADE_WIDGET (dest); GObject *object = glade_widget_get_object (parent); if (object == data) return FALSE; if (GTK_IS_WIDGET (data) && GTK_IS_WIDGET (object) && gtk_widget_is_ancestor (GTK_WIDGET (data), GTK_WIDGET (object))) return FALSE; if ((new_child = glade_widget_get_from_gobject (data)) && (!glade_widget_add_verify (parent, new_child, FALSE) || glade_widget_placeholder_relation (parent, new_child))) return FALSE; return TRUE; } return FALSE; } static gboolean glade_widget_drag_drop (_GladeDrag *dest, gint x, gint y, GObject *data) { GladeWidget *gsource; g_return_val_if_fail (GLADE_IS_DRAG (dest), FALSE); if (!data) return FALSE; if (GLADE_IS_WIDGET_ADAPTOR (data)) { GladeWidget *parent = GLADE_WIDGET (dest); glade_command_create (GLADE_WIDGET_ADAPTOR (data), parent, NULL, glade_widget_get_project (parent)); return TRUE; } else if ((gsource = glade_widget_get_from_gobject (data))) { GladeWidget *parent = GLADE_WIDGET (dest); GList widgets = {gsource, NULL, NULL}; /* Check for recursive paste */ if (parent != gsource) { glade_command_dnd (&widgets, parent, NULL); return TRUE; } } return FALSE; } static void glade_widget_drag_init (_GladeDragInterface *iface) { iface->can_drag = glade_widget_drag_can_drag; iface->can_drop = glade_widget_drag_can_drop; iface->drop = glade_widget_drag_drop; } static void glade_widget_class_init (GladeWidgetClass *klass) { GObjectClass *object_class; if (glade_widget_name_quark == 0) glade_widget_name_quark = g_quark_from_static_string ("GladeWidgetDataTag"); object_class = G_OBJECT_CLASS (klass); object_class->constructor = glade_widget_constructor; object_class->finalize = glade_widget_finalize; object_class->dispose = glade_widget_dispose; object_class->set_property = glade_widget_set_real_property; object_class->get_property = glade_widget_get_real_property; klass->add_child = glade_widget_add_child_impl; klass->remove_child = glade_widget_remove_child_impl; klass->replace_child = glade_widget_replace_child_impl; klass->event = glade_widget_event_impl; klass->button_press_event = glade_widget_button_press_event_impl; klass->button_release_event = NULL; klass->motion_notify_event = NULL; properties[PROP_NAME] = g_param_spec_string ("name", _("Name"), _("The name of the widget"), NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); properties[PROP_INTERNAL] = g_param_spec_string ("internal", _("Internal name"), _("The internal name of the widget"), NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); properties[PROP_ANARCHIST] = g_param_spec_boolean ("anarchist", _("Anarchist"), _("Whether this composite child is " "an ancestral child or an anarchist child"), FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_OBJECT] = g_param_spec_object ("object", _("Object"), _("The object associated"), G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); properties[PROP_ADAPTOR] = g_param_spec_object ("adaptor", _("Adaptor"), _("The class adaptor for the associated widget"), GLADE_TYPE_WIDGET_ADAPTOR, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_PROJECT] = g_param_spec_object ("project", _("Project"), _("The glade project that " "this widget belongs to"), GLADE_TYPE_PROJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); properties[PROP_PROPERTIES] = g_param_spec_pointer ("properties", _("Properties"), _("A list of GladeProperties"), G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_PARENT] = g_param_spec_object ("parent", _("Parent"), _("A pointer to the parenting GladeWidget"), GLADE_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); properties[PROP_INTERNAL_NAME] = g_param_spec_string ("internal-name", _("Internal Name"), _("A generic name prefix for internal widgets"), NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE); properties[PROP_TEMPLATE] = g_param_spec_object ("template", _("Template"), _("A GladeWidget template to base a new widget on"), GLADE_TYPE_WIDGET, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE); properties[PROP_TEMPLATE_EXACT] = g_param_spec_boolean ("template-exact", _("Exact Template"), _ ("Whether we are creating an exact duplicate when using a template"), FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_REASON] = g_param_spec_int ("reason", _("Reason"), _("A GladeCreateReason for this creation"), GLADE_CREATE_USER, GLADE_CREATE_REASONS - 1, GLADE_CREATE_USER, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); properties[PROP_TOPLEVEL_WIDTH] = g_param_spec_int ("toplevel-width", _("Toplevel Width"), _("The width of the widget when toplevel in " "the GladeDesignLayout"), -1, G_MAXINT, -1, G_PARAM_READWRITE); properties[PROP_TOPLEVEL_HEIGHT] = g_param_spec_int ("toplevel-height", _("Toplevel Height"), _("The height of the widget when toplevel in " "the GladeDesignLayout"), -1, G_MAXINT, -1, G_PARAM_READWRITE); properties[PROP_SUPPORT_WARNING] = g_param_spec_string ("support warning", _("Support Warning"), _("A warning string about version mismatches"), NULL, G_PARAM_READABLE); properties[PROP_VISIBLE] = g_param_spec_boolean ("visible", _("Visible"), _("Wether the widget is visible or not"), FALSE, G_PARAM_READABLE); properties[PROP_COMPOSITE] = g_param_spec_boolean ("composite", _("Composite"), _("Whether this widget is the template for a composite widget"), FALSE, G_PARAM_READWRITE); /* Install all properties */ g_object_class_install_properties (object_class, N_PROPERTIES, properties); /** * GladeWidget::add-signal-handler: * @gladewidget: the #GladeWidget which received the signal. * @arg1: the #GladeSignal that was added to @gladewidget. */ glade_widget_signals[ADD_SIGNAL_HANDLER] = g_signal_new ("add-signal-handler", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GladeWidgetClass, add_signal_handler), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GLADE_TYPE_SIGNAL); /** * GladeWidget::remove-signal-handler: * @gladewidget: the #GladeWidget which received the signal. * @arg1: the #GladeSignal that was removed from @gladewidget. */ glade_widget_signals[REMOVE_SIGNAL_HANDLER] = g_signal_new ("remove-signal-handler", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GladeWidgetClass, remove_signal_handler), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GLADE_TYPE_SIGNAL); /** * GladeWidget::change-signal-handler: * @gladewidget: the #GladeWidget which received the signal. * @arg1: the old #GladeSignal * @arg2: the new #GladeSignal */ glade_widget_signals[CHANGE_SIGNAL_HANDLER] = g_signal_new ("change-signal-handler", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GladeWidgetClass, change_signal_handler), NULL, NULL, _glade_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GLADE_TYPE_SIGNAL); /** * GladeWidget::button-press-event: * @gladewidget: the #GladeWidget which received the signal. * @arg1: the #GdkEvent */ glade_widget_signals[BUTTON_PRESS_EVENT] = g_signal_new ("button-press-event", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GladeWidgetClass, button_press_event), _glade_boolean_handled_accumulator, NULL, _glade_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); /** * GladeWidget::button-relese-event: * @gladewidget: the #GladeWidget which received the signal. * @arg1: the #GdkEvent */ glade_widget_signals[BUTTON_RELEASE_EVENT] = g_signal_new ("button-release-event", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GladeWidgetClass, button_release_event), _glade_boolean_handled_accumulator, NULL, _glade_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); /** * GladeWidget::motion-notify-event: * @gladewidget: the #GladeWidget which received the signal. * @arg1: the #GdkEvent */ glade_widget_signals[MOTION_NOTIFY_EVENT] = g_signal_new ("motion-notify-event", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GladeWidgetClass, motion_notify_event), _glade_boolean_handled_accumulator, NULL, _glade_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); /** * GladeWidget::support-changed: * @gladewidget: the #GladeWidget which received the signal. * * Emitted when property and signal support metadatas and messages * have been updated. */ glade_widget_signals[SUPPORT_CHANGED] = g_signal_new ("support-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } /******************************************************************************* Static stuff.... *******************************************************************************/ static void glade_widget_copy_packing_props (GladeWidget *parent, GladeWidget *child, GladeWidget *template_widget) { GladeProperty *dup_prop, *orig_prop; GList *l; g_return_if_fail (child->priv->parent == parent); glade_widget_set_packing_properties (child, parent); for (l = child->priv->packing_properties; l && l->data; l = l->next) { GladePropertyClass *pclass; dup_prop = GLADE_PROPERTY (l->data); pclass = glade_property_get_class (dup_prop); orig_prop = glade_widget_get_pack_property (template_widget, glade_property_class_id (pclass)); glade_property_set_value (dup_prop, glade_property_inline_value (orig_prop)); } } static void glade_widget_set_default_packing_properties (GladeWidget *container, GladeWidget *child) { GladePropertyClass *property_class; const GList *l; for (l = glade_widget_adaptor_get_packing_props (container->priv->adaptor); l; l = l->next) { const gchar *def; GValue *value; property_class = l->data; if ((def = glade_widget_adaptor_get_packing_default (child->priv->adaptor, container->priv->adaptor, glade_property_class_id (property_class))) == NULL) continue; value = glade_property_class_make_gvalue_from_string (property_class, def, child->priv->project); glade_widget_child_set_property (container, child, glade_property_class_id (property_class), value); g_value_unset (value); g_free (value); } } /** * When looking for an internal child we have to walk up the hierarchy... */ static GObject * glade_widget_get_internal_child (GladeWidget *main_target, GladeWidget *parent, const gchar *internal) { while (parent) { if (glade_widget_adaptor_has_internal_children (parent->priv->adaptor)) return glade_widget_adaptor_get_internal_child (parent->priv->adaptor, parent->priv->object, internal); /* Limit the itterations into where the copy routine stared */ if (parent == main_target) break; parent = glade_widget_get_parent (parent); } return NULL; } static GladeWidget * glade_widget_dup_internal (GladeWidget *main_target, GladeWidget *parent, GladeWidget *template_widget, gboolean exact) { GladeWidget *gwidget = NULL; GList *children; GtkWidget *placeholder; gchar *child_type; GList *l; g_return_val_if_fail (GLADE_IS_WIDGET (template_widget), NULL); g_return_val_if_fail (parent == NULL || GLADE_IS_WIDGET (parent), NULL); /* Dont actually duplicate internal widgets, but recurse through them anyway. */ if (parent && template_widget->priv->internal) { GObject *internal_object = NULL; if ((internal_object = glade_widget_get_internal_child (main_target, parent, template_widget->priv->internal)) != NULL) { gwidget = glade_widget_get_from_gobject (internal_object); g_assert (gwidget); } } /* If either it was not internal, or we failed to lookup the internal child * in the copied hierarchy (this can happen when copying an internal vbox from * a composite dialog for instance). */ if (gwidget == NULL) { gchar *name = g_strdup (template_widget->priv->name); gwidget = glade_widget_adaptor_create_widget (template_widget->priv->adaptor, FALSE, "name", name, "parent", parent, "project", template_widget->priv->project, "template", template_widget, "template-exact", exact, "reason", GLADE_CREATE_COPY, NULL); g_free (name); } /* Copy signals over here regardless of internal or not... */ if (exact) glade_widget_copy_signals (gwidget, template_widget); if ((children = glade_widget_adaptor_get_children (template_widget->priv->adaptor, template_widget->priv->object)) != NULL) { GList *list; for (list = children; list && list->data; list = list->next) { GObject *child = G_OBJECT (list->data); GladeWidget *child_gwidget, *child_dup; child_type = g_object_get_data (child, "special-child-type"); if ((child_gwidget = glade_widget_get_from_gobject (child)) == NULL) { /* Bring the placeholders along ... * but not unmarked internal children */ if (GLADE_IS_PLACEHOLDER (child)) { placeholder = glade_placeholder_new (); g_object_set_data_full (G_OBJECT (placeholder), "special-child-type", g_strdup (child_type), g_free); glade_widget_adaptor_add (gwidget->priv->adaptor, gwidget->priv->object, G_OBJECT (placeholder)); } } else { /* Recurse through every GladeWidget (internal or not) */ child_dup = glade_widget_dup_internal (main_target, gwidget, child_gwidget, exact); if (child_dup->priv->internal == NULL) { g_object_set_data_full (child_dup->priv->object, "special-child-type", g_strdup (child_type), g_free); glade_widget_add_child (gwidget, child_dup, FALSE); } /* Internal children that are not heirarchic children * need to avoid copying these packing props (like popup windows * created on behalf of composite widgets). */ if (glade_widget_adaptor_has_child (gwidget->priv->adaptor, gwidget->priv->object, child_dup->priv->object)) glade_widget_copy_packing_props (gwidget, child_dup, child_gwidget); } } g_list_free (children); } if (gwidget->priv->internal) glade_widget_copy_properties (gwidget, template_widget, TRUE, exact); if (gwidget->priv->packing_properties == NULL) gwidget->priv->packing_properties = glade_widget_dup_properties (gwidget, template_widget->priv->packing_properties, FALSE, FALSE, FALSE); /* If custom properties are still at thier * default value, they need to be synced. */ glade_widget_sync_custom_props (gwidget); /* Some properties may not be synced so we reload them */ for (l = gwidget->priv->properties; l; l = l->next) glade_property_load (GLADE_PROPERTY (l->data)); if (GWA_IS_TOPLEVEL (gwidget->priv->adaptor) && GTK_IS_WIDGET (gwidget->priv->object)) g_object_set (gwidget, "toplevel-width", template_widget->priv->width, "toplevel-height", template_widget->priv->height, NULL); return gwidget; } typedef struct { GladeWidget *widget; GtkWidget *placeholder; GList *properties; gchar *internal_name; GList *internal_list; } GladeChildExtract; static GList * glade_widget_extract_children (GladeWidget *gwidget) { GladeChildExtract *extract; GList *extract_list = NULL; GList *children, *list; children = glade_widget_adaptor_get_children (gwidget->priv->adaptor, gwidget->priv->object); for (list = children; list && list->data; list = list->next) { GObject *child = G_OBJECT (list->data); GladeWidget *gchild = glade_widget_get_from_gobject (child); if (gchild && gchild->priv->internal) { /* Recurse and collect any deep child hierarchies * inside composite widgets. */ extract = g_new0 (GladeChildExtract, 1); extract->internal_name = g_strdup (gchild->priv->internal); extract->internal_list = glade_widget_extract_children (gchild); extract->properties = glade_widget_dup_properties (gchild, gchild->priv->properties, TRUE, FALSE, FALSE); extract_list = g_list_prepend (extract_list, extract); } else if (gchild || GLADE_IS_PLACEHOLDER (child)) { extract = g_new0 (GladeChildExtract, 1); if (gchild) { extract->widget = g_object_ref (gchild); /* Make copies of the packing properties */ extract->properties = glade_widget_dup_properties (gchild, gchild->priv->packing_properties, TRUE, FALSE, FALSE); glade_widget_remove_child (gwidget, gchild); } else { /* need to handle placeholders by hand here */ extract->placeholder = g_object_ref (child); glade_widget_adaptor_remove (gwidget->priv->adaptor, gwidget->priv->object, child); } extract_list = g_list_prepend (extract_list, extract); } } if (children) g_list_free (children); return g_list_reverse (extract_list); } static void glade_widget_insert_children (GladeWidget *gwidget, GList *children) { GladeChildExtract *extract; GladeWidget *gchild; GObject *internal_object; GList *list, *l; for (list = children; list; list = list->next) { extract = list->data; if (extract->internal_name) { /* Recurse and add deep widget hierarchies to internal * widgets. */ internal_object = glade_widget_get_internal_child (NULL, gwidget, extract->internal_name); /* Some internal children can disappear after a construct only * property has changed, eg. the "has-entry" property of * GtkComboBox decides whether there is an internal entry. * * Just ignore the saved information we have about missing internal * children. */ if (!internal_object) { if (extract->properties) g_list_free_full (extract->properties, (GDestroyNotify)g_object_unref); g_free (extract->internal_name); g_free (extract); continue; } gchild = glade_widget_get_from_gobject (internal_object); /* This will free the list... */ glade_widget_insert_children (gchild, extract->internal_list); /* Set the properties after inserting the children */ for (l = extract->properties; l; l = l->next) { GValue value = { 0, }; GladeProperty *saved_prop = l->data; GladePropertyClass *pclass = glade_property_get_class (saved_prop); GladeProperty *widget_prop = glade_widget_get_property (gchild, glade_property_class_id (pclass)); glade_property_get_value (saved_prop, &value); glade_property_set_value (widget_prop, &value); g_value_unset (&value); /* Free them as we go ... */ g_object_unref (saved_prop); } if (extract->properties) g_list_free (extract->properties); g_free (extract->internal_name); } else if (extract->widget) { glade_widget_add_child (gwidget, extract->widget, FALSE); g_object_unref (extract->widget); for (l = extract->properties; l; l = l->next) { GValue value = { 0, }; GladeProperty *saved_prop = l->data; GladePropertyClass *pclass = glade_property_get_class (saved_prop); GladeProperty *widget_prop = glade_widget_get_pack_property (extract->widget, glade_property_class_id (pclass)); glade_property_get_value (saved_prop, &value); glade_property_set_value (widget_prop, &value); g_value_unset (&value); /* Free them as we go ... */ g_object_unref (saved_prop); } if (extract->properties) g_list_free (extract->properties); } else { glade_widget_adaptor_add (gwidget->priv->adaptor, gwidget->priv->object, G_OBJECT (extract->placeholder)); g_object_unref (extract->placeholder); } g_free (extract); } if (children) g_list_free (children); } static void glade_widget_set_properties (GladeWidget *widget, GList *properties) { GladeProperty *property; GList *list; if (properties) { if (widget->priv->properties) { g_list_foreach (widget->priv->properties, (GFunc) g_object_unref, NULL); g_list_free (widget->priv->properties); } if (widget->priv->props_hash) g_hash_table_destroy (widget->priv->props_hash); widget->priv->properties = properties; widget->priv->props_hash = g_hash_table_new (g_str_hash, g_str_equal); for (list = properties; list; list = list->next) { GladePropertyClass *pclass; property = list->data; pclass = glade_property_get_class (property); glade_property_set_widget (property, widget); g_hash_table_insert (widget->priv->props_hash, (gchar *)glade_property_class_id (pclass), property); } } } static void glade_widget_set_adaptor (GladeWidget *widget, GladeWidgetAdaptor *adaptor) { GladePropertyClass *property_class; GladeProperty *property; const GList *list; GList *properties = NULL; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor)); /* calling set_class out of the constructor? */ g_return_if_fail (widget->priv->adaptor == NULL); widget->priv->adaptor = adaptor; /* If we have no properties; we are not in the process of loading */ if (!widget->priv->properties) { for (list = glade_widget_adaptor_get_properties (adaptor); list; list = list->next) { property_class = GLADE_PROPERTY_CLASS (list->data); if ((property = glade_property_new (property_class, widget, NULL)) == NULL) { g_warning ("Failed to create [%s] property", glade_property_class_id (property_class)); continue; } properties = g_list_prepend (properties, property); } glade_widget_set_properties (widget, g_list_reverse (properties)); } /* Create actions from adaptor */ widget->priv->actions = glade_widget_adaptor_actions_new (adaptor); } /* * Returns a list of GladeProperties from a list for the correct * child type for this widget of this container. */ static GList * glade_widget_create_packing_properties (GladeWidget *container, GladeWidget *widget) { GladePropertyClass *property_class; GladeProperty *property; const GList *list; GList *packing_props = NULL; /* XXX TODO: by checking with some GladePropertyClass metadata, decide * which packing properties go on which type of children. */ for (list = glade_widget_adaptor_get_packing_props (container->priv->adaptor); list && list->data; list = list->next) { property_class = list->data; property = glade_property_new (property_class, widget, NULL); packing_props = g_list_prepend (packing_props, property); } return g_list_reverse (packing_props); } /* Private API */ GList * _glade_widget_peek_prop_refs (GladeWidget *widget) { return widget->priv->prop_refs; } /******************************************************************************* API *******************************************************************************/ GladeWidget * glade_widget_get_from_gobject (gpointer object) { g_return_val_if_fail (G_IS_OBJECT (object), NULL); return g_object_get_qdata (G_OBJECT (object), glade_widget_name_quark); } /** * glade_widget_show: * @widget: A #GladeWidget * * Display @widget in it's project's GladeDesignView */ void glade_widget_show (GladeWidget *widget) { GladeProperty *property; GladeProject *project; g_return_if_fail (GLADE_IS_WIDGET (widget)); /* Position window at saved coordinates or in the center */ if (GTK_IS_WIDGET (widget->priv->object) && !widget->priv->parent) { /* Maybe a property references this widget internally, show that widget instead */ if ((property = glade_widget_get_parentless_widget_ref (widget)) != NULL) { /* will never happen, paranoid check to avoid endless recursion. */ if (glade_property_get_widget (property) != widget) glade_widget_show (glade_property_get_widget (property)); return; } } else if (GTK_IS_WIDGET (widget->priv->object)) { GladeWidget *toplevel = glade_widget_get_toplevel (widget); if (toplevel != widget) glade_widget_show (toplevel); } if (widget->priv->visible) return; widget->priv->visible = TRUE; if ((project = glade_widget_get_project (widget))) glade_project_widget_visibility_changed (project, widget, TRUE); } /** * glade_widget_hide: * @widget: A #GladeWidget * * Hide @widget */ void glade_widget_hide (GladeWidget *widget) { GladeProject *project; g_return_if_fail (GLADE_IS_WIDGET (widget)); if (!widget->priv->visible) return; widget->priv->visible = FALSE; if ((project = glade_widget_get_project (widget))) glade_project_widget_visibility_changed (project, widget, FALSE); } /** * glade_widget_add_prop_ref: * @widget: A #GladeWidget * @property: the #GladeProperty * * Adds @property to @widget 's list of referenced properties. * * Note: this is used to track properties on other objects that * reffer to this object. */ void glade_widget_add_prop_ref (GladeWidget *widget, GladeProperty *property) { GladePropertyClass *pclass; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_PROPERTY (property)); if (!g_list_find (widget->priv->prop_refs, property)) widget->priv->prop_refs = g_list_prepend (widget->priv->prop_refs, property); /* parentless widget reffed widgets are added to thier reffering widgets. * they cant be in the design view. */ pclass = glade_property_get_class (property); if (glade_property_class_parentless_widget (pclass)) { GladeProject *project = glade_widget_get_project (widget); if (project) glade_project_widget_changed (project, widget); glade_widget_hide (widget); } } /** * glade_widget_remove_prop_ref: * @widget: A #GladeWidget * @property: the #GladeProperty * * Removes @property from @widget 's list of referenced properties. * * Note: this is used to track properties on other objects that * reffer to this object. */ void glade_widget_remove_prop_ref (GladeWidget *widget, GladeProperty *property) { GladePropertyClass *pclass; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_PROPERTY (property)); widget->priv->prop_refs = g_list_remove (widget->priv->prop_refs, property); pclass = glade_property_get_class (property); if (glade_property_class_parentless_widget (pclass)) { GladeProject *project = glade_widget_get_project (widget); if (project) glade_project_widget_changed (project, widget); } } GList * glade_widget_list_prop_refs (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return g_list_copy (widget->priv->prop_refs); } gboolean glade_widget_has_prop_refs (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); return widget->priv->prop_refs != NULL; } GladeProperty * glade_widget_get_parentless_widget_ref (GladeWidget *widget) { GladePropertyClass *pclass; GladeProperty *property; GList *l; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); for (l = widget->priv->prop_refs; l && l->data; l = l->next) { property = l->data; pclass = glade_property_get_class (property); if (glade_property_class_parentless_widget (pclass)) /* only one external property can point to this widget */ return property; } return NULL; } GList * glade_widget_get_parentless_reffed_widgets (GladeWidget *widget) { GladeProperty *property = NULL; GladePropertyClass *pclass; GObject *reffed = NULL; GList *l, *widgets = NULL; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); for (l = widget->priv->properties; l && l->data; l = l->next) { property = l->data; pclass = glade_property_get_class (property); reffed = NULL; if (glade_property_class_parentless_widget (pclass)) { glade_property_get (property, &reffed); if (reffed) widgets = g_list_prepend (widgets, glade_widget_get_from_gobject (reffed)); } } return g_list_reverse (widgets); } static void glade_widget_accum_signal_foreach (const gchar *key, GPtrArray *signals, GList **list) { GladeSignal *signal; gint i; for (i = 0; i < signals->len; i++) { signal = (GladeSignal *) signals->pdata[i]; *list = g_list_append (*list, signal); } } /** * glade_widget_get_signal_list: * @widget: a 'dest' #GladeWidget * * Compiles a list of #GladeSignal elements * * Returns: a newly allocated #GList of #GladeSignals, the caller * must call g_list_free() to free the list. */ GList * glade_widget_get_signal_list (GladeWidget *widget) { GList *signals = NULL; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); g_hash_table_foreach (widget->priv->signals, (GHFunc) glade_widget_accum_signal_foreach, &signals); return signals; } static void glade_widget_copy_signal_foreach (const gchar *key, GPtrArray *signals, GladeWidget *dest) { GladeSignal *signal; gint i; for (i = 0; i < signals->len; i++) { signal = (GladeSignal *) signals->pdata[i]; glade_widget_add_signal_handler (dest, signal); } } /** * glade_widget_copy_signals: * @widget: a 'dest' #GladeWidget * @template_widget: a 'src' #GladeWidget * * Sets signals in @widget based on the values of * matching signals in @template_widget */ void glade_widget_copy_signals (GladeWidget *widget, GladeWidget *template_widget) { g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_WIDGET (template_widget)); g_hash_table_foreach (template_widget->priv->signals, (GHFunc) glade_widget_copy_signal_foreach, widget); } /** * glade_widget_copy_properties: * @widget: a 'dest' #GladeWidget * @template_widget: a 'src' #GladeWidget * @copy_parentless: whether to copy reffed widgets at all * @exact: whether to copy reffed widgets exactly * * Sets properties in @widget based on the values of * matching properties in @template_widget */ void glade_widget_copy_properties (GladeWidget *widget, GladeWidget *template_widget, gboolean copy_parentless, gboolean exact) { GList *l; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_WIDGET (template_widget)); for (l = widget->priv->properties; l && l->data; l = l->next) { GladeProperty *widget_prop = GLADE_PROPERTY (l->data); GladeProperty *template_prop; GladePropertyClass *widget_pclass, *template_pclass = NULL; widget_pclass = glade_property_get_class (widget_prop); template_prop = glade_widget_get_property (template_widget, glade_property_class_id (widget_pclass)); if (template_prop) template_pclass = glade_property_get_class (template_prop); /* Check if they share the same class definition, different * properties may have the same name (support for * copying properties across "not-quite" compatible widget * classes, like GtkImageMenuItem --> GtkCheckMenuItem). */ if (template_pclass != NULL && glade_property_class_match (template_pclass, widget_pclass)) { if (glade_property_class_parentless_widget (template_pclass) && copy_parentless) { GObject *object = NULL; GladeWidget *parentless; glade_property_get (template_prop, &object); if (object) { parentless = glade_widget_get_from_gobject (object); parentless = glade_widget_dup (parentless, exact); glade_widget_set_project (parentless, widget->priv->project); glade_property_set (widget_prop, parentless->priv->object); } else glade_property_set (widget_prop, NULL); } else glade_property_set_value (widget_prop, glade_property_inline_value (template_prop)); } } } /** * glade_widget_add_verify: * @widget: A #GladeWidget * @child: The child #GladeWidget to add * @user_feedback: whether a notification dialog should be * presented in the case that the child cannot not be added. * * Checks whether @child can be added to @parent. * * If @user_feedback is %TRUE and @child cannot be * added then this shows a notification dialog to the user * explaining why. * * Returns: whether @child can be added to @widget. */ gboolean glade_widget_add_verify (GladeWidget *widget, GladeWidget *child, gboolean user_feedback) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (GLADE_IS_WIDGET (child), FALSE); return glade_widget_adaptor_add_verify (widget->priv->adaptor, widget->priv->object, child->priv->object, user_feedback); } /** * glade_widget_add_child: * @parent: A #GladeWidget * @child: the #GladeWidget to add * @at_mouse: whether the added widget should be added * at the current mouse position * * Adds @child to @parent in a generic way for this #GladeWidget parent. */ void glade_widget_add_child (GladeWidget *parent, GladeWidget *child, gboolean at_mouse) { g_return_if_fail (GLADE_IS_WIDGET (parent)); g_return_if_fail (GLADE_IS_WIDGET (child)); GLADE_WIDGET_GET_CLASS (parent)->add_child (parent, child, at_mouse); } /** * glade_widget_remove_child: * @parent: A #GladeWidget * @child: the #GladeWidget to add * * Removes @child from @parent in a generic way for this #GladeWidget parent. */ void glade_widget_remove_child (GladeWidget *parent, GladeWidget *child) { g_return_if_fail (GLADE_IS_WIDGET (parent)); g_return_if_fail (GLADE_IS_WIDGET (child)); GLADE_WIDGET_GET_CLASS (parent)->remove_child (parent, child); } /** * glade_widget_dup: * @template_widget: a #GladeWidget * @exact: whether or not to creat an exact duplicate * * Creates a deep copy of #GladeWidget. if @exact is specified, * the widget name is preserved and signals are carried over * (this is used to maintain names & signals in Cut/Paste context * as opposed to Copy/Paste contexts). * * Returns: The newly created #GladeWidget */ GladeWidget * glade_widget_dup (GladeWidget *template_widget, gboolean exact) { GladeWidget *widget; g_return_val_if_fail (GLADE_IS_WIDGET (template_widget), NULL); glade_widget_push_superuser (); widget = glade_widget_dup_internal (template_widget, NULL, template_widget, exact); glade_widget_pop_superuser (); return widget; } typedef struct { GladeProperty *property; GValue value; } PropertyData; /** * glade_widget_rebuild: * @gwidget: a #GladeWidget * * Replaces the current widget instance with * a new one while preserving all properties children and * takes care of reparenting. * */ void glade_widget_rebuild (GladeWidget *gwidget) { GObject *new_object, *old_object; GladeWidgetAdaptor *adaptor; GladeProject *project = NULL; GladeWidget *parent = NULL; GList *children; GList *selection = NULL; GList *restore_properties = NULL; GList *save_properties, *l; g_return_if_fail (GLADE_IS_WIDGET (gwidget)); /* Mark the widget as currently rebuilding, * * We avoid rewriting and losing packing properties when * reparenting in the process of rebuilding a widget instance */ gwidget->priv->rebuilding = TRUE; glade_widget_push_superuser (); adaptor = gwidget->priv->adaptor; if (gwidget->priv->parent && glade_widget_adaptor_has_child (gwidget->priv->parent->priv->adaptor, gwidget->priv->parent->priv->object, gwidget->priv->object)) parent = gwidget->priv->parent; g_object_ref (gwidget); /* Extract and keep the child hierarchies aside... */ children = glade_widget_extract_children (gwidget); /* Here we take care removing the widget from the project and * the selection before rebuilding the instance. */ if (gwidget->priv->project && glade_project_has_object (gwidget->priv->project, gwidget->priv->object)) { project = gwidget->priv->project; if (glade_project_is_selected (project, gwidget->priv->object)) selection = g_list_copy (glade_project_selection_get (project)); glade_project_remove_object (project, gwidget->priv->object); } /* parentless_widget and object properties that reffer to this widget * should be unset before transfering */ l = g_list_copy (gwidget->priv->properties); save_properties = g_list_copy (gwidget->priv->prop_refs); save_properties = g_list_concat (l, save_properties); for (l = save_properties; l; l = l->next) { GladeProperty *property = l->data; GladePropertyClass *pclass = glade_property_get_class (property); if (glade_property_get_widget (property) != gwidget || glade_property_class_parentless_widget (pclass)) { PropertyData *prop_data; if (!G_IS_PARAM_SPEC_OBJECT (glade_property_class_get_pspec (pclass))) g_warning ("Parentless widget property should be of object type"); prop_data = g_new0 (PropertyData, 1); prop_data->property = property; if (glade_property_get_widget (property) == gwidget) glade_property_get_value (property, &prop_data->value); restore_properties = g_list_prepend (restore_properties, prop_data); glade_property_set (property, NULL); } } g_list_free (save_properties); /* Remove old object from parent */ if (parent) glade_widget_remove_child (parent, gwidget); /* Hold a reference to the old widget while we transport properties * and children from it */ old_object = g_object_ref (glade_widget_get_object (gwidget)); new_object = glade_widget_build_object (gwidget, gwidget, GLADE_CREATE_REBUILD); /* Only call this once the object has a proper GladeWidget */ glade_widget_adaptor_post_create (adaptor, new_object, GLADE_CREATE_REBUILD); /* Reparent any children of the old object to the new object * (this function will consume and free the child list). */ glade_widget_insert_children (gwidget, children); /* Add new object to parent */ if (parent) glade_widget_add_child (parent, gwidget, FALSE); /* Custom properties aren't transfered in build_object, since build_object * is only concerned with object creation. */ glade_widget_sync_custom_props (gwidget); /* Setting parentless_widget and prop_ref properties back */ for (l = restore_properties; l; l = l->next) { PropertyData *prop_data = l->data; GladeProperty *property = prop_data->property; if (glade_property_get_widget (property) == gwidget) { glade_property_set_value (property, &prop_data->value); g_value_unset (&prop_data->value); } else { /* restore property references on rebuilt objects */ glade_property_set (property, gwidget->priv->object); } g_free (prop_data); } g_list_free (restore_properties); /* Sync packing. */ if (parent) glade_widget_sync_packing_props (gwidget); /* If the widget was in a project (and maybe the selection), then * restore that stuff. */ if (project) { glade_project_add_object (project, gwidget->priv->object); if (selection) { glade_project_selection_clear (project, FALSE); for (l = selection; l; l = g_list_next (l)) { GObject *selected = l->data; if (selected == old_object) glade_project_selection_add (project, gwidget->priv->object, TRUE); else glade_project_selection_add (project, selected, TRUE); } g_list_free (selection); } } /* Must call dispose for cases like dialogs and toplevels */ if (GTK_IS_WINDOW (old_object)) gtk_widget_destroy (GTK_WIDGET (old_object)); else g_object_unref (old_object); /* Ensure rebuilt widget visibility */ if (GTK_IS_WIDGET (gwidget->priv->object) && !GTK_IS_WINDOW (gwidget->priv->object)) gtk_widget_show_all (GTK_WIDGET (gwidget->priv->object)); /* We shouldnt show if its not already visible */ if (gwidget->priv->visible) glade_widget_show (gwidget); g_object_unref (gwidget); gwidget->priv->rebuilding = FALSE; glade_widget_pop_superuser (); } /** * glade_widget_list_signal_handlers: * @widget: a #GladeWidget * @signal_name: the name of the signal * * Returns: A #GPtrArray of #GladeSignal for @signal_name */ GPtrArray * glade_widget_list_signal_handlers (GladeWidget *widget, const gchar *signal_name) /* array of GladeSignal* */ { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return g_hash_table_lookup (widget->priv->signals, signal_name); } /** * glade_widget_set_name: * @widget: a #GladeWidget * @name: a string * * Sets @widget's name to @name. */ void glade_widget_set_name (GladeWidget *widget, const gchar *name) { g_return_if_fail (GLADE_IS_WIDGET (widget)); if (widget->priv->name != name) { if (widget->priv->name) g_free (widget->priv->name); widget->priv->name = g_strdup (name); g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_NAME]); } } /** * glade_widget_get_name: * @widget: a #GladeWidget * * Returns: a pointer to @widget's name * * This is what will be serialized as the widget's ID, unless * the name currently carries the %GLADE_UNNAMED_PREFIX. */ const gchar * glade_widget_get_name (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->name; } /** * glade_widget_get_display_name: * @widget: a #GladeWidget * * Returns: a pointer to @widget's displayable name * * This should be used to display the widget's ID in * the UI, it will automatically return an "(unnamed)" * string if the widget is not intended to be serialized * with an ID (i.e. the user did not provide a name). */ G_CONST_RETURN gchar * glade_widget_get_display_name (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); if (g_str_has_prefix (widget->priv->name, GLADE_UNNAMED_PREFIX)) return _("(unnamed)"); return widget->priv->name; } /** * glade_widget_has_name: * @widget: a #GladeWidget * * Returns: whether the user has named this widget with an ID */ gboolean glade_widget_has_name (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); return !g_str_has_prefix (widget->priv->name, GLADE_UNNAMED_PREFIX); } /** * glade_widget_set_is_composite: * @widget: a #GladeWidget * @composite: whether @widget should be a composite template * * Set's this widget to be toplevel composite object to be * eventually used with gtk_widget_class_set_template() * * Only one widget in a project should be composite. */ void glade_widget_set_is_composite (GladeWidget *widget, gboolean composite) { g_return_if_fail (GLADE_IS_WIDGET (widget)); composite = !!composite; if (widget->priv->composite != composite) { GladeProject *project = glade_widget_get_project (widget); widget->priv->composite = composite; g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_COMPOSITE]); /* Update the project model when this changes */ if (widget->priv->parent == NULL && widget->priv->project != NULL && glade_project_has_object (widget->priv->project, widget->priv->object)) glade_project_widget_changed (project, widget); } } /** * glade_widget_get_is_composite: * @widget: a #GladeWidget * * Checks if @widget is a composite template to be used * with gtk_widget_class_set_template(). * * Returns: whether @widget is composite. */ gboolean glade_widget_get_is_composite (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); return widget->priv->composite; } /** * glade_widget_set_internal: * @widget: A #GladeWidget * @internal: The internal name * * Sets the internal name of @widget to @internal */ void glade_widget_set_internal (GladeWidget *widget, const gchar *internal) { g_return_if_fail (GLADE_IS_WIDGET (widget)); if (widget->priv->internal != internal) { g_free (widget->priv->internal); widget->priv->internal = g_strdup (internal); g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_INTERNAL]); } } /** * glade_widget_get_internal: * @widget: a #GladeWidget * * Returns: the internal name of @widget */ G_CONST_RETURN gchar * glade_widget_get_internal (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->internal; } /** * glade_widget_get_adaptor: * @widget: a #GladeWidget * * Returns: the #GladeWidgetAdaptor of @widget */ GladeWidgetAdaptor * glade_widget_get_adaptor (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->adaptor; } /** * glade_widget_set_project: * @widget: a #GladeWidget * @project: a #GladeProject * * Makes @widget belong to @project. */ void glade_widget_set_project (GladeWidget *widget, GladeProject *project) { if (widget->priv->project != project) { widget->priv->project = project; g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_PROJECT]); } } /** * glade_widget_get_project: * @widget: a #GladeWidget * * Returns: the #GladeProject that @widget belongs to */ GladeProject * glade_widget_get_project (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->project; } void glade_widget_set_in_project (GladeWidget *widget, gboolean in_project) { g_return_if_fail (GLADE_IS_WIDGET (widget)); widget->priv->in_project = in_project; } gboolean glade_widget_in_project (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); return widget->priv->in_project; } /** * glade_widget_get_property: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * * Returns: the #GladeProperty in @widget named @id_property */ GladeProperty * glade_widget_get_property (GladeWidget *widget, const gchar *id_property) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); g_return_val_if_fail (id_property != NULL, NULL); if (widget->priv->props_hash && (property = g_hash_table_lookup (widget->priv->props_hash, id_property))) return property; return glade_widget_get_pack_property (widget, id_property); } /** * glade_widget_get_pack_property: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * * Returns: the #GladeProperty in @widget named @id_property */ GladeProperty * glade_widget_get_pack_property (GladeWidget *widget, const gchar *id_property) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); g_return_val_if_fail (id_property != NULL, NULL); if (widget->priv->pack_props_hash && (property = g_hash_table_lookup (widget->priv->pack_props_hash, id_property))) return property; return NULL; } /** * glade_widget_property_get: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @...: The return location for the value of the said #GladeProperty * * Gets the value of @id_property in @widget * * Returns: whether @id_property was found or not. * */ gboolean glade_widget_property_get (GladeWidget *widget, const gchar *id_property, ...) { GladeProperty *property; va_list vl; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (id_property != NULL, FALSE); if ((property = glade_widget_get_property (widget, id_property)) != NULL) { va_start (vl, id_property); glade_property_get_va_list (property, vl); va_end (vl); return TRUE; } return FALSE; } /** * glade_widget_property_set: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @...: A value of the correct type for the said #GladeProperty * * Sets the value of @id_property in @widget * * Returns: whether @id_property was found or not. */ gboolean glade_widget_property_set (GladeWidget *widget, const gchar *id_property, ...) { GladeProperty *property; va_list vl; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (id_property != NULL, FALSE); if ((property = glade_widget_get_property (widget, id_property)) != NULL) { va_start (vl, id_property); glade_property_set_va_list (property, vl); va_end (vl); return TRUE; } return FALSE; } /** * glade_widget_pack_property_get: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @...: The return location for the value of the said #GladeProperty * * Gets the value of @id_property in @widget packing properties * * Returns: whether @id_property was found or not. */ gboolean glade_widget_pack_property_get (GladeWidget *widget, const gchar *id_property, ...) { GladeProperty *property; va_list vl; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (id_property != NULL, FALSE); if ((property = glade_widget_get_pack_property (widget, id_property)) != NULL) { va_start (vl, id_property); glade_property_get_va_list (property, vl); va_end (vl); return TRUE; } return FALSE; } /** * glade_widget_pack_property_set: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @...: The return location for the value of the said #GladeProperty * * Sets the value of @id_property in @widget packing properties * * Returns: whether @id_property was found or not. */ gboolean glade_widget_pack_property_set (GladeWidget *widget, const gchar *id_property, ...) { GladeProperty *property; va_list vl; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (id_property != NULL, FALSE); if ((property = glade_widget_get_pack_property (widget, id_property)) != NULL) { va_start (vl, id_property); glade_property_set_va_list (property, vl); va_end (vl); return TRUE; } return FALSE; } /** * glade_widget_property_set_sensitive: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @sensitive: setting sensitive or insensitive * @reason: a description of why the user cant edit this property * which will be used as a tooltip * * Sets the sensitivity of @id_property in @widget * * Returns: whether @id_property was found or not. */ gboolean glade_widget_property_set_sensitive (GladeWidget *widget, const gchar *id_property, gboolean sensitive, const gchar *reason) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (id_property != NULL, FALSE); if ((property = glade_widget_get_property (widget, id_property)) != NULL) { glade_property_set_sensitive (property, sensitive, reason); return TRUE; } return FALSE; } /** * glade_widget_pack_property_set_sensitive: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @sensitive: setting sensitive or insensitive * @reason: a description of why the user cant edit this property * which will be used as a tooltip * * Sets the sensitivity of @id_property in @widget's packing properties. * * Returns: whether @id_property was found or not. */ gboolean glade_widget_pack_property_set_sensitive (GladeWidget *widget, const gchar *id_property, gboolean sensitive, const gchar *reason) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (id_property != NULL, FALSE); if ((property = glade_widget_get_pack_property (widget, id_property)) != NULL) { glade_property_set_sensitive (property, sensitive, reason); return TRUE; } return FALSE; } /** * glade_widget_property_set_enabled: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @enabled: setting enabled or disabled * * Sets the enabled state of @id_property in @widget; this is * used for optional properties. * * Returns: whether @id_property was found or not. */ gboolean glade_widget_property_set_enabled (GladeWidget *widget, const gchar *id_property, gboolean enabled) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (id_property != NULL, FALSE); if ((property = glade_widget_get_property (widget, id_property)) != NULL) { glade_property_set_enabled (property, enabled); return TRUE; } return FALSE; } /** * glade_widget_pack_property_set_enabled: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @enabled: setting enabled or disabled * * Sets the enabled state of @id_property in @widget's packing * properties; this is used for optional properties. * * Returns: whether @id_property was found or not. */ gboolean glade_widget_pack_property_set_enabled (GladeWidget *widget, const gchar *id_property, gboolean enabled) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (id_property != NULL, FALSE); if ((property = glade_widget_get_pack_property (widget, id_property)) != NULL) { glade_property_set_enabled (property, enabled); return TRUE; } return FALSE; } /** * glade_widget_property_set_save_always: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @setting: the setting * * Sets whether @id_property in @widget should be special cased * to always be saved regardless of its default value. * (used for some special cases like properties * that are assigned initial values in composite widgets * or derived widget code). * * Returns: whether @id_property was found or not. */ gboolean glade_widget_property_set_save_always (GladeWidget *widget, const gchar *id_property, gboolean setting) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (id_property != NULL, FALSE); if ((property = glade_widget_get_property (widget, id_property)) != NULL) { glade_property_set_save_always (property, setting); return TRUE; } return FALSE; } /** * glade_widget_pack_property_set_save_always: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @setting: the setting * * Sets whether @id_property in @widget should be special cased * to always be saved regardless of its default value. * (used for some special cases like properties * that are assigned initial values in composite widgets * or derived widget code). * * Returns: whether @id_property was found or not. */ gboolean glade_widget_pack_property_set_save_always (GladeWidget *widget, const gchar *id_property, gboolean setting) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (id_property != NULL, FALSE); if ((property = glade_widget_get_pack_property (widget, id_property)) != NULL) { glade_property_set_save_always (property, setting); return TRUE; } return FALSE; } /** * glade_widget_property_string: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @value: the #GValue to print or %NULL * * Creates a printable string representing @id_property in * @widget, if @value is specified it will be used in place * of @id_property's real value (this is a convinience * function to print/debug properties usually from plugin * backends). * * Returns: A newly allocated string representing @id_property */ gchar * glade_widget_property_string (GladeWidget *widget, const gchar *id_property, const GValue *value) { GladeProperty *property; GladePropertyClass *pclass; gchar *ret_string = NULL; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); g_return_val_if_fail (id_property != NULL, NULL); if ((property = glade_widget_get_property (widget, id_property)) != NULL) { pclass = glade_property_get_class (property); ret_string = glade_widget_adaptor_string_from_value (glade_property_class_get_adaptor (pclass), pclass, value ? value : glade_property_inline_value (property)); } return ret_string; } /** * glade_widget_pack_property_string: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * @value: the #GValue to print or %NULL * * Same as glade_widget_property_string() but for packing * properties. * * Returns: A newly allocated string representing @id_property */ gchar * glade_widget_pack_property_string (GladeWidget *widget, const gchar *id_property, const GValue *value) { GladeProperty *property; GladePropertyClass *pclass; gchar *ret_string = NULL; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); g_return_val_if_fail (id_property != NULL, NULL); if ((property = glade_widget_get_pack_property (widget, id_property)) != NULL) { pclass = glade_property_get_class (property); ret_string = glade_widget_adaptor_string_from_value (glade_property_class_get_adaptor (pclass), pclass, value ? value : glade_property_inline_value (property)); } return ret_string; } /** * glade_widget_property_reset: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * * Resets @id_property in @widget to it's default value * * Returns: whether @id_property was found or not. */ gboolean glade_widget_property_reset (GladeWidget *widget, const gchar *id_property) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); if ((property = glade_widget_get_property (widget, id_property)) != NULL) { glade_property_reset (property); return TRUE; } return FALSE; } /** * glade_widget_pack_property_reset: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * * Resets @id_property in @widget's packing properties to it's default value * * Returns: whether @id_property was found or not. */ gboolean glade_widget_pack_property_reset (GladeWidget *widget, const gchar *id_property) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); if ((property = glade_widget_get_pack_property (widget, id_property)) != NULL) { glade_property_reset (property); return TRUE; } return FALSE; } static gboolean glade_widget_property_default_common (GladeWidget *widget, const gchar *id_property, gboolean original) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); if ((property = glade_widget_get_property (widget, id_property)) != NULL) return (original) ? glade_property_original_default (property) : glade_property_default (property); return FALSE; } /** * glade_widget_property_default: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * * Returns: whether whether @id_property was found and is * currently set to it's default value. */ gboolean glade_widget_property_default (GladeWidget *widget, const gchar *id_property) { return glade_widget_property_default_common (widget, id_property, FALSE); } /** * glade_widget_property_original_default: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * * Returns: whether whether @id_property was found and is * currently set to it's original default value. */ gboolean glade_widget_property_original_default (GladeWidget *widget, const gchar *id_property) { return glade_widget_property_default_common (widget, id_property, TRUE); } /** * glade_widget_pack_property_default: * @widget: a #GladeWidget * @id_property: a string naming a #GladeProperty * * Returns: whether whether @id_property was found and is * currently set to it's default value. */ gboolean glade_widget_pack_property_default (GladeWidget *widget, const gchar *id_property) { GladeProperty *property; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); if ((property = glade_widget_get_pack_property (widget, id_property)) != NULL) return glade_property_default (property); return FALSE; } /** * glade_widget_object_set_property: * @widget: A #GladeWidget * @property_name: The property identifier * @value: The #GValue * * This function applies @value to the property @property_name on * the runtime object of @widget. */ void glade_widget_object_set_property (GladeWidget *widget, const gchar *property_name, const GValue *value) { g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (property_name != NULL && value != NULL); glade_widget_adaptor_set_property (widget->priv->adaptor, widget->priv->object, property_name, value); } /** * glade_widget_object_get_property: * @widget: A #GladeWidget * @property_name: The property identifier * @value: The #GValue * * This function retrieves the value of the property @property_name on * the runtime object of @widget and sets it in @value. */ void glade_widget_object_get_property (GladeWidget *widget, const gchar *property_name, GValue *value) { g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (property_name != NULL && value != NULL); glade_widget_adaptor_get_property (widget->priv->adaptor, widget->priv->object, property_name, value); } /** * glade_widget_child_set_property: * @widget: A #GladeWidget * @child: The #GladeWidget child * @property_name: The id of the property * @value: The @GValue * * Sets @child's packing property identified by @property_name to @value. */ void glade_widget_child_set_property (GladeWidget *widget, GladeWidget *child, const gchar *property_name, const GValue *value) { GladeWidgetPrivate *priv, *cpriv; GList *old_order; gboolean check; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_WIDGET (child)); g_return_if_fail (property_name != NULL && value != NULL); priv = widget->priv; cpriv = child->priv; check = priv->project && priv->in_project && cpriv->project && cpriv->in_project; old_order = (check) ? glade_widget_get_children (widget) : NULL; glade_widget_adaptor_child_set_property (priv->adaptor, priv->object, cpriv->object, property_name, value); /* After setting a child property... it's possible the order of children * in the parent has been effected. * * If this is the case then we need to signal the GladeProject that * it's rows have been reordered so that any connected views update * themselves properly. */ if (check) glade_project_check_reordered (priv->project, widget, old_order); g_list_free (old_order); } /** * glade_widget_child_get_property: * @widget: A #GladeWidget * @child: The #GladeWidget child * @property_name: The id of the property * @value: The @GValue * * Gets @child's packing property identified by @property_name. */ void glade_widget_child_get_property (GladeWidget *widget, GladeWidget *child, const gchar *property_name, GValue *value) { g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_WIDGET (child)); g_return_if_fail (property_name != NULL && value != NULL); glade_widget_adaptor_child_get_property (widget->priv->adaptor, widget->priv->object, child->priv->object, property_name, value); } static void glade_widget_add_events (GtkWidget *widget) { GList *children, *list; gtk_widget_add_events (widget, GDK_POINTER_MOTION_MASK | /* Handle pointer events */ GDK_POINTER_MOTION_HINT_MASK | /* for drag/resize and */ GDK_BUTTON_PRESS_MASK | /* managing selection. */ GDK_BUTTON_RELEASE_MASK); /* We also need to get events for any children. */ if (GTK_IS_CONTAINER (widget)) { if ((children = glade_util_container_get_all_children (GTK_CONTAINER (widget))) != NULL) { for (list = children; list; list = list->next) glade_widget_add_events (GTK_WIDGET (list->data)); g_list_free (children); } } } static void glade_widget_set_object (GladeWidget *gwidget, GObject *new_object) { GObject *old_object; g_return_if_fail (GLADE_IS_WIDGET (gwidget)); g_return_if_fail (new_object == NULL || g_type_is_a (G_OBJECT_TYPE (new_object), glade_widget_adaptor_get_object_type (gwidget->priv->adaptor))); if (gwidget->priv->object == new_object) return; old_object = gwidget->priv->object; gwidget->priv->object = new_object; if (new_object) { /* Add internal reference to new widget if its not internal */ if (gwidget->priv->internal == NULL) { /* Assume initial ref count of all objects */ if (G_IS_INITIALLY_UNOWNED (new_object)) g_object_ref_sink (new_object); } g_object_set_qdata (G_OBJECT (new_object), glade_widget_name_quark, gwidget); if (g_type_is_a (glade_widget_adaptor_get_object_type (gwidget->priv->adaptor), GTK_TYPE_WIDGET)) { /* Disable any built-in DnD */ gtk_drag_dest_unset (GTK_WIDGET (new_object)); gtk_drag_source_unset (GTK_WIDGET (new_object)); /* We nee to make sure all widgets set the event glade core needs */ glade_widget_add_events (GTK_WIDGET (new_object)); } } /* Remove internal reference to old widget */ if (old_object) { if (gwidget->priv->internal == NULL) { GLADE_NOTE (REF_COUNTS, g_print ("Killing '%s::%s' widget's object with reference count %d\n", glade_widget_adaptor_get_name (gwidget->priv->adaptor), gwidget->priv->name ? gwidget->priv->name : "(unknown)", old_object->ref_count)); /* Have the adaptor for this widget break any additional references */ glade_widget_adaptor_destroy_object (gwidget->priv->adaptor, old_object); } /* From this point on, the GladeWidget is no longer retrievable with * glade_widget_get_from_gobject()... we let it be for the duration * of ->destroy_object() */ g_object_set_qdata (G_OBJECT (old_object), glade_widget_name_quark, NULL); if (gwidget->priv->internal == NULL) g_object_unref (old_object); } g_object_notify_by_pspec (G_OBJECT (gwidget), properties[PROP_OBJECT]); } /** * glade_widget_get_object: * @widget: a #GladeWidget * * Returns: the #GObject associated with @widget */ GObject * glade_widget_get_object (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->object; } /** * glade_widget_get_parent: * @widget: A #GladeWidget * * Returns: The parenting #GladeWidget */ GladeWidget * glade_widget_get_parent (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->parent; } /** * glade_widget_set_parent: * @widget: A #GladeWidget * @parent: the parenting #GladeWidget (or %NULL) * * sets the parenting #GladeWidget */ void glade_widget_set_parent (GladeWidget *widget, GladeWidget *parent) { GladeWidget *old_parent; g_return_if_fail (GLADE_IS_WIDGET (widget)); old_parent = widget->priv->parent; widget->priv->parent = parent; /* Set packing props only if the object is actually parented by 'parent' * (a subsequent call should come from glade_command after parenting). */ if (widget->priv->object && parent != NULL && glade_widget_adaptor_has_child (parent->priv->adaptor, parent->priv->object, widget->priv->object)) { if (old_parent == NULL || widget->priv->packing_properties == NULL || old_parent->priv->adaptor != parent->priv->adaptor) glade_widget_set_packing_properties (widget, parent); else glade_widget_sync_packing_props (widget); } if (parent) glade_widget_set_packing_actions (widget, parent); g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_PARENT]); } /** * glade_widget_find_child: * @widget: A #GladeWidget * @name: child name * * Finds a child widget named @name. * * Returns: The child of widget or NULL if it was not found. */ GladeWidget * glade_widget_find_child (GladeWidget *widget, const gchar *name) { GList *adapter_children; GladeWidget *real_child = NULL; GList *node; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); adapter_children = glade_widget_adaptor_get_children (glade_widget_get_adaptor (widget), widget->priv->object); for (node = adapter_children; node && !real_child; node = g_list_next (node)) { GladeWidget *child = glade_widget_get_from_gobject (node->data); if (child) { if (strcmp (child->priv->name, name) == 0) real_child = child; else real_child = glade_widget_find_child (child, name); } } g_list_free (adapter_children); return real_child; } /** * glade_widget_get_children: * @widget: A #GladeWidget * * Fetches any wrapped children of @widget. * * Returns: The children of widget * * This differs from a direct call to glade_widget_adaptor_get_children() as * it only returns children which have an associated GladeWidget. This function will * not return any placeholders or internal composite children that have not been * exposed for Glade configuration */ GList * glade_widget_get_children (GladeWidget *widget) { GList *adapter_children; GList *real_children = NULL; GList *node; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); adapter_children = glade_widget_adaptor_get_children (glade_widget_get_adaptor (widget), widget->priv->object); for (node = adapter_children; node != NULL; node = g_list_next (node)) { if (glade_widget_get_from_gobject (node->data)) { real_children = g_list_prepend (real_children, node->data); } } g_list_free (adapter_children); return g_list_reverse (real_children); } /** * glade_widget_get_toplevel: * @widget: A #GladeWidget * * Returns: The toplevel #GladeWidget in the hierarchy (or @widget) */ GladeWidget * glade_widget_get_toplevel (GladeWidget *widget) { GladeWidget *toplevel = widget; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); while (toplevel->priv->parent) toplevel = toplevel->priv->parent; return toplevel; } /** * glade_widget_set_packing_properties: * @widget: A #GladeWidget * @container: The parent #GladeWidget * * Generates the packing_properties list of the widget, given * the class of the container we are adding the widget to. * If the widget already has packing_properties, but the container * has changed, the current list is freed and replaced. */ void glade_widget_set_packing_properties (GladeWidget *widget, GladeWidget *container) { GList *list; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_WIDGET (container)); /* Avoid rewriting and losing packing properties when * reparenting in the process of rebuilding a widget instance. */ if (widget->priv->rebuilding) return; g_list_foreach (widget->priv->packing_properties, (GFunc) g_object_unref, NULL); g_list_free (widget->priv->packing_properties); widget->priv->packing_properties = NULL; if (widget->priv->pack_props_hash) g_hash_table_destroy (widget->priv->pack_props_hash); widget->priv->pack_props_hash = NULL; /* We have to detect whether this is an anarchist child of a composite * widget or not, in otherwords; whether its really a direct child or * a child of a popup window created on the composite widget's behalf. */ if (widget->priv->anarchist) return; widget->priv->packing_properties = glade_widget_create_packing_properties (container, widget); widget->priv->pack_props_hash = g_hash_table_new (g_str_hash, g_str_equal); /* update the quick reference hash table */ for (list = widget->priv->packing_properties; list && list->data; list = list->next) { GladeProperty *property = list->data; GladePropertyClass *pclass = glade_property_get_class (property); g_hash_table_insert (widget->priv->pack_props_hash, (gchar *)glade_property_class_id (pclass), property); } /* Dont introspect on properties that are not parented yet. */ if (glade_widget_adaptor_has_child (container->priv->adaptor, container->priv->object, widget->priv->object)) { glade_widget_set_default_packing_properties (container, widget); /* update the values of the properties to the ones we get from gtk */ for (list = widget->priv->packing_properties; list && list->data; list = list->next) { /* XXX Ugly dangerous code, plays with the property value inline */ GladeProperty *property = list->data; GladePropertyClass *pclass = glade_property_get_class (property); GValue *value = glade_property_inline_value (property); g_value_reset (value); glade_widget_child_get_property (container, widget, glade_property_class_id (pclass), value); } } } /** * glade_widget_has_decendant: * @widget: a #GladeWidget * @type: a #GType * * Returns: whether this GladeWidget has any decendants of type @type * or any decendants that implement the @type interface */ gboolean glade_widget_has_decendant (GladeWidget *widget, GType type) { GladeWidget *child; GList *children, *l; gboolean found = FALSE; if (glade_widget_adaptor_get_object_type (widget->priv->adaptor) == type || g_type_is_a (glade_widget_adaptor_get_object_type (widget->priv->adaptor), type)) return TRUE; if ((children = glade_widget_adaptor_get_children (widget->priv->adaptor, widget->priv->object)) != NULL) { for (l = children; l; l = l->next) if ((child = glade_widget_get_from_gobject (l->data)) != NULL && (found = glade_widget_has_decendant (child, type))) break; g_list_free (children); } return found; } /** * glade_widget_replace: * @old_object: a #GObject * @new_object: a #GObject * * Replaces a GObject with another GObject inside a GObject which * behaves as a container. * * Note that both GObjects must be owned by a GladeWidget. */ void glade_widget_replace (GladeWidget *parent, GObject *old_object, GObject *new_object) { g_return_if_fail (G_IS_OBJECT (old_object)); g_return_if_fail (G_IS_OBJECT (new_object)); GLADE_WIDGET_GET_CLASS (parent)->replace_child (parent, old_object, new_object); } /******************************************************************************* * Xml Parsing code * *******************************************************************************/ /* XXX Doc me !*/ void glade_widget_write_special_child_prop (GladeWidget *parent, GObject *object, GladeXmlContext *context, GladeXmlNode *node) { gchar *buff, *special_child_type; buff = g_object_get_data (object, "special-child-type"); g_object_get (parent->priv->adaptor, "special-child-type", &special_child_type, NULL); if (special_child_type && buff) { glade_xml_node_set_property_string (node, GLADE_XML_TAG_TYPE, buff); } g_free (special_child_type); } /* XXX Doc me ! */ void glade_widget_set_child_type_from_node (GladeWidget *parent, GObject *child, GladeXmlNode *node) { gchar *special_child_type, *value; if (!glade_xml_node_verify (node, GLADE_XML_TAG_CHILD)) return; g_object_get (parent->priv->adaptor, "special-child-type", &special_child_type, NULL); if (!special_child_type) return; /* all child types here are depicted by the "type" property */ if ((value = glade_xml_get_property_string (node, GLADE_XML_TAG_TYPE))) { g_object_set_data_full (child, "special-child-type", value, g_free); } g_free (special_child_type); } /** * glade_widget_read_child: * @widget: A #GladeWidget * @node: a #GladeXmlNode * * Reads in a child widget from the xml (handles 'child' tag) */ void glade_widget_read_child (GladeWidget *widget, GladeXmlNode *node) { if (glade_project_load_cancelled (widget->priv->project)) return; glade_widget_adaptor_read_child (widget->priv->adaptor, widget, node); } /** * glade_widget_read: * @project: a #GladeProject * @parent: The parent #GladeWidget or %NULL * @node: a #GladeXmlNode * * Returns: a new #GladeWidget for @project, based on @node */ GladeWidget * glade_widget_read (GladeProject *project, GladeWidget *parent, GladeXmlNode *node, const gchar *internal) { GladeWidgetAdaptor *adaptor; GladeWidget *widget = NULL; gchar *klass, *id = NULL, *template_parent = NULL; gboolean template = FALSE; GType type; const gchar *type_to_use; if (glade_project_load_cancelled (project)) return NULL; if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) || glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE))) return NULL; if (glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE)) template = TRUE; glade_widget_push_superuser (); if ((klass = glade_xml_get_property_string_required (node, GLADE_XML_TAG_CLASS, NULL)) != NULL) { if (template) { template_parent = glade_xml_get_property_string_required (node, GLADE_TAG_PARENT, NULL); if (template_parent) id = g_strdup (klass); } else { id = glade_xml_get_property_string (node, GLADE_XML_TAG_ID); /* Here we use an internal unnamed prefix to identify unnamed widgets, then * we just issue a warning if anyone was dense enough to actually use * this prefix in a project as a widget name. */ if (!id) id = glade_project_new_widget_name (project, NULL, GLADE_UNNAMED_PREFIX); else if (strncmp (id, GLADE_UNNAMED_PREFIX, strlen (GLADE_UNNAMED_PREFIX)) == 0) g_warning ("Loaded widget `%s' has internal glade prefix, please rename this widget", id); } type_to_use = template_parent ? template_parent : klass; /* * Create GladeWidget instance based on type. */ if ((adaptor = glade_widget_adaptor_get_by_name (type_to_use)) && (type = glade_widget_adaptor_get_object_type (adaptor)) && G_TYPE_IS_INSTANTIATABLE (type) && G_TYPE_IS_ABSTRACT (type) == FALSE) { /* Internal children !!! */ if (internal) { GObject *child_object = glade_widget_get_internal_child (NULL, parent, internal); if (!child_object) { g_warning ("Failed to locate " "internal child %s of %s", internal, glade_widget_get_name (parent)); goto out; } if (!(widget = glade_widget_get_from_gobject (child_object))) g_error ("Unable to get GladeWidget " "for internal child %s\n", internal); /* Apply internal widget name from here */ glade_widget_set_name (widget, id); } else { widget = glade_widget_adaptor_create_widget (adaptor, FALSE, "name", id, "composite", template, "parent", parent, "project", project, "reason", GLADE_CREATE_LOAD, NULL); } glade_widget_adaptor_read_widget (adaptor, widget, node); } else { GObject *stub = g_object_new (GLADE_TYPE_OBJECT_STUB, "object-type", klass, "xml-node", node, NULL); widget = glade_widget_adaptor_create_widget (glade_widget_adaptor_get_by_type (GTK_TYPE_WIDGET), FALSE, "parent", parent, "composite", template, "project", project, "reason", GLADE_CREATE_LOAD, "object", stub, "name", id, NULL); } g_free (id); g_free (template_parent); g_free (klass); } out: glade_widget_pop_superuser (); glade_project_push_progress (project); return widget; } /** * glade_widget_write_child: * @widget: A #GladeWidget * @child: The child #GladeWidget to write * @context: A #GladeXmlContext * @node: A #GladeXmlNode * * Writes out a widget to the xml, takes care * of packing properties and special child types. */ void glade_widget_write_child (GladeWidget *widget, GladeWidget *child, GladeXmlContext *context, GladeXmlNode *node) { g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_WIDGET (child)); g_return_if_fail (child->priv->parent == widget); glade_widget_adaptor_write_child (widget->priv->adaptor, child, context, node); } /** * glade_widget_write_placeholder: * @parent: The parent #GladeWidget * @object: A #GladePlaceHolder * @context: A #GladeXmlContext * @node: A #GladeXmlNode * * Writes out a placeholder to the xml */ void glade_widget_write_placeholder (GladeWidget *parent, GObject *object, GladeXmlContext *context, GladeXmlNode *node) { GladeXmlNode *child_node, *packing_node, *placeholder_node; child_node = glade_xml_node_new (context, GLADE_XML_TAG_CHILD); glade_xml_node_append_child (node, child_node); placeholder_node = glade_xml_node_new (context, GLADE_XML_TAG_PLACEHOLDER); glade_xml_node_append_child (child_node, placeholder_node); /* maybe write out special-child-type here */ packing_node = glade_xml_node_new (context, GLADE_XML_TAG_PACKING); glade_xml_node_append_child (child_node, packing_node); glade_widget_write_special_child_prop (parent, object, context, child_node); if (!glade_xml_node_get_children (packing_node)) { glade_xml_node_remove (packing_node); glade_xml_node_delete (packing_node); } } static gint signal_compare (GladeSignal *signal_a, GladeSignal *signal_b) { const gchar *handler_a; const gchar *handler_b; const GladeSignalClass *class_a; const GladeSignalClass *class_b; const gchar *class_name_a; const gchar *class_name_b; const gchar *detail_a; const gchar *detail_b; const gchar *data_a; const gchar *data_b; gint comparison; handler_a = glade_signal_get_handler (signal_a); handler_b = glade_signal_get_handler (signal_b); detail_a = glade_signal_get_detail (signal_a); detail_b = glade_signal_get_detail (signal_b); data_a = glade_signal_get_userdata (signal_a); data_b = glade_signal_get_userdata (signal_b); class_a = glade_signal_get_class (signal_a); class_b = glade_signal_get_class (signal_b); class_name_a = glade_signal_class_get_name (class_a); class_name_b = glade_signal_class_get_name (class_b); /* By signal name ... */ comparison = g_strcmp0 (class_name_a, class_name_b); if (comparison != 0) return comparison; /* By handler name ... */ comparison = g_strcmp0 (handler_a, handler_b); if (comparison != 0) return comparison; /* By detail name ... */ comparison = g_strcmp0 (detail_a, detail_b); if (comparison != 0) return comparison; /* By user data ... */ comparison = g_strcmp0 (data_a, data_b); if (comparison != 0) return comparison; /* By 'after' flag ... */ comparison = glade_signal_get_after (signal_a) - glade_signal_get_after (signal_b); if (comparison != 0) return comparison; /* By 'swapped' flag ... */ comparison = glade_signal_get_swapped (signal_a) - glade_signal_get_swapped (signal_b); if (comparison != 0) return comparison; /* They are actually exactly the same ... return 0 */ return 0; } void glade_widget_write_signals (GladeWidget *widget, GladeXmlContext *context, GladeXmlNode *node) { GHashTableIter iter; gpointer key, value; GladeSignal *signal; GList *sorted_signals = NULL, *l; /* Sort signals alphabetically by signal name (and then by handler name) * before saving them. * * Ensure we don't introduce useless project diffs at save time. */ g_hash_table_iter_init (&iter, widget->priv->signals); while (g_hash_table_iter_next (&iter, &key, &value)) { GPtrArray *signals = (GPtrArray *)value; gint i; for (i = 0; i < signals->len; i++) { signal = g_ptr_array_index (signals, i); sorted_signals = g_list_prepend (sorted_signals, signal); } } sorted_signals = g_list_sort (sorted_signals, (GCompareFunc)signal_compare); for (l = sorted_signals; l; l = l->next) { signal = l->data; glade_signal_write (signal, context, node); } g_list_free (sorted_signals); } /** * glade_widget_write: * @widget: The #GladeWidget * @context: A #GladeXmlContext * @node: A #GladeXmlNode * * Recursively writes out @widget and its children * and appends the created #GladeXmlNode to @node. */ void glade_widget_write (GladeWidget *widget, GladeXmlContext *context, GladeXmlNode *node) { GObject *object = glade_widget_get_object (widget); GladeXmlNode *widget_node; GList *l, *list; /* Check if its an unknown object, and use saved xml if so */ if (GLADE_IS_OBJECT_STUB (object)) { g_object_get (object, "xml-node", &widget_node, NULL); glade_xml_node_append_child (node, widget_node); return; } /* Set class and id */ if (widget->priv->composite) { widget_node = glade_xml_node_new (context, GLADE_XML_TAG_TEMPLATE); glade_xml_node_set_property_string (widget_node, GLADE_XML_TAG_CLASS, widget->priv->name); glade_xml_node_set_property_string (widget_node, GLADE_TAG_PARENT, glade_widget_adaptor_get_name (widget->priv->adaptor)); } else { widget_node = glade_xml_node_new (context, GLADE_XML_TAG_WIDGET); glade_xml_node_set_property_string (widget_node, GLADE_XML_TAG_CLASS, glade_widget_adaptor_get_name (widget->priv->adaptor)); /* Conditionally omit the ID in the output if the name is 'unset' */ if (glade_widget_has_name (widget) || glade_project_writing_preview (widget->priv->project)) glade_xml_node_set_property_string (widget_node, GLADE_XML_TAG_ID, widget->priv->name); } glade_xml_node_append_child (node, widget_node); /* Write out widget content (properties and signals) */ glade_widget_adaptor_write_widget (widget->priv->adaptor, widget, context, widget_node); /* Write the signals strictly after all properties and before children */ glade_widget_write_signals (widget, context, widget_node); /* Write the children */ if ((list = glade_widget_adaptor_get_children (widget->priv->adaptor, widget->priv->object)) != NULL) { for (l = list; l; l = l->next) { GladeWidget *child = glade_widget_get_from_gobject (l->data); if (child) glade_widget_write_child (widget, child, context, widget_node); else if (GLADE_IS_PLACEHOLDER (l->data)) glade_widget_write_placeholder (widget, G_OBJECT (l->data), context, widget_node); } g_list_free (list); } /* Write out trailing widget content (anything that goes after children) */ glade_widget_adaptor_write_widget_after (widget->priv->adaptor, widget, context, widget_node); } /** * glade_widget_is_ancestor: * @widget: a #GladeWidget * @ancestor: another #GladeWidget * * Determines whether @widget is somewhere inside @ancestor, possibly with * intermediate containers. * * Return value: %TRUE if @ancestor contains @widget as a child, * grandchild, great grandchild, etc. **/ gboolean glade_widget_is_ancestor (GladeWidget *widget, GladeWidget *ancestor) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); g_return_val_if_fail (GLADE_IS_WIDGET (ancestor), FALSE); while (widget) { if (widget->priv->parent == ancestor) return TRUE; widget = widget->priv->parent; } return FALSE; } /** * glade_widget_depends: * @widget: a #GladeWidget * @other: another #GladeWidget * * Determines whether @widget is somehow dependent on @other, in * which case it should be serialized after @other. * * A widget is dependent on another widget. * It does not take into account for children dependencies. * * Return value: %TRUE if @widget depends on @other. * * Deprecated: 3.18 **/ gboolean glade_widget_depends (GladeWidget *widget, GladeWidget *other) { return FALSE; } /** * glade_widget_get_device_from_event: * @event: a GdkEvent * * Currently only motion and button events are handled (see IS_GLADE_WIDGET_EVENT) * * Returns: the asociated GdkDevice for this glade widget event. * * Deprecated: use gdk_event_get_device() instead. */ GdkDevice * glade_widget_get_device_from_event (GdkEvent *event) { return gdk_event_get_device (event); } static gint glade_widget_su_stack = 0; /** * glade_widget_superuser: * * Checks if we are in superuser mode. * * Superuser mode is when we are * - Loading a project * - Dupping a widget recursively * - Rebuilding an instance for a construct-only property * * In these cases, we must act like a load, this should be checked * from the plugin when implementing containers, when undo/redo comes * around, the plugin is responsable for maintaining the same container * size when widgets are added/removed. */ gboolean glade_widget_superuser (void) { return glade_widget_su_stack > 0; } /** * glade_widget_push_superuser: * * Sets superuser mode */ void glade_widget_push_superuser (void) { glade_property_push_superuser (); glade_widget_su_stack++; } /** * glade_widget_pop_superuser: * * Unsets superuser mode */ void glade_widget_pop_superuser (void) { if (--glade_widget_su_stack < 0) { g_critical ("Bug: widget super user stack is corrupt.\n"); } glade_property_pop_superuser (); } /** * glade_widget_placeholder_relation: * @parent: A #GladeWidget * @widget: The child #GladeWidget * * Returns whether placeholders should be used * in operations concerning this parent & child. * * Currently that criteria is whether @parent is a * GtkContainer, @widget is a GtkWidget and the parent * adaptor has been marked to use placeholders. * * Returns: whether to use placeholders for this relationship. */ gboolean glade_widget_placeholder_relation (GladeWidget *parent, GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (parent), FALSE); g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); return (GTK_IS_CONTAINER (parent->priv->object) && GTK_IS_WIDGET (widget->priv->object) && GWA_USE_PLACEHOLDERS (parent->priv->adaptor)); } static GladeWidgetAction * glade_widget_action_lookup (GList *actions, const gchar *path) { GList *l; for (l = actions; l; l = g_list_next (l)) { GladeWidgetAction *action = l->data; GWActionClass *aclass = glade_widget_action_get_class (action); GList *children = glade_widget_action_get_children (action); if (strcmp (aclass->path, path) == 0) return action; if (children && g_str_has_prefix (path, aclass->path) && (action = glade_widget_action_lookup (children, path))) return action; } return NULL; } /** * glade_widget_get_action: * @widget: a #GladeWidget * @action_path: a full action path including groups * * Returns a #GladeWidgetAction object indentified by @action_path. * * Returns: the action or NULL if not found. */ GladeWidgetAction * glade_widget_get_action (GladeWidget *widget, const gchar *action_path) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); g_return_val_if_fail (action_path != NULL, NULL); return glade_widget_action_lookup (widget->priv->actions, action_path); } /** * glade_widget_get_pack_action: * @widget: a #GladeWidget * @action_path: a full action path including groups * * Returns a #GladeWidgetAction object indentified by @action_path. * * Returns: the action or NULL if not found. */ GladeWidgetAction * glade_widget_get_pack_action (GladeWidget *widget, const gchar *action_path) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); g_return_val_if_fail (action_path != NULL, NULL); return glade_widget_action_lookup (widget->priv->packing_actions, action_path); } GList * glade_widget_get_actions (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->actions; } GList * glade_widget_get_pack_actions (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->packing_actions; } /** * glade_widget_set_action_sensitive: * @widget: a #GladeWidget * @action_path: a full action path including groups * @sensitive: setting sensitive or insensitive * * Sets the sensitivity of @action_path in @widget * * Returns: whether @action_path was found or not. */ gboolean glade_widget_set_action_sensitive (GladeWidget *widget, const gchar *action_path, gboolean sensitive) { GladeWidgetAction *action; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); if ((action = glade_widget_get_action (widget, action_path)) != NULL) { glade_widget_action_set_sensitive (action, sensitive); return TRUE; } return FALSE; } /** * glade_widget_set_pack_action_sensitive: * @widget: a #GladeWidget * @action_path: a full action path including groups * @sensitive: setting sensitive or insensitive * * Sets the sensitivity of @action_path in @widget * * Returns: whether @action_path was found or not. */ gboolean glade_widget_set_pack_action_sensitive (GladeWidget *widget, const gchar *action_path, gboolean sensitive) { GladeWidgetAction *action; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); if ((action = glade_widget_get_pack_action (widget, action_path)) != NULL) { glade_widget_action_set_sensitive (action, sensitive); return TRUE; } return FALSE; } /** * glade_widget_set_action_visible: * @widget: a #GladeWidget * @action_path: a full action path including groups * @visible: setting visible or invisible * * Sets the visibility of @action_path in @widget * * Returns: whether @action_path was found or not. */ gboolean glade_widget_set_action_visible (GladeWidget *widget, const gchar *action_path, gboolean visible) { GladeWidgetAction *action; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); if ((action = glade_widget_get_action (widget, action_path)) != NULL) { glade_widget_action_set_visible (action, visible); return TRUE; } return FALSE; } /** * glade_widget_set_pack_action_visible: * @widget: a #GladeWidget * @action_path: a full action path including groups * @visible: setting visible or invisible * * Sets the visibility of @action_path in @widget * * Returns: whether @action_path was found or not. */ gboolean glade_widget_set_pack_action_visible (GladeWidget *widget, const gchar *action_path, gboolean visible) { GladeWidgetAction *action; g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); if ((action = glade_widget_get_pack_action (widget, action_path)) != NULL) { glade_widget_action_set_visible (action, visible); return TRUE; } return FALSE; } /** * glade_widget_create_editor_property: * @widget: A #GladeWidget * @property: The widget's property * @packing: whether @property indicates a packing property or not. * @use_command: Whether the undo/redo stack applies here. * * This is a convenience function to create a GladeEditorProperty corresponding * to @property * * Returns: A newly created and connected GladeEditorProperty */ GladeEditorProperty * glade_widget_create_editor_property (GladeWidget *widget, const gchar *property, gboolean packing, gboolean use_command) { GladeEditorProperty *eprop; GladeProperty *prop; GladePropertyClass *pclass; if (packing) prop = glade_widget_get_pack_property (widget, property); else prop = glade_widget_get_property (widget, property); g_return_val_if_fail (GLADE_IS_PROPERTY (prop), NULL); pclass = glade_property_get_class (prop); eprop = glade_widget_adaptor_create_eprop (widget->priv->adaptor, pclass, use_command); glade_editor_property_load (eprop, prop); return eprop; } /** * glade_widget_generate_path_name: * @widget: A #GladeWidget * * Creates a user friendly name to describe project widgets * * Returns: A newly allocated string */ gchar * glade_widget_generate_path_name (GladeWidget *widget) { GString *string; GladeWidget *iter; g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); string = g_string_new (widget->priv->name); for (iter = widget->priv->parent; iter; iter = iter->priv->parent) { gchar *str = g_strdup_printf ("%s:", iter->priv->name); g_string_prepend (string, str); g_free (str); } return g_string_free (string, FALSE); } /** * glade_widget_verify: * @widget: A #GladeWidget * * Verify this widget for deprecation and versioning warnings. * * This function will update the widget's support warning. */ void glade_widget_verify (GladeWidget *widget) { GladeWidgetPrivate *priv; gchar *warning = NULL; g_return_if_fail (GLADE_IS_WIDGET (widget)); priv = widget->priv; if (priv->project == NULL) return; if (priv->composite) { gint major, minor; glade_project_get_target_version (priv->project, "gtk+", &major, &minor); if (major == 3 && minor < 10) warning = g_strdup_printf (_("Template classes are not supported in gtk+ %d.%d"), major, minor); } if (!warning && GLADE_IS_OBJECT_STUB (priv->object)) { gchar *type; g_object_get (priv->object, "object-type", &type, NULL); warning = g_strdup_printf (_("Object has unrecognized type %s"), type); g_free (type); } if (!warning) warning = glade_project_verify_widget_adaptor (priv->project, priv->adaptor, NULL); /* If there is already support issues with the adaptor, skip signals * and properties */ if (!warning) { GList *warn_properties = NULL; GList *warn_signals = NULL; GString *string = NULL; GHashTableIter iter; gpointer key, value; GList *l; /* Collect signals with warnings on them */ g_hash_table_iter_init (&iter, priv->signals); while (g_hash_table_iter_next (&iter, &key, &value)) { GPtrArray *signals = (GPtrArray *)value; gint i; for (i = 0; i < signals->len; i++) { GladeSignal *signal = g_ptr_array_index (signals, i); if (glade_signal_get_support_warning (signal)) warn_signals = g_list_prepend (warn_signals, signal); } } /* Collect properties with warnings on them */ for (l = priv->properties; l; l = g_list_next (l)) { GladeProperty *property = l->data; if (glade_property_warn_usage (property)) warn_properties = g_list_prepend (warn_properties, property); } for (l = priv->packing_properties; l; l = g_list_next (l)) { GladeProperty *property = l->data; if (glade_property_warn_usage (property)) warn_properties = g_list_prepend (warn_properties, property); } if (warn_signals || warn_properties) string = g_string_new (NULL); /* Print out property warnings */ for (l = warn_properties; l; l = g_list_next (l)) { GladeProperty *property = l->data; GladePropertyClass *pclass = glade_property_get_class (property); if (l->prev == NULL) { if (l->next == NULL) g_string_append (string, _("Property has versioning problems: ")); else g_string_append (string, _("Some properties have versioning problems: ")); } else g_string_append (string, ", "); g_string_append (string, glade_property_class_get_name (pclass)); } /* New line if printing both */ if (warn_signals && warn_properties) g_string_append (string, "\n"); /* Print out signal warnings */ for (l = warn_signals; l; l = g_list_next (l)) { GladeSignal *signal = l->data; if (l->prev == NULL) { if (l->next == NULL) g_string_append (string, _("Signal has versioning problems: ")); else g_string_append (string, _("Some signals have versioning problems: ")); } else g_string_append (string, ", "); g_string_append (string, glade_signal_get_name (signal)); } if (string) warning = g_string_free (string, FALSE); } glade_widget_set_support_warning (widget, warning); g_free (warning); } void glade_widget_set_support_warning (GladeWidget *widget, const gchar *warning) { g_return_if_fail (GLADE_IS_WIDGET (widget)); if (widget->priv->support_warning) g_free (widget->priv->support_warning); widget->priv->support_warning = g_strdup (warning); if (widget->priv->project && glade_project_has_object (widget->priv->project, widget->priv->object)) glade_project_widget_changed (widget->priv->project, widget); g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_SUPPORT_WARNING]); } G_CONST_RETURN gchar * glade_widget_support_warning (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->support_warning; } /** * glade_widget_lock: * @widget: A #GladeWidget * @locked: The #GladeWidget to lock * * Sets @locked to be in a locked up state * spoken for by @widget, locked widgets cannot * be removed from the project until unlocked. * */ void glade_widget_lock (GladeWidget *widget, GladeWidget *locked) { g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_WIDGET (locked)); g_return_if_fail (locked->priv->lock == NULL); locked->priv->lock = widget; widget->priv->locked_widgets = g_list_prepend (widget->priv->locked_widgets, locked); } /** * glade_widget_unlock: * @widget: A #GladeWidget * * Unlocks @widget so that it can be removed * from the project again * */ void glade_widget_unlock (GladeWidget *widget) { GladeWidget *lock; g_return_if_fail (GLADE_IS_WIDGET (widget)); g_return_if_fail (GLADE_IS_WIDGET (widget->priv->lock)); lock = widget->priv->lock; lock->priv->locked_widgets = g_list_remove (lock->priv->locked_widgets, widget); widget->priv->lock = NULL; } GladeWidget * glade_widget_get_locker (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->lock; } GList * glade_widget_list_locked_widgets (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return g_list_copy (widget->priv->locked_widgets); } /** * glade_widget_support_changed: * @widget: A #GladeWidget * * Notifies that support metadata has changed on the widget. * */ void glade_widget_support_changed (GladeWidget *widget) { g_return_if_fail (GLADE_IS_WIDGET (widget)); g_signal_emit (widget, glade_widget_signals[SUPPORT_CHANGED], 0); } /** * glade_widget_get_signal_model: * @widget: A #GladeWidget * * Returns: a GtkTreeModel that can be used to view the widget's signals. * The signal model is owned by the #GladeWidget. */ GtkTreeModel * glade_widget_get_signal_model (GladeWidget *widget) { if (!widget->priv->signal_model) { widget->priv->signal_model = glade_signal_model_new (widget, widget->priv->signals); } return widget->priv->signal_model; } GList * glade_widget_get_properties (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->properties; } GList * glade_widget_get_packing_properties (GladeWidget *widget) { g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); return widget->priv->packing_properties; } void glade_widget_ensure_name (GladeWidget *widget, GladeProject *project, gboolean use_command) { if (!glade_widget_has_name (widget)) { gchar *new_name = glade_project_new_widget_name (project, NULL, glade_widget_adaptor_get_generic_name (widget->priv->adaptor)); if (use_command) glade_command_set_name (widget, new_name); else glade_widget_set_name (widget, new_name); g_free (new_name); } }