/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* 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-widget.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"
static void glade_widget_set_adaptor (GladeWidget *widget,
GladeWidgetAdaptor *adaptor);
static void glade_widget_set_properties (GladeWidget *widget,
GList *properties);
static GParameter *glade_widget_info_params (GladeWidgetAdaptor *adaptor,
GladeWidgetInfo *info,
gboolean construct,
guint *n_params);
static void glade_widget_fill_from_widget_info (GladeWidgetInfo *info,
GladeWidget *widget,
gboolean apply_props);
static GladeWidget *glade_widget_new_from_widget_info (GladeWidgetInfo *info,
GladeProject *project,
GladeWidget *parent);
static gboolean glade_window_is_embedded (GtkWindow *window);
static void glade_widget_embed (GladeWidget *widget);
enum
{
ADD_SIGNAL_HANDLER,
REMOVE_SIGNAL_HANDLER,
CHANGE_SIGNAL_HANDLER,
BUTTON_PRESS_EVENT,
BUTTON_RELEASE_EVENT,
MOTION_NOTIFY_EVENT,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_NAME,
PROP_INTERNAL,
PROP_ANARCHIST,
PROP_OBJECT,
PROP_ADAPTOR,
PROP_PROJECT,
PROP_PROPERTIES,
PROP_PARENT,
PROP_INTERNAL_NAME,
PROP_TEMPLATE,
PROP_INFO,
PROP_REASON,
PROP_TOPLEVEL_WIDTH,
PROP_TOPLEVEL_HEIGHT
};
static guint glade_widget_signals[LAST_SIGNAL] = {0};
/* Sometimes we need to use the project deep in the loading code,
* this is just a shortcut way to get the project.
*/
static GladeProject *loading_project = NULL;
static GQuark glade_widget_name_quark = 0;
G_DEFINE_TYPE (GladeWidget, glade_widget, G_TYPE_OBJECT)
/*******************************************************************************
GladeWidget class methods
*******************************************************************************/
static void
glade_widget_set_packing_actions (GladeWidget *widget, GladeWidget *parent)
{
if (widget->packing_actions)
{
g_list_foreach (widget->packing_actions, (GFunc)g_object_unref, NULL);
g_list_free (widget->packing_actions);
widget->packing_actions = NULL;
}
if (parent->adaptor->packing_actions)
widget->packing_actions = glade_widget_adaptor_pack_actions_new (parent->adaptor);
}
static void
glade_widget_add_child_impl (GladeWidget *widget,
GladeWidget *child,
gboolean at_mouse)
{
/* 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->adaptor, widget->object, child->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->adaptor, widget->object, child->object);
}
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)
{
gnew_widget->parent = widget;
/* Set packing actions first so we have access from the plugin
*/
glade_widget_set_packing_actions (gnew_widget, widget);
}
if (gold_widget && gold_widget != gnew_widget)
gold_widget->parent = NULL;
glade_widget_adaptor_replace_child
(widget->adaptor, widget->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);
}
static void
glade_widget_add_signal_handler_impl (GladeWidget *widget, 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, signal_handler->name);
if (!signals)
{
signals = g_ptr_array_new ();
g_hash_table_insert (widget->signals, g_strdup (signal_handler->name), signals);
}
new_signal_handler = glade_signal_clone (signal_handler);
g_ptr_array_add (signals, new_signal_handler);
}
static void
glade_widget_remove_signal_handler_impl (GladeWidget *widget, 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, signal_handler->name);
/* 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))
{
glade_signal_free (tmp_signal_handler);
g_ptr_array_remove_index (signals, i);
break;
}
}
}
static void
glade_widget_change_signal_handler_impl (GladeWidget *widget,
GladeSignal *old_signal_handler,
GladeSignal *new_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 (old_signal_handler));
g_return_if_fail (GLADE_IS_SIGNAL (new_signal_handler));
g_return_if_fail (strcmp (old_signal_handler->name, new_signal_handler->name) == 0);
signals = glade_widget_list_signal_handlers (widget, old_signal_handler->name);
/* 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, old_signal_handler))
{
if (strcmp (old_signal_handler->handler,
new_signal_handler->handler) != 0)
{
g_free (tmp_signal_handler->handler);
tmp_signal_handler->handler =
g_strdup (new_signal_handler->handler);
}
/* Handler */
if (tmp_signal_handler->handler)
g_free (tmp_signal_handler->handler);
tmp_signal_handler->handler =
g_strdup (new_signal_handler->handler);
/* Object */
if (tmp_signal_handler->userdata)
g_free (tmp_signal_handler->userdata);
tmp_signal_handler->userdata =
g_strdup (new_signal_handler->userdata);
tmp_signal_handler->after = new_signal_handler->after;
tmp_signal_handler->lookup = new_signal_handler->lookup;
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_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->project,
gwidget->object))
glade_app_selection_remove
(gwidget->object, TRUE);
else
glade_app_selection_add
(gwidget->object, TRUE);
handled = TRUE;
}
else if (glade_project_is_selected (gwidget->project,
gwidget->object) == FALSE)
{
glade_util_clear_selection ();
glade_app_selection_set
(gwidget->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);
}
}
else if (event->button == 3)
{
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);
#if 0
if (event->type != GDK_EXPOSE)
g_print ("event widget '%s' handled '%d' event '%d'\n",
gwidget->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 *klass;
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);
klass = widget->adaptor;
/* As a slight optimization, we never unref the class
*/
oclass = g_type_class_ref (klass->type);
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->klass;
/* Ignore properties based on some criteria
*/
if (!glade_property_get_enabled (glade_property) ||
pclass == NULL || /* Unaccounted for in the builder */
pclass->virt || /* should not be set before
GladeWidget wrapper exists */
pclass->ignore) /* 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 (pclass->def),
pspec[i]->value_type) == FALSE)
{
g_critical ("Type mismatch on %s property of %s",
parameter.name, klass->name);
continue;
}
if (g_param_values_cmp (pspec[i],
glade_property->value,
pclass->orig_def) == 0)
continue;
parameter.name = pspec[i]->name; /* These are not copied/freed */
g_value_init (¶meter.value, pspec[i]->value_type);
g_value_copy (glade_property->value, ¶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 (GladeWidgetAdaptor *adaptor, GladeWidget *widget, GladeWidgetInfo *info)
{
GParameter *params;
GObject *object;
guint n_params, i;
if (widget)
params = glade_widget_template_params (widget, TRUE, &n_params);
else if (info)
params = glade_widget_info_params (adaptor, info, TRUE, &n_params);
else
params = glade_widget_adaptor_default_params (adaptor, TRUE, &n_params);
/* Create the new object with the correct parameters.
*/
object = g_object_newv (adaptor->type, n_params, params);
free_params (params, n_params);
if (widget)
params = glade_widget_template_params (widget, FALSE, &n_params);
else if (info)
params = glade_widget_info_params (adaptor, info, FALSE, &n_params);
else
params = glade_widget_adaptor_default_params (adaptor, FALSE, &n_params);
for (i = 0; i < n_params; i++)
{
g_object_set_property (object, params[i].name, &(params[i].value));
}
free_params (params, n_params);
return object;
}
/**
* glade_widget_dup_properties:
* @template_props: the #GladeProperty list to copy
* @as_load: whether to behave as if loading the project
*
* 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 (GList *template_props, gboolean as_load)
{
GList *list, *properties = NULL;
for (list = template_props; list && list->data; list = list->next)
{
GladeProperty *prop = list->data;
if (prop->klass->save == FALSE && as_load)
continue;
properties = g_list_prepend (properties, glade_property_dup (prop, NULL));
}
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);
if ((prop = glade_widget_get_property (widget, id_property)) != NULL)
{
widget->properties = g_list_remove (widget->properties, prop);
g_object_unref (prop);
}
else
g_critical ("Couldnt find property %s on widget %s\n",
id_property, widget->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 = prop->klass;
if (glade_property_equals_value (prop, klass->orig_def) &&
g_param_values_cmp (klass->pspec, klass->orig_def, klass->def))
glade_property_reset (prop);
}
}
static void
glade_widget_sync_custom_props (GladeWidget *widget)
{
GList *l;
for (l = widget->properties; l && l->data; l = l->next)
{
GladeProperty *prop = GLADE_PROPERTY(l->data);
/* XXX We need a better option to this hack.
*
* This used to be based on whether a function was
* provided by the backend to treat the said property, now
* that function is classwide so we dont know, so currently
* we are just syncing all properties for the sake of those
* properties.
*/
if (!prop->klass->construct_only)
glade_property_sync (prop);
}
}
static void
glade_widget_sync_packing_props (GladeWidget *widget)
{
GList *l;
for (l = widget->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, *object;
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->name == NULL)
{
if (gwidget->internal)
{
gchar *name_base = g_strdup_printf ("%s-%s",
gwidget->construct_internal,
gwidget->internal);
if (gwidget->project)
{
gwidget->name =
glade_project_new_widget_name (gwidget->project,
name_base);
g_free (name_base);
}
else
gwidget->name = name_base;
}
else if (gwidget->project)
gwidget->name = glade_project_new_widget_name
(GLADE_PROJECT (gwidget->project),
gwidget->adaptor->generic_name);
else
gwidget->name =
g_strdup (gwidget->adaptor->generic_name);
}
if (gwidget->construct_template)
{
properties = glade_widget_dup_properties
(gwidget->construct_template->properties, FALSE);
glade_widget_set_properties (gwidget, properties);
}
if (gwidget->object == NULL)
{
object = glade_widget_build_object(gwidget->adaptor,
gwidget->construct_template,
gwidget->construct_info);
glade_widget_set_object (gwidget, object);
}
/* Setup width/height */
gwidget->width = GWA_DEFAULT_WIDTH (gwidget->adaptor);
gwidget->height = GWA_DEFAULT_HEIGHT (gwidget->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->construct_reason == GLADE_CREATE_USER)
for (list = gwidget->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! */
if (gwidget->construct_reason == GLADE_CREATE_USER)
glade_widget_set_catalog_defaults (gwidget->properties);
/* Only call this once the GladeWidget is completely built
* (but before calling custom handlers...)
*/
glade_widget_adaptor_post_create (gwidget->adaptor,
gwidget->object,
gwidget->construct_reason);
/* Virtual properties need to be explicitly synchronized.
*/
if (gwidget->construct_reason == GLADE_CREATE_USER)
glade_widget_sync_custom_props (gwidget);
if (gwidget->parent && gwidget->packing_properties == NULL)
glade_widget_set_packing_properties (gwidget, gwidget->parent);
if (GTK_IS_WIDGET (gwidget->object) && !GTK_WIDGET_TOPLEVEL (gwidget->object))
{
gwidget->visible = TRUE;
gtk_widget_show_all (GTK_WIDGET (gwidget->object));
}
else if (GTK_IS_WIDGET (gwidget->object) == FALSE)
gwidget->visible = TRUE;
return ret_obj;
}
static void
glade_widget_finalize (GObject *object)
{
GladeWidget *widget = GLADE_WIDGET (object);
g_return_if_fail (GLADE_IS_WIDGET (object));
g_free (widget->name);
g_free (widget->internal);
g_hash_table_destroy (widget->signals);
G_OBJECT_CLASS(glade_widget_parent_class)->finalize(object);
}
static void
glade_widget_dispose (GObject *object)
{
GladeWidget *widget = GLADE_WIDGET (object);
g_return_if_fail (GLADE_IS_WIDGET (object));
/* We do not keep a reference to internal widgets */
if (widget->internal == NULL)
{
if (GTK_IS_OBJECT (widget->object))
gtk_object_destroy (GTK_OBJECT (widget->object));
else
g_object_unref (widget->object);
}
if (widget->properties)
{
g_list_foreach (widget->properties, (GFunc)g_object_unref, NULL);
g_list_free (widget->properties);
}
if (widget->packing_properties)
{
g_list_foreach (widget->packing_properties, (GFunc)g_object_unref, NULL);
g_list_free (widget->packing_properties);
}
if (widget->actions)
{
g_list_foreach (widget->actions, (GFunc)g_object_unref, NULL);
g_list_free (widget->actions);
}
if (widget->packing_actions)
{
g_list_foreach (widget->packing_actions, (GFunc)g_object_unref, NULL);
g_list_free (widget->packing_actions);
}
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->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->construct_internal = g_value_dup_string (value);
break;
case PROP_TEMPLATE:
widget->construct_template = g_value_get_object (value);
break;
case PROP_INFO:
widget->construct_info = g_value_get_pointer (value);
break;
case PROP_REASON:
widget->construct_reason = g_value_get_int (value);
break;
case PROP_TOPLEVEL_WIDTH:
widget->width = g_value_get_int (value);
break;
case PROP_TOPLEVEL_HEIGHT:
widget->height = g_value_get_int (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->name);
break;
case PROP_INTERNAL:
g_value_set_string (value, widget->internal);
break;
case PROP_ANARCHIST:
g_value_set_boolean (value, widget->anarchist);
break;
case PROP_ADAPTOR:
g_value_set_object (value, widget->adaptor);
break;
case PROP_PROJECT:
g_value_set_object (value, G_OBJECT (widget->project));
break;
case PROP_OBJECT:
g_value_set_object (value, widget->object);
break;
case PROP_PROPERTIES:
g_value_set_pointer (value, widget->properties);
break;
case PROP_PARENT:
g_value_set_object (value, widget->parent);
break;
case PROP_TOPLEVEL_WIDTH:
g_value_set_int (value, widget->width);
break;
case PROP_TOPLEVEL_HEIGHT:
g_value_set_int (value, widget->height);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
free_signals (gpointer value)
{
GPtrArray *signals = (GPtrArray*) value;
guint i;
guint nb_signals;
if (signals == NULL)
return;
/* g_ptr_array_foreach (signals, (GFunc) glade_signal_free, NULL);
* only available in modern versions of Gtk+ */
nb_signals = signals->len;
for (i = 0; i < nb_signals; i++)
glade_signal_free (g_ptr_array_index (signals, i));
g_ptr_array_free (signals, TRUE);
}
static void
glade_widget_init (GladeWidget *widget)
{
widget->adaptor = NULL;
widget->project = NULL;
widget->name = NULL;
widget->internal = NULL;
widget->object = NULL;
widget->properties = NULL;
widget->packing_properties = NULL;
widget->prop_refs = NULL;
widget->prop_refs_readonly = FALSE;
widget->signals = g_hash_table_new_full
(g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) free_signals);
/* Initial invalid values */
widget->width = -1;
widget->height = -1;
}
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->add_signal_handler = glade_widget_add_signal_handler_impl;
klass->remove_signal_handler = glade_widget_remove_signal_handler_impl;
klass->change_signal_handler = glade_widget_change_signal_handler_impl;
klass->button_press_event = glade_widget_button_press_event_impl;
klass->button_release_event = NULL;
klass->motion_notify_event = NULL;
g_object_class_install_property
(object_class, PROP_NAME,
g_param_spec_string ("name", _("Name"),
_("The name of the widget"),
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property
(object_class, PROP_INTERNAL,
g_param_spec_string ("internal", _("Internal name"),
_("The internal name of the widget"),
NULL, G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property
(object_class, 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));
g_object_class_install_property
(object_class, PROP_OBJECT,
g_param_spec_object ("object", _("Object"),
_("The object associated"),
G_TYPE_OBJECT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property
(object_class, 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));
g_object_class_install_property
(object_class, 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));
g_object_class_install_property
(object_class, PROP_PROPERTIES,
g_param_spec_pointer ("properties", _("Properties"),
_("A list of GladeProperties"),
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_PARENT,
g_param_spec_object ("parent", _("Parent"),
_("A pointer to the parenting GladeWidget"),
GLADE_TYPE_WIDGET,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property
(object_class, 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));
g_object_class_install_property
(object_class, 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));
g_object_class_install_property
(object_class, PROP_INFO,
g_param_spec_pointer ("info", _("Info"),
_("A GladeWidgetInfo struct to base a new widget on"),
G_PARAM_CONSTRUCT_ONLY|G_PARAM_WRITABLE));
g_object_class_install_property
(object_class, 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_WRITABLE));
g_object_class_install_property
(object_class, 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));
g_object_class_install_property
(object_class, 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));
/**
* 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__POINTER,
G_TYPE_NONE,
1,
G_TYPE_POINTER);
/**
* 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__POINTER,
G_TYPE_NONE,
1,
G_TYPE_POINTER);
/**
* 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__POINTER_POINTER,
G_TYPE_NONE,
2,
G_TYPE_POINTER, G_TYPE_POINTER);
/**
* 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);
}
/*******************************************************************************
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->parent == parent);
glade_widget_set_packing_properties (child, parent);
for (l = child->packing_properties; l && l->data; l = l->next)
{
dup_prop = GLADE_PROPERTY(l->data);
orig_prop =
glade_widget_get_property (template_widget, dup_prop->klass->id);
glade_property_set_value (dup_prop, orig_prop->value);
}
}
static void
glade_widget_set_default_packing_properties (GladeWidget *container,
GladeWidget *child)
{
GladePropertyClass *property_class;
GList *l;
for (l = container->adaptor->packing_props; l; l = l->next)
{
const gchar *def;
GValue *value;
property_class = l->data;
if ((def =
glade_widget_adaptor_get_packing_default
(child->adaptor, container->adaptor, property_class->id)) == NULL)
continue;
value = glade_property_class_make_gvalue_from_string (property_class,
def,
child->project);
glade_widget_child_set_property (container, child,
property_class->id, 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 *parent,
const gchar *internal)
{
while (parent)
{
GladeWidgetAdaptorClass *adaptor_class =
GLADE_WIDGET_ADAPTOR_GET_CLASS (parent->adaptor);
if (adaptor_class->get_internal_child)
return glade_widget_adaptor_get_internal_child
(parent->adaptor, parent->object, internal);
parent = glade_widget_get_parent (parent);
}
return NULL;
}
static GladeGetInternalFunc
glade_widget_get_internal_func (GladeWidget *parent, GladeWidget **parent_ret)
{
GladeWidget *gwidget;
g_return_val_if_fail (GLADE_IS_WIDGET (parent), NULL);
for (gwidget = parent; gwidget; gwidget = gwidget->parent)
{
GladeWidgetAdaptorClass *adaptor_class =
GLADE_WIDGET_ADAPTOR_GET_CLASS (gwidget->adaptor);
if (adaptor_class->get_internal_child)
{
if (parent_ret) *parent_ret = gwidget;
return adaptor_class->get_internal_child;
}
}
g_error ("No internal child search function "
"provided for widget class %s (or any parents)",
parent->adaptor->name);
return NULL;
}
static GladeWidget *
glade_widget_dup_internal (GladeWidget *parent,
GladeWidget *template_widget,
gboolean exact)
{
GladeGetInternalFunc get_internal;
GladeWidget *gwidget = NULL, *internal_parent;
GList *list, *children;
GtkWidget *placeholder;
gchar *child_type;
g_return_val_if_fail (template_widget != NULL && 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 (template_widget->internal)
{
GObject *internal_object = NULL;
if (parent &&
(get_internal =
glade_widget_get_internal_func (parent, &internal_parent)) != NULL)
{
/* We cant use "parent" here, we have to recurse up the hierarchy to find
* the "parent" that has `get_internal_child' support (i.e. internal children
* may have depth).
*/
internal_object = get_internal (internal_parent->adaptor,
internal_parent->object,
template_widget->internal);
g_assert (internal_object);
gwidget = glade_widget_get_from_gobject (internal_object);
g_assert (gwidget);
}
}
else
{
gchar *name;
if (exact)
name = g_strdup (template_widget->name);
else
name = glade_project_new_widget_name (template_widget->project,
template_widget->name);
gwidget = glade_widget_adaptor_create_widget
(template_widget->adaptor, FALSE,
"name", name,
"parent", parent,
"project", template_widget->project,
"template", template_widget,
"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->adaptor,
template_widget->object)) != NULL)
{
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->adaptor,
gwidget->object,
G_OBJECT (placeholder));
}
}
else
{
/* Recurse through every GladeWidget (internal or not) */
child_dup = glade_widget_dup_internal (gwidget, child_gwidget, exact);
if (child_gwidget->internal == NULL)
{
g_object_set_data_full (child_dup->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->adaptor,
gwidget->object,
child_dup->object))
glade_widget_copy_packing_props (gwidget,
child_dup,
child_gwidget);
}
}
g_list_free (children);
}
if (gwidget->internal)
glade_widget_copy_properties (gwidget, template_widget);
if (gwidget->packing_properties == NULL)
gwidget->packing_properties = glade_widget_dup_properties (template_widget->packing_properties , FALSE);
/* If custom properties are still at thier
* default value, they need to be synced.
*/
glade_widget_sync_custom_props (gwidget);
/* Special case GtkWindow here and ensure the pasted window
* has the same size as the 'Cut' one.
*/
if (GTK_IS_WINDOW (gwidget->object))
{
gint width, height;
g_assert (GTK_IS_WINDOW (template_widget->object));
gtk_window_get_size (GTK_WINDOW (template_widget->object),
&width, &height);
gtk_window_resize (GTK_WINDOW (gwidget->object),
width, height);
}
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->adaptor, gwidget->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 0
g_print ("Extracting %s from %s\n",
gchild ? gchild->name :
GLADE_IS_PLACEHOLDER (child) ? "placeholder" : "unknown widget",
gwidget->name);
#endif
if (gchild && gchild->internal)
{
/* Recurse and collect any deep child hierarchies
* inside composite widgets.
*/
extract = g_new0 (GladeChildExtract, 1);
extract->internal_name = g_strdup (gchild->internal);
extract->internal_list = glade_widget_extract_children (gchild);
extract->properties =
glade_widget_dup_properties (gchild->properties, TRUE);
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->packing_properties, TRUE);
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->adaptor,
gwidget->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)
{
GladeGetInternalFunc get_internal;
GladeWidget *internal_parent;
/* Recurse and add deep widget hierarchies to internal
* widgets.
*/
get_internal = glade_widget_get_internal_func
(gwidget, &internal_parent);
internal_object = get_internal (internal_parent->adaptor,
internal_parent->object,
extract->internal_name);
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;
GladeProperty *widget_prop =
glade_widget_get_property (gchild,
saved_prop->klass->id);
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;
GladeProperty *widget_prop =
glade_widget_get_pack_property (extract->widget,
saved_prop->klass->id);
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->adaptor,
gwidget->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->properties)
{
g_list_foreach (widget->properties, (GFunc)g_object_unref, NULL);
g_list_free (widget->properties);
}
widget->properties = properties;
for (list = properties; list; list = list->next)
{
property = list->data;
property->widget = widget;
}
}
}
static void
glade_widget_set_actions (GladeWidget *widget, GladeWidgetAdaptor *adaptor)
{
GList *l;
for (l = adaptor->actions; l; l = g_list_next (l))
{
GWActionClass *action = l->data;
GObject *obj = g_object_new (GLADE_TYPE_WIDGET_ACTION,
"class", action, NULL);
widget->actions = g_list_prepend (widget->actions,
GLADE_WIDGET_ACTION (obj));
}
widget->actions = g_list_reverse (widget->actions);
}
static void
glade_widget_set_adaptor (GladeWidget *widget, GladeWidgetAdaptor *adaptor)
{
GladePropertyClass *property_class;
GladeProperty *property;
GList *list;
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->adaptor == NULL);
widget->adaptor = adaptor;
/* If we have no properties; we are not in the process of loading
*/
if (!widget->properties)
{
for (list = adaptor->properties; 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",
property_class->id);
continue;
}
widget->properties = g_list_prepend (widget->properties, property);
}
widget->properties = g_list_reverse (widget->properties);
}
/* Create actions from adaptor */
glade_widget_set_actions (widget, adaptor);
}
static void
expose_draw_selection (GtkWidget *widget_gtk,
GdkEventExpose *event,
GladeWidget *gwidget)
{
glade_util_draw_selection_nodes (event->window);
}
/* Connects a signal handler to the 'event' signal for a widget and
all its children recursively. We need this to draw the selection
rectangles and to get button press/release events reliably. */
static void
glade_widget_connect_signal_handlers (GtkWidget *widget_gtk,
GCallback callback,
GladeWidget *gwidget)
{
GList *children, *list;
/* Check if we've already connected an event handler. */
if (!g_object_get_data (G_OBJECT (widget_gtk),
GLADE_TAG_EVENT_HANDLER_CONNECTED))
{
/* Make sure we can recieve the kind of events we're
* connecting for
*/
gtk_widget_add_events (widget_gtk,
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);
g_signal_connect (G_OBJECT (widget_gtk), "event",
callback, gwidget);
g_signal_connect_after (G_OBJECT (widget_gtk), "expose-event",
G_CALLBACK (expose_draw_selection), gwidget);
g_object_set_data (G_OBJECT (widget_gtk),
GLADE_TAG_EVENT_HANDLER_CONNECTED,
GINT_TO_POINTER (1));
}
/* We also need to get expose events for any children.
*/
if (GTK_IS_CONTAINER (widget_gtk))
{
if ((children =
glade_util_container_get_all_children (GTK_CONTAINER
(widget_gtk))) != NULL)
{
for (list = children; list; list = list->next)
glade_widget_connect_signal_handlers
(GTK_WIDGET (list->data), callback, gwidget);
g_list_free (children);
}
}
}
/*
* 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;
GList *list, *packing_props = NULL;
/* XXX TODO: by checking with some GladePropertyClass metadata, decide
* which packing properties go on which type of children.
*/
for (list = container->adaptor->packing_props;
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);
}
/*******************************************************************************
GladeInterface Parsing code
*******************************************************************************/
static gint
glade_widget_set_child_type_from_child_info (GladeChildInfo *child_info,
GladeWidgetAdaptor *parent_adaptor,
GObject *child)
{
guint i;
GladePropInfo *prop_info;
gchar *special_child_type;
g_object_get (parent_adaptor, "special-child-type", &special_child_type, NULL);
if (!special_child_type)
return -1;
for (i = 0; i < child_info->n_properties; ++i)
{
prop_info = child_info->properties + i;
if (!strcmp (prop_info->name, special_child_type))
{
g_free (special_child_type);
g_object_set_data_full (child,
"special-child-type",
g_strdup (prop_info->value),
g_free);
return i;
}
}
g_free (special_child_type);
return -1;
}
static gboolean
glade_widget_new_child_from_child_info (GladeChildInfo *info,
GladeProject *project,
GladeWidget *parent)
{
GladeWidget *child;
GList *list;
g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (project != NULL, FALSE);
/* is it a placeholder? */
if (!info->child)
{
GObject *palaceholder = G_OBJECT (glade_placeholder_new ());
glade_widget_set_child_type_from_child_info
(info, parent->adaptor, palaceholder);
glade_widget_adaptor_add (parent->adaptor,
parent->object,
palaceholder);
return TRUE;
}
/* is it an internal child? */
if (info->internal_child)
{
GObject *child_object =
glade_widget_get_internal_child (parent, info->internal_child);
if (!child_object)
{
g_warning ("Failed to locate internal child %s of %s",
info->internal_child, glade_widget_get_name (parent));
return FALSE;
}
if ((child = glade_widget_get_from_gobject (child_object)) == NULL)
g_error ("Unable to get GladeWidget for internal child %s\n",
info->internal_child);
/* Apply internal widget name from here */
glade_widget_set_name (child, info->child->name);
glade_widget_fill_from_widget_info (info->child, child, TRUE);
glade_widget_sync_custom_props (child);
}
else
{
child = glade_widget_new_from_widget_info (info->child, project, parent);
if (!child)
return FALSE;
child->parent = parent;
glade_widget_set_child_type_from_child_info
(info, parent->adaptor, child->object);
glade_widget_add_child (parent, child, FALSE);
glade_widget_sync_packing_props (child);
}
/* Get the packing properties */
for (list = child->packing_properties; list; list = list->next)
{
GladeProperty *property = list->data;
glade_property_read (property, property->klass,
loading_project, info, TRUE);
}
return TRUE;
}
static void
glade_widget_fill_from_widget_info (GladeWidgetInfo *info,
GladeWidget *widget,
gboolean apply_props)
{
GladeProperty *property;
GList *list;
guint i;
g_return_if_fail (GLADE_IS_WIDGET (widget));
g_return_if_fail (info != NULL);
g_assert (strcmp (info->classname, widget->adaptor->name) == 0);
/* Children */
for (i = 0; i < info->n_children; ++i)
{
if (!glade_widget_new_child_from_child_info (info->children + i,
widget->project, widget))
{
g_warning ("Failed to read child of %s",
glade_widget_get_name (widget));
continue;
}
}
/* Signals */
for (i = 0; i < info->n_signals; ++i)
{
GladeSignal *signal;
signal = glade_signal_new_from_signal_info (info->signals + i);
if (!signal)
{
g_warning ("Failed to read signal");
continue;
}
glade_widget_add_signal_handler (widget, signal);
}
/* Properties */
if (apply_props)
{
for (list = widget->properties; list; list = list->next)
{
property = list->data;
glade_property_read (property, property->klass,
loading_project, info, TRUE);
}
}
}
static GList *
glade_widget_properties_from_widget_info (GladeWidgetAdaptor *klass,
GladeWidgetInfo *info)
{
GList *properties = NULL, *list;
for (list = klass->properties; list && list->data; list = list->next)
{
GladePropertyClass *pclass = list->data;
GladeProperty *property;
/* If there is a value in the XML, initialize property with it,
* otherwise initialize property to default.
*/
property = glade_property_new (pclass, NULL, NULL);
glade_property_original_reset (property);
glade_property_read (property, property->klass,
loading_project, info, TRUE);
properties = g_list_prepend (properties, property);
}
return g_list_reverse (properties);
}
static GladeWidget *
glade_widget_new_from_widget_info (GladeWidgetInfo *info,
GladeProject *project,
GladeWidget *parent)
{
GladeWidgetAdaptor *adaptor;
GladeWidget *widget;
GList *properties;
g_return_val_if_fail (info != NULL, NULL);
g_return_val_if_fail (project != NULL, NULL);
if ((adaptor =
glade_widget_adaptor_get_by_name (info->classname)) == NULL)
{
g_warning ("Widget class %s unknown.", info->classname);
return NULL;
}
properties = glade_widget_properties_from_widget_info (adaptor, info);
widget = glade_widget_adaptor_create_widget
(adaptor, FALSE,
"name", info->name,
"parent", parent,
"project", project,
"info", info,
"properties", properties,
"reason", GLADE_CREATE_LOAD, NULL);
/* create the packing_properties list, without setting them */
if (parent)
widget->packing_properties =
glade_widget_create_packing_properties (parent, widget);
/* Load children first */
glade_widget_fill_from_widget_info (info, widget, FALSE);
/* Now sync custom props, things like "size" on GtkBox need
* this to be done afterwards.
*/
glade_widget_sync_custom_props (widget);
return widget;
}
static GParameter *
glade_widget_info_params (GladeWidgetAdaptor *adaptor,
GladeWidgetInfo *info,
gboolean construct,
guint *n_params)
{
GladePropertyClass *glade_property_class;
GObjectClass *oclass;
GParamSpec **pspec;
GArray *params;
guint i, n_props;
oclass = g_type_class_ref (adaptor->type);
pspec = g_object_class_list_properties (oclass, &n_props);
params = g_array_new (FALSE, FALSE, sizeof (GParameter));
/* prepare parameters that have glade_property_class->def */
for (i = 0; i < n_props; i++)
{
GParameter parameter = { 0, };
GValue *value;
glade_property_class =
glade_widget_adaptor_get_property_class (adaptor,
pspec[i]->name);
if (glade_property_class == NULL ||
glade_property_class->virt ||
glade_property_class->ignore)
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;
/* Try filling parameter with value from widget info.
*/
if ((value = glade_property_read (NULL, glade_property_class,
loading_project, info, FALSE)) != NULL)
{
parameter.name = pspec[i]->name;
g_value_init (¶meter.value, pspec[i]->value_type);
g_value_copy (value, ¶meter.value);
g_value_unset (value);
g_free (value);
g_array_append_val (params, parameter);
}
}
g_free(pspec);
g_type_class_unref (oclass);
*n_params = params->len;
return (GParameter *)g_array_free (params, FALSE);
}
/*******************************************************************************
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);
}
static void
glade_widget_debug_real (GladeWidget *widget, int indent)
{
g_print ("%*sGladeWidget at %p\n", indent, "", widget);
g_print ("%*sname = [%s]\n", indent, "", widget->name ? widget->name : "-");
g_print ("%*sinternal = [%s]\n", indent, "", widget->internal ? widget->internal : "-");
g_print ("%*sgobject = %p [%s]\n",
indent, "", widget->object, G_OBJECT_TYPE_NAME (widget->object));
if (GTK_IS_WIDGET (widget->object))
g_print ("%*sgtkwidget->parent = %p\n", indent, "",
gtk_widget_get_parent (GTK_WIDGET(widget->object)));
if (GTK_IS_CONTAINER (widget->object)) {
GList *children, *l;
children = glade_util_container_get_all_children
(GTK_CONTAINER (widget->object));
for (l = children; l; l = l->next) {
GtkWidget *widget_gtk = GTK_WIDGET (l->data);
GladeWidget *widget = glade_widget_get_from_gobject (widget_gtk);
if (widget) {
glade_widget_debug_real (widget, indent + 2);
} else if (GLADE_IS_PLACEHOLDER (widget_gtk)) {
g_print ("%*sGtkWidget child %p is a placeholder.\n",
indent + 2, "", widget_gtk);
} else {
g_print ("%*sGtkWidget child %p [%s] has no glade widget.\n",
indent + 2, "",
widget_gtk, G_OBJECT_TYPE_NAME (widget_gtk));
}
}
if (!children)
g_print ("%*shas no children\n", indent, "");
g_list_free (children);
} else {
g_print ("%*snot a container\n", indent, "");
}
g_print ("\n");
}
/**
* glade_widget_debug:
* @widget: a #GladeWidget
*
* Prints some information about a #GladeWidget, currently
* this is unmaintained.
*/
static void
glade_widget_debug (GladeWidget *widget)
{
glade_widget_debug_real (widget, 0);
}
/**
* glade_widget_show:
* @widget: A #GladeWidget
*
* Display @widget in it's project's GladeDesignView
*/
void
glade_widget_show (GladeWidget *widget)
{
GladeDesignView *view;
GtkWidget *layout;
g_return_if_fail (GLADE_IS_WIDGET (widget));
/* Position window at saved coordinates or in the center */
if (GTK_IS_WINDOW (widget->object))
{
if (!glade_window_is_embedded (GTK_WINDOW (widget->object)))
glade_widget_embed (widget);
view = glade_design_view_get_from_project (glade_widget_get_project (widget));
layout = GTK_WIDGET (glade_design_view_get_layout (view));
if (gtk_bin_get_child (GTK_BIN (layout)) != NULL)
gtk_container_remove (GTK_CONTAINER (layout), gtk_bin_get_child (GTK_BIN (layout)));
gtk_container_add (GTK_CONTAINER (layout), GTK_WIDGET (widget->object));
gtk_widget_show_all (GTK_WIDGET (widget->object));
} else if (GTK_IS_WIDGET (widget->object)) {
gtk_widget_show_all (GTK_WIDGET (widget->object));
}
widget->visible = TRUE;
}
/**
* glade_widget_hide:
* @widget: A #GladeWidget
*
* Hide @widget
*/
void
glade_widget_hide (GladeWidget *widget)
{
g_return_if_fail (GLADE_IS_WIDGET (widget));
if (GTK_IS_WINDOW (widget->object))
{
/* Save coordinates */
gtk_widget_hide (GTK_WIDGET (widget->object));
}
widget->visible = 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 object reference properties that
* go in and out of the project.
*/
void
glade_widget_add_prop_ref (GladeWidget *widget, GladeProperty *property)
{
g_return_if_fail (GLADE_IS_WIDGET (widget));
g_return_if_fail (GLADE_IS_PROPERTY (property));
if (property && !widget->prop_refs_readonly &&
!g_list_find (widget->prop_refs, property))
widget->prop_refs = g_list_prepend (widget->prop_refs, property);
}
/**
* 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 object reference properties that
* go in and out of the project.
*/
void
glade_widget_remove_prop_ref (GladeWidget *widget, GladeProperty *property)
{
g_return_if_fail (GLADE_IS_WIDGET (widget));
g_return_if_fail (GLADE_IS_PROPERTY (property));
if (!widget->prop_refs_readonly)
widget->prop_refs = g_list_remove (widget->prop_refs, property);
}
/**
* glade_widget_project_notify:
* @widget: A #GladeWidget
* @project: The #GladeProject (or %NULL)
*
* Notifies @widget that it is now in @project.
*
* Note that this doesnt really set the project; the project is saved
* for internal reasons even when the widget is on the clipboard.
* (also used for property references).
*/
void
glade_widget_project_notify (GladeWidget *widget, GladeProject *project)
{
GList *l;
GladeProperty *property;
g_return_if_fail (GLADE_IS_WIDGET (widget));
/* Since glade_property_set() will try to modify list,
* we protect it with the 'prop_refs_readonly' flag.
*/
widget->prop_refs_readonly = TRUE;
for (l = widget->prop_refs; l && l->data; l = l->next)
{
property = GLADE_PROPERTY (l->data);
if (project != NULL &&
project == property->widget->project)
glade_property_add_object (property, widget->object);
else
glade_property_remove_object (property, widget->object);
}
widget->prop_refs_readonly = FALSE;
}
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->signals,
(GHFunc)glade_widget_copy_signal_foreach,
widget);
}
/**
* glade_widget_copy_properties:
* @widget: a 'dest' #GladeWidget
* @template_widget: a 'src' #GladeWidget
*
* Sets properties in @widget based on the values of
* matching properties in @template_widget
*/
void
glade_widget_copy_properties (GladeWidget *widget,
GladeWidget *template_widget)
{
GList *l;
g_return_if_fail (GLADE_IS_WIDGET (widget));
g_return_if_fail (GLADE_IS_WIDGET (template_widget));
for (l = widget->properties; l && l->data; l = l->next)
{
GladeProperty *widget_prop = GLADE_PROPERTY(l->data);
GladeProperty *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_prop =
glade_widget_get_property (template_widget,
widget_prop->klass->id)) != NULL &&
glade_property_class_match (template_prop->klass, widget_prop->klass))
glade_property_set_value (widget_prop, template_prop->value);
}
}
/**
* 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 (NULL, template_widget, exact);
glade_widget_pop_superuser ();
return widget;
}
/**
* 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;
GList *children;
gboolean reselect = FALSE, inproject;
g_return_if_fail (GLADE_IS_WIDGET (gwidget));
adaptor = gwidget->adaptor;
/* Here we take care removing the widget from the project and
* the selection before rebuilding the instance.
*/
inproject = gwidget->project ?
(glade_project_has_object
(gwidget->project, gwidget->object) ? TRUE : FALSE) : FALSE;
if (inproject)
{
if (glade_project_is_selected (gwidget->project,
gwidget->object))
{
reselect = TRUE;
glade_project_selection_remove
(gwidget->project, gwidget->object, FALSE);
}
glade_project_remove_object (gwidget->project, gwidget->object);
}
/* Extract and keep the child hierarchies aside... */
children = glade_widget_extract_children (gwidget);
/* Hold a reference to the old widget while we transport properties
* and children from it
*/
new_object = glade_widget_build_object(adaptor, gwidget, NULL);
old_object = g_object_ref(glade_widget_get_object (gwidget));
glade_widget_set_object (gwidget, new_object);
/* Only call this once the object has a proper GladeWidget */
glade_widget_adaptor_post_create (adaptor, new_object, GLADE_CREATE_REBUILD);
/* Replace old object with new object in parent
*/
if (gwidget->parent)
glade_widget_replace (gwidget->parent,
old_object, new_object);
/* Reparent any children of the old object to the new object
* (this function will consume and free the child list).
*/
glade_widget_push_superuser ();
glade_widget_insert_children (gwidget, children);
glade_widget_pop_superuser ();
/* Custom properties aren't transfered in build_object, since build_object
* is only concerned with object creation.
*/
glade_widget_sync_custom_props (gwidget);
/* Sync packing.
*/
glade_widget_sync_packing_props (gwidget);
if (g_type_is_a (adaptor->type, GTK_TYPE_WIDGET))
{
/* Must use gtk_widget_destroy here for cases like dialogs and toplevels
* (otherwise I'd prefer g_object_unref() )
*/
gtk_widget_destroy (GTK_WIDGET(old_object));
}
else
g_object_unref (old_object);
/* If the widget was in a project (and maybe the selection), then
* restore that stuff.
*/
if (inproject)
{
glade_project_add_object (gwidget->project, NULL,
gwidget->object);
if (reselect)
glade_project_selection_add
(gwidget->project, gwidget->object, TRUE);
}
/* We shouldnt show if its not already visible */
if (gwidget->visible)
glade_widget_show (gwidget);
}
/**
* 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, GladeSignal *signal_handler)
{
g_return_if_fail (GLADE_IS_WIDGET (widget));
g_signal_emit (widget, glade_widget_signals[ADD_SIGNAL_HANDLER], 0, signal_handler);
}
/**
* 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, GladeSignal *signal_handler)
{
g_return_if_fail (GLADE_IS_WIDGET (widget));
g_signal_emit (widget, glade_widget_signals[REMOVE_SIGNAL_HANDLER], 0, signal_handler);
}
/**
* 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,
GladeSignal *old_signal_handler,
GladeSignal *new_signal_handler)
{
g_return_if_fail (GLADE_IS_WIDGET (widget));
g_signal_emit (widget, glade_widget_signals[CHANGE_SIGNAL_HANDLER], 0,
old_signal_handler, new_signal_handler);
}
/**
* 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->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->name != name) {
if (widget->name)
g_free (widget->name);
widget->name = g_strdup (name);
g_object_notify (G_OBJECT (widget), "name");
}
}
/**
* glade_widget_get_name:
* @widget: a #GladeWidget
*
* Returns: a pointer to @widget's name
*/
const gchar *
glade_widget_get_name (GladeWidget *widget)
{
g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
return widget->name;
}
/**
* 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->internal != internal) {
g_free (widget->internal);
widget->internal = g_strdup (internal);
g_object_notify (G_OBJECT (widget), "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->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->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->project != project)
{
widget->project = project;
g_object_notify (G_OBJECT (widget), "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->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)
{
static gchar id_buffer[GPC_PROPERTY_NAMELEN] = { 0, };
GList *list;
GladeProperty *property;
g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
g_return_val_if_fail (id_property != NULL, NULL);
/* "-1" to always leave a trailing '\0' charachter */
strncpy (id_buffer, id_property, GPC_PROPERTY_NAMELEN - 1);
glade_util_replace (id_buffer, '_', '-');
for (list = widget->properties; list; list = list->next) {
property = list->data;
if (strcmp (property->klass->id, id_buffer) == 0)
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)
{
static gchar id_buffer[GPC_PROPERTY_NAMELEN] = { 0, };
GList *list;
GladeProperty *property;
g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
g_return_val_if_fail (id_property != NULL, NULL);
/* "-1" to always leave a trailing '\0' charachter */
strncpy (id_buffer, id_property, GPC_PROPERTY_NAMELEN - 1);
glade_util_replace (id_buffer, '_', '-');
for (list = widget->packing_properties; list; list = list->next) {
property = list->data;
if (strcmp (property->klass->id, id_buffer) == 0)
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;
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)
ret_string =
glade_property_class_make_string_from_gvalue (property->klass,
value ? value : property->value);
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;
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)
ret_string =
glade_property_class_make_string_from_gvalue (property->klass,
value ? value : property->value);
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->adaptor,
widget->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->adaptor,
widget->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)
{
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_set_property (widget->adaptor,
widget->object,
child->object,
property_name, value);
}
/**
* 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->adaptor,
widget->object,
child->object,
property_name, value);
}
static gboolean
glade_widget_event_private (GtkWidget *widget,
GdkEvent *event,
GladeWidget *gwidget)
{
GtkWidget *layout = widget;
/* Find the parenting layout container */
while (layout && !GLADE_IS_DESIGN_LAYOUT (layout))
layout = layout->parent;
/* Event outside the logical heirarchy, could be a menuitem
* or other such popup window, we'll presume to send it directly
* to the GladeWidget that connected here.
*/
if (!layout)
return glade_widget_event (gwidget, event);
/* Let the parenting GladeDesignLayout decide which GladeWidget to
* marshall this event to.
*/
if (GLADE_IS_DESIGN_LAYOUT (layout))
return glade_design_layout_widget_event (GLADE_DESIGN_LAYOUT (layout),
gwidget, event);
else
return FALSE;
}
/**
* glade_widget_set_object:
* @gwidget: A #GladeWidget
* @new_object: the new #GObject for @gwidget
*
* Set the runtime object for this GladeWidget wrapper
* (this is used deep in the core and is probably unsafe
* to use elsewhere).
*/
void
glade_widget_set_object (GladeWidget *gwidget, GObject *new_object)
{
GladeWidgetAdaptor *adaptor;
GObject *old_object;
g_return_if_fail (GLADE_IS_WIDGET (gwidget));
g_return_if_fail (G_IS_OBJECT (new_object));
g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (new_object),
gwidget->adaptor->type));
adaptor = gwidget->adaptor;
old_object = gwidget->object;
/* Add internal reference to new widget if its not internal */
if (gwidget->internal)
gwidget->object = G_OBJECT(new_object);
else
gwidget->object = g_object_ref (G_OBJECT(new_object));
g_object_set_qdata (G_OBJECT (new_object), glade_widget_name_quark, gwidget);
if (g_type_is_a (gwidget->adaptor->type, GTK_TYPE_WIDGET))
{
/* Disable any built-in DnD
*/
gtk_drag_dest_unset (GTK_WIDGET (new_object));
gtk_drag_source_unset (GTK_WIDGET (new_object));
/* Take care of drawing selection directly on widgets
* for the time being
*/
glade_widget_connect_signal_handlers
(GTK_WIDGET(new_object),
G_CALLBACK (glade_widget_event_private),
gwidget);
}
/* Remove internal reference to old widget */
if (gwidget->internal == NULL && old_object) {
g_object_set_qdata (G_OBJECT (old_object), glade_widget_name_quark, NULL);
g_object_unref (G_OBJECT (old_object));
}
g_object_notify (G_OBJECT (gwidget), "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->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->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->parent;
widget->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->object && parent != NULL &&
glade_widget_adaptor_has_child
(parent->adaptor, parent->object, widget->object))
{
if (old_parent == NULL || widget->packing_properties == NULL ||
old_parent->adaptor->type != parent->adaptor->type)
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 (G_OBJECT (widget), "parent");
}
/**
* 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));
g_list_foreach (widget->packing_properties, (GFunc)g_object_unref, NULL);
g_list_free (widget->packing_properties);
widget->packing_properties = 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->anarchist) return;
widget->packing_properties = glade_widget_create_packing_properties (container, widget);
/* Dont introspect on properties that are not parented yet.
*/
if (glade_widget_adaptor_has_child (container->adaptor,
container->object,
widget->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->packing_properties;
list && list->data;
list = list->next)
{
GladeProperty *property = list->data;
g_value_reset (property->value);
glade_widget_child_get_property
(container,
widget, property->klass->id, property->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 (G_TYPE_IS_INTERFACE (type) &&
glade_util_class_implements_interface
(widget->adaptor->type, type))
return TRUE;
else if (G_TYPE_IS_INTERFACE (type) == FALSE &&
g_type_is_a (widget->adaptor->type, type))
return TRUE;
if ((children = glade_widget_adaptor_get_children
(widget->adaptor, widget->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 Serialization */
static gboolean
glade_widget_write_child (GArray *children, GladeWidget *parent, GObject *object, GladeInterface *interface);
typedef struct _WriteSignalsContext
{
GladeInterface *interface;
GArray *signals;
} WriteSignalsContext;
static void
glade_widget_write_signals (gpointer key, gpointer value, gpointer user_data)
{
WriteSignalsContext *write_signals_context;
GPtrArray *signals;
guint i;
write_signals_context = (WriteSignalsContext *) user_data;
signals = (GPtrArray *) value;
for (i = 0; i < signals->len; i++)
{
GladeSignal *signal = g_ptr_array_index (signals, i);
GladeSignalInfo signalinfo;
glade_signal_write (&signalinfo, signal,
write_signals_context->interface);
g_array_append_val (write_signals_context->signals,
signalinfo);
}
}
/**
* glade_widget_write:
* @widget: a #GladeWidget
* @interface: a #GladeInterface
*
* TODO: write me
*
* Returns:
*/
GladeWidgetInfo*
glade_widget_write (GladeWidget *widget, GladeInterface *interface)
{
WriteSignalsContext write_signals_context;
GladeWidgetInfo *info;
GArray *props, *atk_props, *atk_actions, *atk_relations, *accels, *children;
GList *list;
g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
info = g_new0 (GladeWidgetInfo, 1);
info->classname = glade_xml_alloc_string (interface, widget->adaptor->name);
info->name = glade_xml_alloc_string (interface, widget->name);
/* Write the properties */
props = g_array_new (FALSE, FALSE, sizeof (GladePropInfo));
atk_props = g_array_new (FALSE, FALSE, sizeof (GladePropInfo));
atk_relations = g_array_new (FALSE, FALSE, sizeof (GladeAtkRelationInfo));
atk_actions = g_array_new (FALSE, FALSE, sizeof (GladeAtkActionInfo));
accels = g_array_new (FALSE, FALSE, sizeof (GladeAccelInfo));
for (list = widget->properties; list; list = list->next)
{
GladeProperty *property = list->data;
/* This should never happen */
if (property->klass->packing)
continue;
switch (property->klass->type)
{
case GPC_NORMAL:
glade_property_write (property, interface, props);
break;
case GPC_ATK_PROPERTY:
glade_property_write (property, interface, atk_props);
break;
case GPC_ATK_RELATION:
glade_property_write (property, interface, atk_relations);
break;
case GPC_ATK_ACTION:
glade_property_write (property, interface, atk_actions);
break;
case GPC_ACCEL_PROPERTY:
glade_property_write (property, interface, accels);
break;
default:
break;
}
}
/* Properties */
info->properties = (GladePropInfo *) props->data;
info->n_properties = props->len;
g_array_free(props, FALSE);
/* Atk Properties */
info->atk_props = (GladePropInfo *) atk_props->data;
info->n_atk_props = atk_props->len;
g_array_free(atk_props, FALSE);
/* Atk Relations */
info->relations = (GladeAtkRelationInfo *) atk_relations->data;
info->n_relations = atk_relations->len;
g_array_free(atk_relations, FALSE);
/* Atk Actions */
info->atk_actions = (GladeAtkActionInfo *) atk_actions->data;
info->n_atk_actions = atk_actions->len;
g_array_free(atk_actions, FALSE);
/* Accels */
info->accels = (GladeAccelInfo *) accels->data;
info->n_accels = accels->len;
g_array_free(accels, FALSE);
/* Signals */
write_signals_context.interface = interface;
write_signals_context.signals = g_array_new (FALSE, FALSE,
sizeof (GladeSignalInfo));
g_hash_table_foreach (widget->signals,
glade_widget_write_signals,
&write_signals_context);
info->signals = (GladeSignalInfo *)
write_signals_context.signals->data;
info->n_signals = write_signals_context.signals->len;
g_array_free (write_signals_context.signals, FALSE);
/* Children */
if ((list =
glade_widget_adaptor_get_children (widget->adaptor,
widget->object)) != NULL)
{
children = g_array_new (FALSE, FALSE, sizeof (GladeChildInfo));
while (list && list->data)
{
GObject *child = list->data;
glade_widget_write_child (children, widget, child, interface);
list = list->next;
}
info->children = (GladeChildInfo *) children->data;
info->n_children = children->len;
g_array_free (children, FALSE);
g_list_free (list);
}
g_hash_table_insert(interface->names, info->name, info);
return info;
}
static gboolean
glade_widget_write_special_child_prop (GArray *props,
GladeWidget *parent,
GObject *object,
GladeInterface *interface)
{
GladePropInfo info = { 0 };
gchar *buff, *special_child_type;
buff = g_object_get_data (object, "special-child-type");
g_object_get (parent->adaptor, "special-child-type", &special_child_type, NULL);
if (special_child_type && buff)
{
info.name = glade_xml_alloc_propname (interface,
special_child_type);
info.value = glade_xml_alloc_string (interface, buff);
g_array_append_val (props, info);
g_free (special_child_type);
return TRUE;
}
g_free (special_child_type);
return FALSE;
}
gboolean
glade_widget_write_child (GArray *children,
GladeWidget *parent,
GObject *object,
GladeInterface *interface)
{
GladeChildInfo info = { 0 };
GladeWidget *child_widget;
GList *list;
GArray *props;
if (GLADE_IS_PLACEHOLDER (object))
{
props = g_array_new (FALSE, FALSE,
sizeof (GladePropInfo));
/* Here we have to add the "special-child-type" packing property */
glade_widget_write_special_child_prop (props, parent,
object, interface);
info.properties = (GladePropInfo *) props->data;
info.n_properties = props->len;
g_array_free(props, FALSE);
g_array_append_val (children, info);
return TRUE;
}
child_widget = glade_widget_get_from_gobject (object);
if (!child_widget)
return FALSE;
if (child_widget->internal)
info.internal_child = glade_xml_alloc_string(interface, child_widget->internal);
info.child = glade_widget_write (child_widget, interface);
if (!info.child)
{
g_warning ("Failed to write child widget");
return FALSE;
}
/* Append the packing properties */
props = g_array_new (FALSE, FALSE, sizeof (GladePropInfo));
/* Here we have to add the "special-child-type" packing property */
glade_widget_write_special_child_prop (props, parent,
child_widget->object,
interface);
if (child_widget->packing_properties != NULL)
{
for (list = child_widget->packing_properties;
list; list = list->next)
{
GladeProperty *property;
property = list->data;
g_assert (property->klass->packing != FALSE);
glade_property_write (property, interface, props);
}
}
info.properties = (GladePropInfo *) props->data;
info.n_properties = props->len;
g_array_free(props, FALSE);
g_array_append_val (children, info);
return TRUE;
}
/**
* glade_widget_read:
* @project: a #GladeProject
* @info: a #GladeWidgetInfo
*
* Returns: a new #GladeWidget for @project, based on @info
*/
GladeWidget *
glade_widget_read (GladeProject *project, GladeWidgetInfo *info)
{
GladeWidget *widget;
glade_widget_push_superuser ();
loading_project = project;
if ((widget = glade_widget_new_from_widget_info
(info, project, NULL)) != NULL)
{
#if 0
if (glade_verbose)
glade_widget_debug (widget);
#endif
}
loading_project = NULL;
glade_widget_pop_superuser ();
return widget;
}
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->object) &&
GTK_IS_WIDGET (widget->object) &&
GWA_USE_PLACEHOLDERS (parent->adaptor));
}
static GladeWidgetAction *
glade_widget_action_lookup (GList **actions, const gchar *path, gboolean remove)
{
GList *l;
for (l = *actions; l; l = g_list_next (l))
{
GladeWidgetAction *action = l->data;
if (strcmp (action->klass->path, path) == 0)
{
if (remove)
{
*actions = g_list_remove (*actions, action);
g_object_unref (action);
return NULL;
}
return action;
}
if (action->actions &&
g_str_has_prefix (path, action->klass->path) &&
(action = glade_widget_action_lookup (&action->actions, path, remove)))
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->actions, action_path, FALSE);
}
/**
* 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->packing_actions, action_path, FALSE);
}
/**
* 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_remove_action:
* @widget: a #GladeWidget
* @action_path: a full action path including groups
*
* Remove an action.
*/
void
glade_widget_remove_action (GladeWidget *widget, const gchar *action_path)
{
g_return_if_fail (GLADE_IS_WIDGET (widget));
g_return_if_fail (action_path != NULL);
glade_widget_action_lookup (&widget->actions, action_path, TRUE);
}
/**
* glade_widget_remove_pack_action:
* @widget: a #GladeWidget
* @action_path: a full action path including groups
*
* Remove a packing action.
*/
void
glade_widget_remove_pack_action (GladeWidget *widget, const gchar *action_path)
{
g_return_if_fail (GLADE_IS_WIDGET (widget));
g_return_if_fail (action_path != NULL);
glade_widget_action_lookup (&widget->packing_actions, action_path, TRUE);
}
/**
* glade_widget_create_action_menu:
* @widget: a #GladeWidget
* @action_path: an action path or NULL to include every @widget action.
*
* Create a new GtkMenu with every action in it.
*
*/
GtkWidget *
glade_widget_create_action_menu (GladeWidget *widget, const gchar *action_path)
{
GladeWidgetAction *action = NULL;
GtkWidget *menu;
g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
if (action_path)
{
action = glade_widget_action_lookup (&widget->actions, action_path, FALSE);
if (action == NULL)
action = glade_widget_action_lookup (&widget->packing_actions, action_path, FALSE);
}
menu = gtk_menu_new ();
if (glade_popup_action_populate_menu (menu, widget, action, TRUE))
return menu;
g_object_unref (G_OBJECT (menu));
return NULL;
}
/*******************************************************************************
* Toplevel GladeWidget Embedding *
******************************************************************************
*
* Overrides realize() and size_allocate() by signal connection on GtkWindows.
*
* This is high crack code and should be replaced by a more robust implementation
* in GTK+ proper.
*
*/
static GQuark
embedded_window_get_quark ()
{
static GQuark embedded_window_quark = 0;
if (embedded_window_quark == 0)
embedded_window_quark = g_quark_from_string ("GladeEmbedWindow");
return embedded_window_quark;
}
static gboolean
glade_window_is_embedded (GtkWindow *window)
{
return GPOINTER_TO_INT (g_object_get_qdata ((GObject *) window, embedded_window_get_quark ()));
}
static void
embedded_window_realize_handler (GtkWidget *widget)
{
GtkWindow *window;
GdkWindowAttr attributes;
gint attributes_mask;
window = GTK_WINDOW (widget);
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
attributes.event_mask = gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK |
GDK_FOCUS_CHANGE_MASK |
GDK_KEY_PRESS_MASK |
GDK_KEY_RELEASE_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK |
GDK_STRUCTURE_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
/* destroy the previously created window */
if (GDK_IS_WINDOW (widget->window))
{
gdk_window_hide (widget->window);
}
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
gdk_window_enable_synchronized_configure (widget->window);
gdk_window_set_user_data (widget->window, window);
widget->style = gtk_style_attach (widget->style, widget->window);
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
}
static void
embedded_window_size_allocate_handler (GtkWidget *widget)
{
if (GTK_WIDGET_REALIZED (widget))
{
gdk_window_move_resize (widget->window,
widget->allocation.x,
widget->allocation.y,
widget->allocation.width,
widget->allocation.height);
}
}
/**
* glade_widget_embed:
* @window: a #GtkWindow
*
* Embeds a window by signal connection method
*/
static void
glade_widget_embed (GladeWidget *widget)
{
GtkWindow *window;
g_return_if_fail (GLADE_IS_WIDGET (widget));
g_return_if_fail (GTK_IS_WINDOW (widget->object));
window = GTK_WINDOW (widget->object);
if (glade_window_is_embedded (window) || GTK_WIDGET_REALIZED (GTK_WIDGET (window))) {
g_critical ("Cannot embed a window that is already realized or embedded");
return;
}
GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (window), GTK_TOPLEVEL);
gtk_container_set_resize_mode (GTK_CONTAINER (window), GTK_RESIZE_PARENT);
g_signal_connect (G_OBJECT (window), "realize",
G_CALLBACK (embedded_window_realize_handler), NULL);
g_signal_connect (G_OBJECT (window), "size-allocate",
G_CALLBACK (embedded_window_size_allocate_handler), NULL);
/* mark window as embedded */
g_object_set_qdata (G_OBJECT (window),
embedded_window_get_quark (), GINT_TO_POINTER (TRUE));
}
syntax highlighted by Code2HTML, v. 0.9.1