/*
* 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 <e98cuenc@yahoo.com>
* Chema Celorio <chema@celorio.com>
* Tristan Van Berkom <tvb@gnome.org>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#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 <string.h>
#include <glib-object.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n-lib.h>
#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
*
* <note><para>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</para></note>
*/
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);
}
}