/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Copyright (C) 2001 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:
 *   Tristan Van Berkom <tvb@gnome.org>
 */

#include <config.h>

/**
 * SECTION:glade-widget-adaptor
 * @Short_Description: Adaptor base class to add runtime support for each widget class.
 * 
 * TODO: Add long description.
 */

#include "glade.h"
#include "glade-widget-adaptor.h"
#include "glade-xml-utils.h"
#include "glade-property-class.h"
#include "glade-signal.h"
#include "glade-marshallers.h"
#include "glade-accumulators.h"

/* For g_file_exists */
#include <sys/types.h>
#include <string.h>

#include <glib/gdir.h>
#include <glib/gi18n-lib.h>
#include <gmodule.h>
#include <ctype.h>

#define DEFAULT_ICON_NAME "widget-gtk-frame"

#define GLADE_WIDGET_ADAPTOR_GET_PRIVATE(o)  \
        (G_TYPE_INSTANCE_GET_PRIVATE ((o), GLADE_TYPE_WIDGET_ADAPTOR, GladeWidgetAdaptorPrivate))

struct _GladeWidgetAdaptorPrivate {

	gchar       *catalog;      /* The name of the widget catalog this class
				    * was declared by.
				    */

	gchar       *book;         /* DevHelp search namespace for this widget class
				    */

	GdkCursor   *cursor;       /* a cursor for inserting widgets */

	gchar       *special_child_type; /* Special case code for children that
					  * are special children (like notebook tab 
					  * widgets for example).
					  */
};

struct _GladeChildPacking {
	gchar *parent_name;
	GList *packing_defaults;
};

enum {
	PROP_0,
	PROP_NAME,
	PROP_TYPE,
	PROP_TITLE,
	PROP_GENERIC_NAME,
	PROP_ICON_NAME,
	PROP_CATALOG,
	PROP_BOOK,
	PROP_SPECIAL_TYPE,
	PROP_CURSOR
};

typedef struct _GladeChildPacking GladeChildPacking;

static GObjectClass *parent_class = NULL;
static GHashTable   *adaptor_hash = NULL;

/*******************************************************************************
                              Helper functions
 *******************************************************************************/
static GladeWidgetAdaptor *
gwa_get_parent_adaptor (GladeWidgetAdaptor *adaptor)
{
	GladeWidgetAdaptor *parent_adaptor = NULL;
	GType               iter_type;

	for (iter_type = g_type_parent (adaptor->type);
	     iter_type > 0;
	     iter_type = g_type_parent (iter_type))
	{
		if ((parent_adaptor = 
		     glade_widget_adaptor_get_by_type (iter_type)) != NULL)
			return parent_adaptor;
	}

	return NULL;
}

/*
  This function assignes "weight" to each property in its natural order staring from 1.
  If parent is 0 weight will be set for every GladePropertyClass in the list.
  This function will not override weight if it is already set (weight >= 0.0)
*/
static void
gwa_properties_set_weight (GList **properties, GType parent)
{
	gint normal = 0, common = 0, packing = 0;
	GList *l;

	for (l = *properties; l && l->data; l = g_list_next (l))
	{
		GladePropertyClass *klass = l->data;
		GPCType type = klass->type;
	
		if (klass->visible &&
		    (parent) ? parent == klass->pspec->owner_type : TRUE &&
	    	    (type == GPC_NORMAL || type == GPC_ACCEL_PROPERTY))
		{
			/* Use a different counter for each tab (common, packing and normal) */
			if (klass->common) common++;
			else if (klass->packing) packing++;
			else normal++;

			/* Skip if it is already set */
			if (klass->weight >= 0.0) continue;
			
			/* Special-casing weight of properties for seperate tabs */
			if (klass->common) klass->weight = common;
			else if (klass->packing) klass->weight = packing;
			else klass->weight = normal;
		}
	}
}

static void
gwa_create_cursor (GladeWidgetAdaptor *adaptor)
{
	GdkPixbuf *tmp_pixbuf, *widget_pixbuf;
	const GdkPixbuf *add_pixbuf;
	GdkDisplay *display;
	GError *error = NULL;

	/* only certain widget classes need to have cursors */
	if (G_TYPE_IS_INSTANTIATABLE (adaptor->type) == FALSE ||
            G_TYPE_IS_ABSTRACT (adaptor->type) != FALSE ||
            adaptor->generic_name == NULL)
		return;

	/* cannot continue if 'add widget' cursor pixbuf has not been loaded for any reason */
	if ((add_pixbuf = glade_cursor_get_add_widget_pixbuf ()) == NULL)
		return;

	display = gdk_display_get_default ();

	/* create a temporary pixbuf clear to transparent black*/
	tmp_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
	gdk_pixbuf_fill (tmp_pixbuf, 0x00000000);

	if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), adaptor->icon_name))
	{
		widget_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
							  adaptor->icon_name,
							  22,
							  0,
							  &error);
		
		if (error) {
			g_warning ("Could not load image data for named icon '%s': %s", 
				   adaptor->icon_name,
				   error->message);
			g_error_free (error);
			return;
		}
	
	} else {
		return;
	}

	/* composite pixbufs */
	gdk_pixbuf_composite (widget_pixbuf, tmp_pixbuf,
			      8, 8, 22, 22,
			      8, 8, 1, 1,
                              GDK_INTERP_NEAREST, 255);

	gdk_pixbuf_composite (add_pixbuf, tmp_pixbuf,
			      0, 0, 12, 12,
			      0, 0, 1, 1,
                              GDK_INTERP_NEAREST, 255);


	adaptor->priv->cursor = gdk_cursor_new_from_pixbuf (display, tmp_pixbuf, 6, 6);

	g_object_unref (tmp_pixbuf);
	g_object_unref (widget_pixbuf);
}



static gboolean
gwa_gtype_equal (gconstpointer v1,
		 gconstpointer v2)
{
  return *((const GType*) v1) == *((const GType*) v2);
}

static guint
gwa_gtype_hash (gconstpointer v)
{
  return *(const GType*) v;
}

static gboolean
glade_widget_adaptor_hash_find (gpointer key, gpointer value, gpointer user_data)
{
	GladeWidgetAdaptor *adaptor = value;
	GType *type = user_data;
	
	if (g_type_is_a (adaptor->type, *type))
	{
		*type = adaptor->type;
		return TRUE;
	}
		
	return FALSE;
}

static void
glade_abort_if_derived_adaptors_exist (GType type)
{
	if (adaptor_hash)
	{
		GType retval = type;
	
		g_hash_table_find (adaptor_hash,
				   glade_widget_adaptor_hash_find,
				   &retval);
		if (retval != type)
			g_error (_("A derived adaptor (%s) of %s already exist!"),
				 g_type_name (retval), g_type_name (type));
	}
}

/*******************************************************************************
                     Base Object Implementation detail
 *******************************************************************************/
static gint
gwa_signal_comp (gconstpointer a, gconstpointer b)
{
	const GladeSignalClass *signal_a = a, *signal_b = b;	
	return strcmp (signal_b->query.signal_name, signal_a->query.signal_name);
}

static void
gwa_add_signals (GList **signals, GType type)
{
	guint count, *sig_ids, num_signals;
	GladeSignalClass *cur;
	GList *list = NULL;
	
	if (G_TYPE_IS_INSTANTIATABLE (type) || G_TYPE_IS_INTERFACE (type))
	{
		sig_ids = g_signal_list_ids (type, &num_signals);

		for (count = 0; count < num_signals; count++)
		{
			cur = g_new0 (GladeSignalClass, 1);
			
			g_signal_query (sig_ids[count], &(cur->query));

			/* Since glib gave us this signal id... it should
			 * exist no matter what.
			 */
			g_assert (cur->query.signal_id != 0);

			cur->name = (cur->query.signal_name);
			cur->type = (gchar *) g_type_name (type);

			list = g_list_prepend (list, cur);
		}
		g_free (sig_ids);
		
		list = g_list_sort (list, gwa_signal_comp);
		*signals = g_list_concat (list, *signals);
	}
}

static GList * 
gwa_list_signals (GladeWidgetAdaptor *adaptor) 
{
	GList *signals = NULL;
	GType type, parent, *i, *p;

	g_return_val_if_fail (adaptor->type != 0, NULL);

	for (type = adaptor->type; g_type_is_a (type, G_TYPE_OBJECT); type = parent)
	{
		parent = g_type_parent (type);
		
		/* Add class signals */
		gwa_add_signals (&signals, type);
	
		/* Add class interfaces signals */
		for (i = p = g_type_interfaces (type, NULL); *i; i++)
			if (!glade_util_class_implements_interface (parent, *i))
				gwa_add_signals (&signals, *i);

		g_free (p);
	}

	return g_list_reverse (signals);
}

static GList * 
gwa_clone_parent_properties (GladeWidgetAdaptor *adaptor, gboolean is_packing) 
{
	GladeWidgetAdaptor *parent_adaptor;
	GList              *properties = NULL, *list, *proplist;
	
	if ((parent_adaptor = gwa_get_parent_adaptor (adaptor)) != NULL)
	{
		proplist = is_packing ? 
			parent_adaptor->packing_props : 
			parent_adaptor->properties;

		for (list = proplist; list; list = list->next)
		{
			GladePropertyClass *pclass =
				glade_property_class_clone (list->data);
			pclass->handle = adaptor;
			properties = g_list_prepend (properties, pclass);
		}
	}

	return g_list_reverse (properties);
}

static void
gwa_setup_introspected_props_from_pspecs (GladeWidgetAdaptor   *adaptor,
					  GParamSpec          **specs,
					  gint                  n_specs,
					  gboolean              is_packing)
{
	GladeWidgetAdaptor *parent_adaptor = gwa_get_parent_adaptor (adaptor);
	GladePropertyClass *property_class;
	GType               class_type;
	gint                i;
	GList              *list = NULL;

	for (i = 0; i < n_specs; i++)
	{
		gboolean found;

		/* Only create properties that dont exist on the adaptor yet */
		for (found = FALSE, class_type = adaptor->type;
		     ((!parent_adaptor && class_type != 0) ||
		      ( parent_adaptor && class_type != parent_adaptor->type));
		     class_type = g_type_parent (class_type))
			if (specs[i]->owner_type == class_type ||
			    (G_TYPE_IS_INTERFACE (specs[i]->owner_type) &&
			    glade_util_class_implements_interface (class_type, specs[i]->owner_type)))
			{
				found = TRUE;
				break;
			}

		if (found && 
		    (property_class = 
		     glade_property_class_new_from_spec (adaptor, specs[i])) != NULL)
			list = g_list_prepend (list, property_class);
	}

	if (is_packing)
		adaptor->packing_props =
			g_list_concat (adaptor->packing_props, 
				       g_list_reverse (list));
	else
		adaptor->properties =
			g_list_concat (adaptor->properties, 
				       g_list_reverse (list));
}

/* XXX Atk relations and accel props disregarded - they should
 *     be implemented from the gtk+ catalog instead I think.
 */
static void
gwa_setup_properties (GladeWidgetAdaptor *adaptor,
		      GObjectClass       *object_class,
		      gboolean            is_packing) 
{
	GParamSpec   **specs = NULL;
	guint          n_specs = 0;
	GList         *l;

	/* only GtkContainer child propeties can be introspected */
	if (is_packing && !g_type_is_a (adaptor->type, GTK_TYPE_CONTAINER))
		return;

	/* First clone the parents properties */
	if (is_packing)
		adaptor->packing_props = gwa_clone_parent_properties (adaptor, is_packing);
	else
		adaptor->properties = gwa_clone_parent_properties (adaptor, is_packing);

	/* Now automaticly introspect new properties added in this class,
	 * allow the class writer to decide what to override in the resulting
	 * list of properties.
	 */
	if (is_packing)
		specs = gtk_container_class_list_child_properties (object_class, &n_specs);
	else
		specs = g_object_class_list_properties (object_class, &n_specs);
	gwa_setup_introspected_props_from_pspecs (adaptor, specs, n_specs, is_packing);
	g_free (specs);

	if (is_packing)
	{
		/* We have to mark packing properties from GladeWidgetAdaptor
		 * because GladePropertyClass doesnt have a valid parent GType
		 * to introspect it.
		 *
		 * (which could be used to call gtk_container_class_find_child_property()
		 * and properly introspect whether or not its a packing property).
		 */
		for (l = adaptor->packing_props; l; l = l->next)
		{
			GladePropertyClass *property_class = l->data;
			property_class->packing = TRUE;
		}
	}
}

static GList *
gwa_inherit_child_packing (GladeWidgetAdaptor *adaptor)
{
	GladeWidgetAdaptor *parent_adaptor;
	GList              *child_packings = NULL, *packing_list, *default_list;

	if ((parent_adaptor = gwa_get_parent_adaptor (adaptor)) != NULL)
	{
		for (packing_list = parent_adaptor->child_packings;
		     packing_list; packing_list = packing_list->next)
		{
			GladeChildPacking *packing     = packing_list->data;
			GladeChildPacking *packing_dup = g_new0 (GladeChildPacking, 1);

			packing_dup->parent_name = g_strdup (packing->parent_name);

			for (default_list = packing->packing_defaults;
			     default_list; default_list = default_list->next)
			{
				GladePackingDefault *def     = default_list->data;
				GladePackingDefault *def_dup = g_new0 (GladePackingDefault, 1);

				def_dup->id    = g_strdup (def->id);
				def_dup->value = g_strdup (def->value);

				packing_dup->packing_defaults = 
					g_list_prepend (packing_dup->packing_defaults, def_dup);
			}

			child_packings = g_list_prepend (child_packings, packing_dup);
		}
	}
	return child_packings;
}

static GObject *
glade_widget_adaptor_constructor (GType                  type,
				  guint                  n_construct_properties,
				  GObjectConstructParam *construct_properties)
{
	GladeWidgetAdaptor   *adaptor, *parent_adaptor;
	GObject              *ret_obj;
	GObjectClass         *object_class;

	glade_abort_if_derived_adaptors_exist (type);
	
	ret_obj = G_OBJECT_CLASS (parent_class)->constructor
		(type, n_construct_properties, construct_properties);

	adaptor = GLADE_WIDGET_ADAPTOR (ret_obj);
	parent_adaptor = gwa_get_parent_adaptor (adaptor);
	
	if (adaptor->type == G_TYPE_NONE)
		g_warning ("Adaptor created without a type");
	if (adaptor->name == NULL)
		g_warning ("Adaptor created without a name");

	/* Build decorations */
	if (!adaptor->icon_name) {		
		adaptor->icon_name = g_strdup ("gtk-missing-image");
	}
	gwa_create_cursor (adaptor);

	/* Let it leek */
	if ((object_class = g_type_class_ref (adaptor->type)))
	{
		/* Build signals & properties */
		adaptor->signals = gwa_list_signals (adaptor);
		gwa_setup_properties (adaptor, object_class, FALSE);
		gwa_setup_properties (adaptor, object_class, TRUE);
	}
	else
		g_critical ("Failed to get class for type %s\n", 
			    g_type_name (adaptor->type));

	/* Inherit packing defaults here */
	adaptor->child_packings = gwa_inherit_child_packing (adaptor);

	/* Inherit special-child-type */
	if (parent_adaptor)
		adaptor->priv->special_child_type =
			parent_adaptor->priv->special_child_type ?
			g_strdup (parent_adaptor->priv->special_child_type) : NULL;
	
	/* Copy parent actions */
	if (parent_adaptor)
	{
		GList *l;
		
		if (parent_adaptor->actions)
		{
			for (l = parent_adaptor->actions; l; l = g_list_next (l))
			{
				GWActionClass *child = glade_widget_action_class_clone (l->data);
				adaptor->actions = g_list_prepend (adaptor->actions, child);
			}
			adaptor->actions = g_list_reverse (adaptor->actions);
		}
		
		if (parent_adaptor->packing_actions)
		{
			for (l = parent_adaptor->packing_actions; l; l = g_list_next (l))
			{
				GWActionClass *child = glade_widget_action_class_clone (l->data);
				adaptor->packing_actions = g_list_prepend (adaptor->packing_actions, child);
			}
			adaptor->packing_actions = g_list_reverse (adaptor->packing_actions);
		}
	}
	
	return ret_obj;
}

static void
gwa_packing_default_free (GladePackingDefault *def)
{
	g_free (def->id);
	g_free (def->value);
}

static void
gwa_child_packing_free (GladeChildPacking *packing)
{
        g_free (packing->parent_name);

        g_list_foreach (packing->packing_defaults,
                        (GFunc) gwa_packing_default_free, NULL);
        g_list_free (packing->packing_defaults);
}

static void
glade_widget_adaptor_finalize (GObject *object)
{
	GladeWidgetAdaptor *adaptor = GLADE_WIDGET_ADAPTOR (object);

	/* Free properties and signals */
	g_list_foreach (adaptor->properties, (GFunc) glade_property_class_free, NULL);
	g_list_free (adaptor->properties);

	g_list_foreach (adaptor->packing_props, (GFunc) glade_property_class_free, NULL);
	g_list_free (adaptor->packing_props);

	g_list_foreach (adaptor->signals, (GFunc) glade_signal_free, NULL);
	g_list_free (adaptor->signals);


	/* Free child packings */
	g_list_foreach (adaptor->child_packings,
			(GFunc) gwa_child_packing_free,
			NULL);
	g_list_free (adaptor->child_packings);

	if (adaptor->priv->book)       g_free (adaptor->priv->book);
	if (adaptor->priv->catalog)    g_free (adaptor->priv->catalog);
	if (adaptor->priv->special_child_type)
		g_free (adaptor->priv->special_child_type);

	if (adaptor->priv->cursor != NULL)
		gdk_cursor_unref (adaptor->priv->cursor);

	if (adaptor->name)         g_free (adaptor->name);
	if (adaptor->generic_name) g_free (adaptor->generic_name);
	if (adaptor->title)        g_free (adaptor->title);
	if (adaptor->icon_name)    g_free (adaptor->icon_name);

	if (adaptor->actions)
	{
		g_list_foreach (adaptor->actions,
				(GFunc) glade_widget_action_class_free,
				NULL);
		g_list_free (adaptor->actions);
	}
	
	if (adaptor->packing_actions)
	{
		g_list_foreach (adaptor->packing_actions,
				(GFunc) glade_widget_action_class_free,
				NULL);
		g_list_free (adaptor->packing_actions);
	}
	
	g_free (adaptor->priv);

	G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
glade_widget_adaptor_real_set_property (GObject         *object,
					guint            prop_id,
					const GValue    *value,
					GParamSpec      *pspec)
{
	GladeWidgetAdaptor *adaptor;

	adaptor = GLADE_WIDGET_ADAPTOR (object);

	switch (prop_id)
	{
	case PROP_NAME:
		/* assume once (construct-only) */
		adaptor->name = g_value_dup_string (value);
		break;
	case PROP_ICON_NAME:
		/* assume once (construct-only) */
		adaptor->icon_name = g_value_dup_string (value);
		break;
	case PROP_TYPE:
		adaptor->type = g_value_get_gtype (value);
		break;
	case PROP_TITLE:
		if (adaptor->title) g_free (adaptor->title);
		adaptor->title = g_value_dup_string (value);
		break;
	case PROP_GENERIC_NAME:
		if (adaptor->generic_name) g_free (adaptor->generic_name);
		adaptor->generic_name = g_value_dup_string (value);
		break;
	case PROP_CATALOG:
		/* assume once (construct-only) */
		adaptor->priv->catalog = g_value_dup_string (value);
		break;
	case PROP_BOOK:
		/* assume once (construct-only) */
		adaptor->priv->book = g_value_dup_string (value);
		break;
	case PROP_SPECIAL_TYPE:
		/* assume once (construct-only) */
		adaptor->priv->special_child_type = g_value_dup_string (value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
glade_widget_adaptor_real_get_property (GObject         *object,
					guint            prop_id,
					GValue          *value,
					GParamSpec      *pspec)
{

	GladeWidgetAdaptor *adaptor;

	adaptor = GLADE_WIDGET_ADAPTOR (object);

	switch (prop_id)
	{
	case PROP_NAME:         g_value_set_string (value, adaptor->name);             break;
	case PROP_TYPE:	        g_value_set_gtype  (value, adaptor->type);             break;
	case PROP_TITLE:        g_value_set_string (value, adaptor->title);            break;
	case PROP_GENERIC_NAME: g_value_set_string (value, adaptor->generic_name);     break;
	case PROP_ICON_NAME:    g_value_set_string (value, adaptor->icon_name);        break;
	case PROP_CATALOG:      g_value_set_string (value, adaptor->priv->catalog);    break;
	case PROP_BOOK:         g_value_set_string (value, adaptor->priv->book);       break;
	case PROP_SPECIAL_TYPE: 
		g_value_set_string (value, adaptor->priv->special_child_type); 
		break;
	case PROP_CURSOR:     g_value_set_pointer (value, adaptor->priv->cursor);     break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

/*******************************************************************************
                  GladeWidgetAdaptor base class implementations
 *******************************************************************************/
static void
glade_widget_adaptor_object_set_property (GladeWidgetAdaptor *adaptor,
					  GObject            *object,
					  const gchar        *property_name,
					  const GValue       *value)
{
	g_object_set_property (object, property_name, value);
}

static void
glade_widget_adaptor_object_get_property (GladeWidgetAdaptor *adaptor,
					  GObject            *object,
					  const gchar        *property_name,
					  GValue             *value)
{
	g_object_get_property (object, property_name, value);
}

static void
glade_widget_adaptor_object_action_activate (GladeWidgetAdaptor *adaptor,
					     GObject *object,
					     const gchar *action_id)
{
	g_message ("No action_activate() support in adaptor %s for action '%s'",
		   adaptor->name, action_id);
}

static void
glade_widget_adaptor_object_child_action_activate (GladeWidgetAdaptor *adaptor,
						   GObject *container,
						   GObject *object,
						   const gchar *action_id)
{
	g_message ("No child_action_activate() support in adaptor %s for action '%s'",
		   adaptor->name, action_id);
}

/*******************************************************************************
            GladeWidgetAdaptor type registration and class initializer
 *******************************************************************************/
static void
glade_widget_adaptor_init (GladeWidgetAdaptor *adaptor)
{
	adaptor->priv = GLADE_WIDGET_ADAPTOR_GET_PRIVATE (adaptor);
}

static void
glade_widget_adaptor_class_init (GladeWidgetAdaptorClass *adaptor_class)
{
	GObjectClass       *object_class;
	g_return_if_fail (adaptor_class != NULL);
	
	parent_class = g_type_class_peek_parent (adaptor_class);
	object_class = G_OBJECT_CLASS (adaptor_class);

	/* GObjectClass */
	object_class->constructor           = glade_widget_adaptor_constructor;
	object_class->finalize              = glade_widget_adaptor_finalize;
	object_class->set_property          = glade_widget_adaptor_real_set_property;
	object_class->get_property          = glade_widget_adaptor_real_get_property;

	/* Class methods */
	adaptor_class->deep_post_create     = NULL;
	adaptor_class->post_create          = NULL;
	adaptor_class->get_internal_child   = NULL;
	adaptor_class->verify_property      = NULL;
	adaptor_class->set_property         = glade_widget_adaptor_object_set_property;
	adaptor_class->get_property         = glade_widget_adaptor_object_get_property;
	adaptor_class->add                  = NULL;
	adaptor_class->remove               = NULL;
	adaptor_class->replace_child        = NULL;
	adaptor_class->get_children         = NULL;
	adaptor_class->child_set_property   = NULL;
	adaptor_class->child_get_property   = NULL;
	adaptor_class->action_activate      = glade_widget_adaptor_object_action_activate;
	adaptor_class->child_action_activate= glade_widget_adaptor_object_child_action_activate;

	/* Base defaults here */
	adaptor_class->fixed                = FALSE;
	adaptor_class->toplevel             = FALSE;
	adaptor_class->use_placeholders     = FALSE;
	adaptor_class->default_width        = -1;
	adaptor_class->default_height       = -1;

	/* Properties */
	g_object_class_install_property 
		(object_class, PROP_NAME,
		 g_param_spec_string 
		 ("name", _("Name"), 
		  _("Name of the class"),
		  NULL, 
		  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property 
		(object_class, PROP_TYPE,
		 g_param_spec_gtype 
		 ("type", _("Type"), 
		  _("GType of the class"),
		  G_TYPE_NONE,
		  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property 
		(object_class, PROP_TITLE,
		 g_param_spec_string 
		 ("title", _("Title"), 
		  _("Translated title for the class used in the glade UI"),
		  NULL, 
		  G_PARAM_READWRITE));

	g_object_class_install_property 
		(object_class, PROP_GENERIC_NAME,
		 g_param_spec_string 
		 ("generic-name", _("Generic Name"), 
		  _("Used to generate names of new widgets"),
		  NULL, 
		  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
		  
	g_object_class_install_property 
		(object_class, PROP_ICON_NAME,
		 g_param_spec_string 
		 ("icon-name", _("Icon Name"), 
		  _("The icon name"),
		  DEFAULT_ICON_NAME, 
		  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property 
		(object_class, PROP_CATALOG,
		 g_param_spec_string 
		 ("catalog", _("Catalog"), 
		  _("The name of the widget catalog this class was declared by"),
		  NULL, 
		  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property 
		(object_class, PROP_BOOK,
		 g_param_spec_string 
		 ("book", _("Book"), 
		  _("DevHelp search namespace for this widget class"),
		  NULL, 
		  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property 
		(object_class, PROP_SPECIAL_TYPE,
		 g_param_spec_string 
		 ("special-child-type", _("Special Child Type"), 
		  _("Holds the name of the packing property to depict "
		    "special children for this container class"),
		  NULL, 
		  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property 
		(object_class, PROP_CURSOR,
		 g_param_spec_pointer 
		 ("cursor", _("Cursor"), 
		  _("A cursor for inserting widgets in the UI"),
		  G_PARAM_READABLE));
	
	g_type_class_add_private (adaptor_class, sizeof (GladeWidgetAdaptorPrivate));
}

GType
glade_widget_adaptor_get_type (void)
{
	static GType adaptor_type = 0;
	
	if (!adaptor_type)
	{
		static const GTypeInfo adaptor_info = 
		{
			sizeof (GladeWidgetAdaptorClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) glade_widget_adaptor_class_init,
			(GClassFinalizeFunc) NULL,
			NULL,           /* class_data */
			sizeof (GladeWidgetAdaptor),
			0,              /* n_preallocs */
			(GInstanceInitFunc) glade_widget_adaptor_init,
		};
		adaptor_type = 
			g_type_register_static (G_TYPE_OBJECT,
						"GladeGObjectAdaptor",
						&adaptor_info, 0);
	}
	return adaptor_type;
}

GType
glade_create_reason_get_type (void)
{
	static GType etype = 0;

	if (etype == 0)
	{
		static const GEnumValue values[] = {
			{ GLADE_CREATE_USER,    "GLADE_CREATE_USER",    "create-user" },
			{ GLADE_CREATE_COPY,    "GLADE_CREATE_COPY",    "create-copy" },
			{ GLADE_CREATE_LOAD,    "GLADE_CREATE_LOAD",    "create-load" },
			{ GLADE_CREATE_REBUILD, "GLADE_CREATE_REBUILD", "create-rebuild" },
			{ 0, NULL, NULL }
		};

		etype = g_enum_register_static ("GladeCreateReason", values);

	}

	return etype;
}

/*******************************************************************************
                        Synthetic Object Derivation
 *******************************************************************************/
typedef struct
{
	GladeXmlNode *node;
	GModule      *module;
} GWADerivedClassData;

static void
gwa_derived_init (GladeWidgetAdaptor *adaptor, gpointer g_class)
{

}

static void
gwa_extend_with_node_load_sym (GladeWidgetAdaptorClass *klass,
			       GladeXmlNode            *node,
			       GModule                 *module)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	gpointer symbol;
	
	/*
	 * We use a temporary variable to avoid a bogus gcc warning.
	 * the thing it that g_module_symbol() should use a function pointer
	 * instead of a gpointer!
	 */
	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_CONSTRUCTOR_FUNCTION,
					  &symbol))
		object_class->constructor = symbol;

	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_DEEP_POST_CREATE_FUNCTION,
					  &symbol))
		klass->deep_post_create = symbol;

	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_POST_CREATE_FUNCTION,
					  &symbol))
		klass->post_create = symbol;
	
	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_GET_INTERNAL_CHILD_FUNCTION,
					  &symbol))
		klass->get_internal_child = symbol;
	
	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_SET_FUNCTION,
					  &symbol))
		klass->set_property = symbol;
	
	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_GET_FUNCTION,
					  &symbol))
		klass->get_property = symbol;

	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_VERIFY_FUNCTION,
					  &symbol))
		klass->verify_property = symbol;
	
	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_ADD_CHILD_FUNCTION,
					  &symbol))
		klass->add = symbol;

	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_REMOVE_CHILD_FUNCTION,
					  &symbol))
		klass->remove = symbol;
	
	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_GET_CHILDREN_FUNCTION,
					  &symbol))
		klass->get_children = symbol;

	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_CHILD_SET_PROP_FUNCTION,
					  &symbol))
		klass->child_set_property = symbol;

	if (glade_xml_load_sym_from_node (node, module,
				      GLADE_TAG_CHILD_GET_PROP_FUNCTION,
				      &symbol))
		klass->child_get_property = symbol;

	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_CHILD_VERIFY_FUNCTION,
					  &symbol))
		klass->child_verify_property = symbol;

	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_REPLACE_CHILD_FUNCTION,
					  &symbol))
		klass->replace_child = symbol;

	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_ACTION_ACTIVATE_FUNCTION,
					  &symbol))
		klass->action_activate = symbol;
	
	if (glade_xml_load_sym_from_node (node, module,
					  GLADE_TAG_CHILD_ACTION_ACTIVATE_FUNCTION,
					  &symbol))
		klass->child_action_activate = symbol;
}

static void
gwa_derived_class_init (GladeWidgetAdaptorClass *adaptor_class,
			GWADerivedClassData *data)
{
	GladeXmlNode *node = data->node;
	GModule      *module = data->module;
	
	/* Load catalog symbols from module */
	if (module) gwa_extend_with_node_load_sym (adaptor_class, node, module);
	
	adaptor_class->fixed = 
		glade_xml_get_property_boolean
		(node, GLADE_TAG_FIXED, adaptor_class->fixed);

	/* Check if this class is toplevel */
	adaptor_class->toplevel =
		glade_xml_get_property_boolean
		(node, GLADE_TAG_TOPLEVEL, adaptor_class->toplevel);

	/* Check if this class uses placeholders for child widgets */
	adaptor_class->use_placeholders =
		glade_xml_get_property_boolean
		(node, GLADE_TAG_USE_PLACEHOLDERS, adaptor_class->use_placeholders);

	/* Check default size when used as a toplevel in the GladeDesignView */
	adaptor_class->default_width =
		glade_xml_get_property_int
		(node, GLADE_TAG_DEFAULT_WIDTH, adaptor_class->default_width);
	adaptor_class->default_height =
		glade_xml_get_property_int
		(node, GLADE_TAG_DEFAULT_HEIGHT, adaptor_class->default_height);
}

static GType
gwa_derive_adaptor_for_type (GType object_type, GWADerivedClassData *data)
{
	GladeWidgetAdaptor *adaptor;
	GType               iter_type, derived_type;
	GType               parent_type = GLADE_TYPE_WIDGET_ADAPTOR;
	gchar              *type_name;
	GTypeInfo adaptor_info = 
	{
		sizeof (GladeWidgetAdaptorClass),
		(GBaseInitFunc) NULL,
		(GBaseFinalizeFunc) NULL,
		(GClassInitFunc) gwa_derived_class_init,
		(GClassFinalizeFunc) NULL,
		data,           /* class_data */
		sizeof (GladeWidgetAdaptor),
		0,              /* n_preallocs */
		(GInstanceInitFunc) gwa_derived_init,
	};
	
	for (iter_type = g_type_parent (object_type); 
	     iter_type > 0; 
	     iter_type = g_type_parent (iter_type))
	{
		if ((adaptor = 
		     glade_widget_adaptor_get_by_type (iter_type)) != NULL)
		{
			parent_type = G_TYPE_FROM_INSTANCE (adaptor);
			break;
		}
	}

	/* Note that we must pass ownership of the type_name string
	 * to the type system 
	 */
	type_name    = g_strdup_printf ("Glade%sAdaptor", g_type_name (object_type));
	derived_type = g_type_register_static (parent_type, type_name,
					       &adaptor_info, 0);
			
	return derived_type;
}


/*******************************************************************************
                                     API
 *******************************************************************************/

/**
 * glade_widget_adaptor_register:
 * @adaptor: A #GladeWidgetAdaptor
 *
 * Registers @adaptor into the Glade core (every supported
 * object type must have a registered adaptor).
 */
void
glade_widget_adaptor_register (GladeWidgetAdaptor *adaptor)
{

	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));

	if (glade_widget_adaptor_get_by_name (adaptor->name))
	{
		g_warning ("Adaptor class for '%s' already registered", 
			   adaptor->name);
		return;
	}

	if (!adaptor_hash)
		adaptor_hash = g_hash_table_new_full (gwa_gtype_hash, gwa_gtype_equal,
						      g_free, g_object_unref);

	g_hash_table_insert (adaptor_hash, 
			     g_memdup (&adaptor->type, 
				       sizeof (GType)), adaptor);
}

static GladePackingDefault *
gwa_default_from_child_packing (GladeChildPacking *packing, const gchar *id)
{
	GList *list;

	for (list = packing->packing_defaults; list; list = list->next)
	{
		GladePackingDefault *def = list->data;

		if (id && !strcmp (id, def->id))
			return def;
	}

	return NULL;
}

static GladeChildPacking *
glade_widget_adaptor_get_child_packing (GladeWidgetAdaptor *child_adaptor,
					const gchar        *parent_name)
{
	GList *l;

	for (l = child_adaptor->child_packings; l; l = l->next) 
	{
		GladeChildPacking *packing;

		packing = (GladeChildPacking *) l->data;

		if (!strcmp (packing->parent_name, parent_name))
			return packing;
	}

	return NULL;
}

static void
gwa_set_packing_defaults_from_node (GladeWidgetAdaptor *adaptor,
				    GladeXmlNode       *node)
{
        GladeXmlNode *child;

        for (child = glade_xml_node_get_children (node); 
	     child; child = glade_xml_node_next (child))
        {
                gchar             *name;
                GladeXmlNode      *prop_node;
		GladeChildPacking *packing;

                if (!glade_xml_node_verify (child, GLADE_TAG_PARENT_CLASS))
                        continue;

                if ((name = glade_xml_get_property_string_required
		     (child, GLADE_TAG_NAME, adaptor->name)) == NULL)
                        continue;

		/* If a GladeChildPacking exists for this parent, use it -
		 * otherwise prepend a new one
		 */
		if ((packing = 
		     glade_widget_adaptor_get_child_packing (adaptor, name)) == NULL)
		{

			packing = g_new0 (GladeChildPacking, 1);
			packing->parent_name = name;

			adaptor->child_packings = 
				g_list_prepend (adaptor->child_packings, packing);

		}

                for (prop_node = glade_xml_node_get_children (child); 
		     prop_node; prop_node = glade_xml_node_next (prop_node))
                {
			GladePackingDefault *def;
			gchar               *id;
			gchar               *value;

			if ((id = 
			     glade_xml_get_property_string_required
			     (prop_node, GLADE_TAG_ID, adaptor->name)) == NULL)
				continue;

			if ((value = 
			     glade_xml_get_property_string_required
			     (prop_node, GLADE_TAG_DEFAULT, adaptor->name)) == NULL)
			{
				g_free (id);
				continue;
			}

			if ((def = gwa_default_from_child_packing (packing, id)) == NULL)
			{
				def        = g_new0 (GladePackingDefault, 1);
				def->id    = id;
				def->value = value;

				packing->packing_defaults = 
					g_list_prepend (packing->packing_defaults, def);
			}
			else
			{
				g_free (id);
				g_free (def->value);
				def->value = value;
			}

			adaptor->child_packings = 
				g_list_prepend (adaptor->child_packings, packing);

                }
	}
}

static void
gwa_update_properties_from_node (GladeWidgetAdaptor  *adaptor,
				 GladeXmlNode        *node,
				 GModule             *module,
				 GList              **properties,
				 const gchar         *domain,
				 gboolean             is_packing)
{
	GladeXmlNode *child;

	for (child = glade_xml_node_get_children (node);
	     child; child = glade_xml_node_next (child))
	{
		gchar *id;
		GList *list;
		GladePropertyClass *property_class;
		gboolean updated;

		if (!glade_xml_node_verify (child, GLADE_TAG_PROPERTY))
			continue;

		id = glade_xml_get_property_string_required
			(child, GLADE_TAG_ID, adaptor->name);
		if (!id)
			continue;

		/* property names from catalogs also need to have the '-' form */
		glade_util_replace (id, '_', '-');
		
		/* find the property in our list, if not found append a new property */
		for (list = *properties; list && list->data; list = list->next)
		{
			property_class = GLADE_PROPERTY_CLASS (list->data);
			if (property_class->id != NULL &&
			    g_ascii_strcasecmp (id, property_class->id) == 0)
				break;
		}

		if (list)
		{
			property_class = GLADE_PROPERTY_CLASS (list->data);
		}
		else
		{
			property_class = glade_property_class_new (adaptor);
			property_class->id = g_strdup (id);

			/* When creating new virtual packing properties,
			 * make sure we mark them as such here. 
			 */
			if (is_packing)
				property_class->packing = TRUE;

			*properties = g_list_append (*properties, property_class);
			list = g_list_last (*properties);
		}

		if ((updated = glade_property_class_update_from_node
		     (child, module, adaptor->type,
		      &property_class, domain)) == FALSE)
		{
			g_warning ("failed to update %s property of %s from xml",
				   id, adaptor->name);
			g_free (id);
			continue;
		}

		/* the property has Disabled=TRUE ... */
		if (!property_class)
			*properties = g_list_delete_link (*properties, list);

		g_free (id);
	}
}

static void
gwa_action_update_from_node (GladeWidgetAdaptor *adaptor,
			     gboolean is_packing,
			     GladeXmlNode *node,
			     const gchar *domain,
			     gchar *group_path)
{
	GladeXmlNode *child;
	gchar *id, *label, *stock, *action_path;
	gboolean group, important;
	
	for (child = glade_xml_node_get_children (node);
	     child; child = glade_xml_node_next (child))
	{
		if ((group = glade_xml_node_verify_silent (child, GLADE_TAG_ACTION)) == FALSE)
			continue;

		id = glade_xml_get_property_string_required
					(child,	GLADE_TAG_ID, adaptor->name);
		if (id == NULL)
			continue;

		if (group_path)
			action_path = g_strdup_printf ("%s/%s", group_path, id);
		else
			action_path = id;
		
		label = glade_xml_get_property_string (child, GLADE_TAG_NAME);
		stock = glade_xml_get_property_string (child, GLADE_TAG_STOCK);
		important = glade_xml_get_property_boolean (child, GLADE_TAG_IMPORTANT, FALSE);
		
		if (label) 
		{
			gchar *translated = dgettext (domain, label);
			if (label != translated)
			{
				g_free (label);
				label = g_strdup (translated);
			}
		}
		
		if (is_packing)
			glade_widget_adaptor_pack_action_add (adaptor, action_path, label, stock, important);
		else
			glade_widget_adaptor_action_add (adaptor, action_path, label, stock, important);
		
		if (group) gwa_action_update_from_node (adaptor, is_packing, child, domain, action_path);
		
		g_free (id);
		g_free (label);
		g_free (stock);
		if (group_path) g_free (action_path);
	}
}

static gboolean
gwa_extend_with_node (GladeWidgetAdaptor *adaptor, 
		      GladeXmlNode       *node,
		      GModule            *module,
		      const gchar        *domain)
{
	GladeXmlNode *child;
	gchar        *child_type;

	/* Override the special-child-type here */
	if ((child_type =
	     glade_xml_get_value_string (node, GLADE_TAG_SPECIAL_CHILD_TYPE)) != NULL)
		adaptor->priv->special_child_type =
			(g_free (adaptor->priv->special_child_type), child_type);
	
	/* if we found a <properties> tag on the xml file, we add the properties
	 * that we read from the xml file to the class.
	 */
	if ((child = 
	     glade_xml_search_child (node, GLADE_TAG_PROPERTIES)) != NULL)
		gwa_update_properties_from_node
			(adaptor, child, module, &adaptor->properties, domain, FALSE);

	if ((child = 
	     glade_xml_search_child (node, GLADE_TAG_PACKING_PROPERTIES)) != NULL)
		gwa_update_properties_from_node
			(adaptor, child, module, &adaptor->packing_props, domain, TRUE);

	if ((child = 
	     glade_xml_search_child (node, GLADE_TAG_PACKING_DEFAULTS)) != NULL)
		gwa_set_packing_defaults_from_node (adaptor, child);

	/* Update actions from child node */
	if ((child = 
	     glade_xml_search_child (node, GLADE_TAG_ACTIONS)) != NULL)
		gwa_action_update_from_node (adaptor, FALSE, child, domain, NULL);
	
	/* Update packing actions from child node */
	if ((child = 
	     glade_xml_search_child (node, GLADE_TAG_PACKING_ACTIONS)) != NULL)
		gwa_action_update_from_node (adaptor, TRUE, child, domain, NULL);
	
	return TRUE;
}

/** 
 * create_icon_name_for_adaptor:
 * @adaptor_name: The name of the widget class
 * @adaptor_type: The #GType of the adaptor class
 * @icon_name:    The icon name as set from the catalog
 * @icon_prefix:  The icon prefix as set from the catalog
 * @generic_name: The generic name for the widget class
 *
 * Creates a suitable icon name for an adaptor, based on several parameters.
 *
 * Returns: An icon name, or NULL if the adaptor does not require one.
 */
static gchar *
create_icon_name_for_adaptor (const gchar *adaptor_name,
			      GType        adaptor_type,
			      const gchar *icon_name,
			      const gchar *icon_prefix,
			      const gchar *generic_name)
{
	gchar *name;

	/* only certain widget adaptors need to have icons */
	if (G_TYPE_IS_INSTANTIATABLE (adaptor_type) == FALSE ||
            G_TYPE_IS_ABSTRACT (adaptor_type) != FALSE ||
            generic_name == NULL)
	{
		return NULL;
	}
	
	/* if no icon name has been specified, we then fallback to a default icon name */
	if (!icon_name)
		name = g_strdup_printf ("widget-%s-%s", icon_prefix, generic_name);
	else
		name = g_strdup (icon_name);
		
	/* check if icon is available */
	if (!gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), name))
	{
		g_warning ("No icon named '%s' was found for widget class '%s'.", name, adaptor_name);
		g_free (name);
		name = g_strdup (DEFAULT_ICON_NAME);
	}
	
	return name;
}

static void
gwa_displayable_values_check (GladeWidgetAdaptor *adaptor, gboolean packing)
{
	GList *l, *p = (packing) ? adaptor->packing_props : adaptor->properties;
	
	for (l = p; l; l = g_list_next (l))
	{
		GladePropertyClass *klass = l->data;
		
		if (adaptor->type == klass->pspec->owner_type &&
		    (G_IS_PARAM_SPEC_ENUM (klass->pspec) || G_IS_PARAM_SPEC_FLAGS (klass->pspec)) &&
		    !klass->displayable_values && klass->visible &&
		    klass->pspec->value_type != GLADE_TYPE_STOCK &&
		    klass->pspec->value_type != GLADE_TYPE_STOCK_IMAGE)
		{
			/* We do not need displayable values if the property is not visible */
			g_message ("No displayable values for %sproperty %s::%s", 
				   (packing) ? "child " : "",
				   adaptor->name, klass->id);
		}
	}
}

static GType
generate_type (const char *name, 
	       const char *parent_name)
{
	GType parent_type;
	GTypeQuery query;
	GTypeInfo *type_info;
	
	g_return_val_if_fail (name != NULL, 0);
	g_return_val_if_fail (parent_name != NULL, 0);
	
	parent_type = glade_util_get_type_from_name (parent_name, FALSE);
	g_return_val_if_fail (parent_type != 0, 0);
	
	g_type_query (parent_type, &query);
	g_return_val_if_fail (query.type != 0, 0);
	
	type_info = g_new0 (GTypeInfo, 1);
	type_info->class_size = query.class_size;
	type_info->instance_size = query.instance_size;
	
	return g_type_register_static (parent_type, name, type_info, 0);
}


/**
 * glade_widget_adaptor_from_catalog:
 * @class_node: A #GladeXmlNode
 * @catname: the name of the owning catalog
 * @icon_prefix:
 * @module: the plugin GModule.
 * @domain: the domain to translate strings from this plugin from
 * @book: the devhelp search domain for the owning catalog.
 *
 * Dynamicly creates a subclass of #GladeWidgetAdaptor and subclasses
 * the closest parent adaptor (parent class adapters must be creates/registerd
 * prior to child classes, otherwise inheritance wont work) and parses in
 * the relevent catalog info.
 */
GladeWidgetAdaptor *
glade_widget_adaptor_from_catalog (GladeXmlNode     *class_node,
				   const gchar      *catname,
				   const gchar      *icon_prefix,
				   GModule          *module,
				   const gchar      *domain,
				   const gchar      *book)
{
	GladeWidgetAdaptor *adaptor = NULL;
	gchar              *name, *generic_name, *icon_name, *adaptor_icon_name, *adaptor_name, *func_name;
	gchar              *title, *translated_title, *parent_name;
	GType               object_type, adaptor_type, parent_type;
	GWADerivedClassData data;
	
	if (!glade_xml_node_verify (class_node, GLADE_TAG_GLADE_WIDGET_CLASS))
	{
		g_warning ("Widget class node is not '%s'", 
			   GLADE_TAG_GLADE_WIDGET_CLASS);
		return NULL;
	}
	
	if ((name = glade_xml_get_property_string_required
	     (class_node, GLADE_TAG_NAME, NULL)) == NULL)
		return NULL;

	/* Either get the instance type by:
	 *
	 * - Autosubclassing a specified parent type (a fake widget class)
	 * - parsing the _get_type() function directly from the catalog
	 * - deriving foo_bar_get_type() from the name FooBar and loading that.
	 */
	if ((parent_name = glade_xml_get_property_string (class_node, GLADE_TAG_PARENT)) != NULL)
	{
		if (!glade_widget_adaptor_get_by_name (parent_name))
		{
			g_warning ("Trying to define class '%s' for parent class '%s', but parent class '%s' "
				   "is not registered", name, parent_name, parent_name);
			g_free (name);
			g_free (parent_name);
			return NULL;
		}
		object_type = generate_type (name, parent_name);
		g_free (parent_name);
	}
	else if ((func_name = glade_xml_get_property_string (class_node, GLADE_TAG_GET_TYPE_FUNCTION)) != NULL)
		object_type = glade_util_get_type_from_name (func_name, TRUE);
	else
		object_type = glade_util_get_type_from_name (name, FALSE);
	
	if (object_type == 0)
	{
		g_warning ("Failed to load the GType for '%s'", name);
		g_free (name);
		return NULL;
	}

	if (glade_widget_adaptor_get_by_type (object_type))
	{
		g_warning ("Adaptor class for '%s' already defined", 
			   g_type_name (object_type));
		g_free (name);
		return NULL;
	}
	
	if ((adaptor_name = glade_xml_get_property_string (class_node, GLADE_TAG_ADAPTOR)))
		adaptor_type = g_type_from_name (adaptor_name);
	else
	{
		/*
		 * We use this struct pointer to pass data to 
		 * gwa_derived_class_init() because we must override constructor()
		 * from the catalog before calling g_object_new() :P
		 */
		data.node = class_node;
		data.module = module;
		adaptor_type = gwa_derive_adaptor_for_type (object_type, &data);
	}
	
	if (adaptor_type == 0)
	{
		g_warning ("Failed to get %s's adaptor %s", name,
			   (adaptor_name) ? adaptor_name : "");
		g_free (adaptor_name);
		g_free (name);
		return NULL;
	}
	
	generic_name = glade_xml_get_property_string (class_node, GLADE_TAG_GENERIC_NAME);
	icon_name    = glade_xml_get_property_string (class_node, GLADE_TAG_ICON_NAME);
	
	/* get a suitable icon name for adaptor */
	adaptor_icon_name = create_icon_name_for_adaptor (name,
							  adaptor_type,
							  icon_name,
							  icon_prefix,
							  generic_name);

	adaptor = g_object_new (adaptor_type, 
				"type", object_type,
				"name", name,
				"generic-name", generic_name,
				"icon-name", adaptor_icon_name,
				NULL);
				
	g_free (generic_name);
	g_free (icon_name);
	g_free (adaptor_icon_name);


	title = glade_xml_get_property_string_required (class_node,
							GLADE_TAG_TITLE,
	      						"This value is needed to "
	      						"display object class names "
	      						"in the UI");
	if (title == NULL)
	{
		g_warning ("Class '%s' declared without a '%s' attribute", name, GLADE_TAG_TITLE);
		adaptor->title = g_strdup (name);
	}
	else
	{
		/* translate */
		translated_title = dgettext (domain, title);
		if (translated_title != title)
		{
			/* gettext owns translated_title */
			adaptor->title = g_strdup (translated_title);
			g_free (title);
		}
		else
		{
			adaptor->title = title;
		}
	}

	if (G_TYPE_IS_INSTANTIATABLE (adaptor->type)    &&
	    G_TYPE_IS_ABSTRACT (adaptor->type) == FALSE &&
	    adaptor->generic_name == NULL)
	{
		g_warning ("Instantiatable class '%s' built without a '%s'",
			   name, GLADE_TAG_GENERIC_NAME);
	}

	/* Perform a stoopid fallback just incase */
	if (adaptor->generic_name == NULL)
		adaptor->generic_name = g_strdup ("widget");
	
	/* Dont mention gtk+ as a required lib in the generated glade file */
	if (strcmp (catname, "gtk+"))
		adaptor->priv->catalog = g_strdup (catname);

	if (book)
		adaptor->priv->book = g_strdup (book);

	gwa_extend_with_node (adaptor, class_node, module, domain);

	/* Set default weight on properties */
	for (parent_type = adaptor->type;
	     parent_type != 0;
	     parent_type = g_type_parent (parent_type))
	{
		gwa_properties_set_weight (&adaptor->properties, parent_type);
		gwa_properties_set_weight (&adaptor->packing_props, parent_type);
	}
	
	gwa_displayable_values_check (adaptor, FALSE);
	gwa_displayable_values_check (adaptor, TRUE);
	
	glade_widget_adaptor_register (adaptor);

	return adaptor;
}

/**
 * glade_widget_adaptor_create_internal:
 * @parent:            The parent #GladeWidget, or %NULL for children
 *                     outside of the hierarchy.
 * @internal_object:   the #GObject
 * @internal_name:     a string identifier for this internal widget.
 * @anarchist:         Whether or not this widget is a widget outside
 *                     of the parent's hierarchy (like a popup window)
 * @reason:            The #GladeCreateReason for which this internal widget
 *                     was created (usually just pass the reason from the post_create
 *                     function; note also this is used only by the plugin code so
 *                     pass something usefull here).
 *
 * A convenienve function to create a #GladeWidget of the prescribed type
 * for internal widgets.
 *
 * Returns: a freshly created #GladeWidget wrapper object for the
 *          @internal_object of name @internal_name
 */
GladeWidget *
glade_widget_adaptor_create_internal  (GladeWidget      *parent,
				       GObject          *internal_object,
				       const gchar      *internal_name,
				       const gchar      *parent_name,
				       gboolean          anarchist,
				       GladeCreateReason reason)
{
	GladeWidgetAdaptor *adaptor;
	GladeProject       *project;

	g_return_val_if_fail (GLADE_IS_WIDGET (parent), NULL);
	project = glade_widget_get_project (parent);

        if ((adaptor = glade_widget_adaptor_get_by_name 
	     (G_OBJECT_TYPE_NAME (internal_object))) == NULL)
	{
		g_critical ("Unable to find widget class for type %s", 
			    G_OBJECT_TYPE_NAME (internal_object));
		return NULL;
	}

	return glade_widget_adaptor_create_widget (adaptor, FALSE,
						   "anarchist", anarchist,
						   "parent", parent,
						   "project", project,
						   "internal", internal_name,
						   "internal-name", parent_name,
						   "reason", reason,
						   "object", internal_object,
						   NULL);
}

/**
 * glade_widget_adaptor_create_widget:
 * @adaptor: a #GladeWidgetAdaptor
 * @query:   whether to display query dialogs if
 *           applicable to the class
 * @...:     a %NULL terminated list of string/value pairs of #GladeWidget 
 *           properties
 *
 *
 * This factory function returns a new #GladeWidget of the correct type/class
 * with the properties defined in @... and queries the user if nescisary.
 *
 * The resulting object will have all default properties applied to it
 * including the overrides specified in the catalog, unless the catalog
 * has specified 'ignore' for that property.
 *
 * Note that the widget class must be fed twice; once as the
 * leading arg... and also as the property for the #GladeWidget
 *
 * this macro returns the newly created #GladeWidget
 */
GladeWidget *
glade_widget_adaptor_create_widget_real (gboolean          query, 
					 const gchar      *first_property,
					 ...)
{
	GladeWidgetAdaptor *adaptor;
	GType               gwidget_type;
	GladeWidget        *gwidget;
	va_list             vl, vl_copy;

	g_return_val_if_fail (strcmp (first_property, "adaptor") == 0, NULL);

	va_start (vl, first_property);
	va_copy (vl_copy, vl);

	adaptor = va_arg (vl, GladeWidgetAdaptor *);

	va_end (vl);

	if (GLADE_IS_WIDGET_ADAPTOR (adaptor) == FALSE)
	{
		g_critical ("No adaptor found in glade_widget_adaptor_create_widget_real args");
		va_end (vl_copy);
		return NULL;
	}

	if (GWA_IS_FIXED (adaptor))
		gwidget_type = GLADE_TYPE_FIXED;
	else 
		gwidget_type = GLADE_TYPE_WIDGET;


	gwidget = (GladeWidget *)g_object_new_valist (gwidget_type,
						      first_property, 
						      vl_copy);
	va_end (vl_copy);
	
	if (query && glade_widget_adaptor_query (adaptor))
	{
		GladeEditor *editor = glade_app_get_editor ();
		
		/* If user pressed cancel on query popup. */
		if (!glade_editor_query_dialog (editor, gwidget))
		{
			g_object_unref (G_OBJECT (gwidget));
			return NULL;
		}
	}

	return gwidget;
}

typedef struct
{
	const gchar        *name;
	GladeWidgetAdaptor *adaptor;
} GladeAdaptorSearchPair;


static void
search_adaptor_by_name (GType                  *type,
			GladeWidgetAdaptor     *adaptor,
			GladeAdaptorSearchPair *pair)
{
	if (!strcmp (adaptor->name, pair->name))
		pair->adaptor = adaptor;
}

/**
 * glade_widget_adaptor_get_by_name:
 * @name: name of the widget class (for instance: GtkButton)
 *
 * Returns: an existing #GladeWidgetAdaptor with the name equaling @name,
 *          or %NULL if such a class doesn't exist
 **/
GladeWidgetAdaptor  *
glade_widget_adaptor_get_by_name (const gchar  *name)
{


	GladeAdaptorSearchPair pair = { name, NULL };
	
	if (adaptor_hash != NULL)
	{
		g_hash_table_foreach (adaptor_hash, 
				      (GHFunc)search_adaptor_by_name, &pair);
	}
	return pair.adaptor;
}


/**
 * glade_widget_adaptor_get_by_type:
 * @type: the #GType of an object class
 *
 * Returns: an existing #GladeWidgetAdaptor with the type equaling @type,
 *          or %NULL if such a class doesn't exist
 **/
GladeWidgetAdaptor  *
glade_widget_adaptor_get_by_type (GType  type)
{
	if (adaptor_hash != NULL)
		return g_hash_table_lookup (adaptor_hash, &type);
	else
		return NULL;
}

/**
 * glade_widget_adaptor_get_property_class:
 * @adaptor: a #GladeWidgetAdaptor
 * @name: a string
 *
 * Retrieves the #GladePropertyClass for @name in @adaptor
 *
 * Returns: A #GladePropertyClass object
 */
GladePropertyClass  *
glade_widget_adaptor_get_property_class (GladeWidgetAdaptor *adaptor,
					 const gchar        *name)
{
	GList *list;
	GladePropertyClass *pclass;

	for (list = adaptor->properties; list && list->data; list = list->next)
	{
		pclass = list->data;
		if (strcmp (pclass->id, name) == 0)
			return pclass;
	}
	return NULL;
}

/**
 * glade_widget_adaptor_get_pack_property_class:
 * @adaptor: a #GladeWidgetAdaptor
 * @name: a string
 *
 * Retrieves the #GladePropertyClass for @name in 
 * @adaptor's child properties
 * 
 * Returns: A #GladePropertyClass object
 */
GladePropertyClass  *
glade_widget_adaptor_get_pack_property_class (GladeWidgetAdaptor *adaptor,
					      const gchar        *name)
{
	GList *list;
	GladePropertyClass *pclass;

	for (list = adaptor->packing_props; list && list->data; list = list->next)
	{
		pclass = list->data;
		if (strcmp (pclass->id, name) == 0)
			return pclass;
	}
	return NULL;
}

/**
 * glade_widget_class_default_params:
 * @adaptor: a #GladeWidgetAdaptor
 * @construct: whether to return construct params or not construct params
 * @n_params: return location if any defaults are specified for this class.
 * 
 * Returns: A list of params for use in g_object_newv ()
 */
GParameter *
glade_widget_adaptor_default_params (GladeWidgetAdaptor *adaptor,
				     gboolean            construct,
				     guint              *n_params)
{
	GArray              *params;
	GObjectClass        *oclass;
	GParamSpec         **pspec;
	GladePropertyClass  *pclass;
	guint                n_props, i;

	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), NULL);
	g_return_val_if_fail (n_params != NULL, NULL);

	/* As a slight optimization, we never unref the class
	 */
	oclass = g_type_class_ref (adaptor->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, };

		pclass = glade_widget_adaptor_get_property_class
			(adaptor, pspec[i]->name);
		
		/* Ignore properties based on some criteria
		 */
		if (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, adaptor->name);
			continue;
		}

		if (g_param_values_cmp (pspec[i], 
					pclass->def, 
					pclass->orig_def) == 0)
			continue;

		parameter.name = pspec[i]->name; /* These are not copied/freed */
		g_value_init (&parameter.value, pspec[i]->value_type);
		g_value_copy (pclass->def, &parameter.value);

		g_array_append_val (params, parameter);
	}
	g_free (pspec);

	*n_params = params->len;
	return (GParameter *)g_array_free (params, FALSE);
}


/**
 * glade_widget_adaptor_post_create:
 * @adaptor:   A #GladeWidgetAdaptor
 * @object:    The #GObject
 * @reason:    The #GladeCreateReason that @object was created for
 *
 * An adaptor function to be called after the object is created
 */
void
glade_widget_adaptor_post_create (GladeWidgetAdaptor *adaptor,
				  GObject            *object,
				  GladeCreateReason   reason)
{
	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));
	g_return_if_fail (G_IS_OBJECT (object));
	g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (object), adaptor->type));

	/* Run post_create in 2 stages, one that chains up and all class adaptors
	 * in the hierarchy get a peek, another that is used to setup placeholders
	 * and things that differ from the child/parent implementations
	 */
	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->deep_post_create)
		GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->deep_post_create (adaptor, object, reason);
	
	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->post_create)
		GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->post_create (adaptor, object, reason);
	/* XXX Dont complain here if no implementation is found */
}

/**
 * glade_widget_adaptor_get_internal_child:
 * @adaptor:       A #GladeWidgetAdaptor
 * @object:        The #GObject
 * @internal_name: The string identifier of the internal object
 *
 * Retrieves the internal object @internal_name from @object
 *
 * Returns: The internal #GObject
 */
GObject *
glade_widget_adaptor_get_internal_child (GladeWidgetAdaptor *adaptor,
					 GObject            *object,
					 const gchar        *internal_name)
{
	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), NULL);
	g_return_val_if_fail (G_IS_OBJECT (object), NULL);
	g_return_val_if_fail (internal_name != NULL, NULL);
	g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (object), adaptor->type), NULL);

	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->get_internal_child)
		return GLADE_WIDGET_ADAPTOR_GET_CLASS
			(adaptor)->get_internal_child (adaptor, object, internal_name);
	else
		g_critical ("No get_internal_child() support in adaptor %s", adaptor->name);

	return NULL;
}

/**
 * glade_widget_adaptor_set_property:
 * @adaptor:       A #GladeWidgetAdaptor
 * @object:        The #GObject
 * @property_name: The property identifier
 * @value:         The #GValue
 *
 * This delagate function is used to apply the property value on
 * the runtime object.
 *
 */
void
glade_widget_adaptor_set_property (GladeWidgetAdaptor *adaptor,
				   GObject            *object,
				   const gchar        *property_name,
				   const GValue       *value)
{
	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));
	g_return_if_fail (G_IS_OBJECT (object));
	g_return_if_fail (property_name != NULL && value != NULL);
	g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (object), adaptor->type));

	/* The base class provides an implementation */
	GLADE_WIDGET_ADAPTOR_GET_CLASS
		(adaptor)->set_property (adaptor, object, property_name, value);
}


/**
 * glade_widget_adaptor_get_property:
 * @adaptor:       A #GladeWidgetAdaptor
 * @object:        The #GObject
 * @property_name: The property identifier
 * @value:         The #GValue
 *
 * Gets @value of @property_name on @object.
 *
 */
void
glade_widget_adaptor_get_property (GladeWidgetAdaptor *adaptor,
				   GObject            *object,
				   const gchar        *property_name,
				   GValue             *value)
{
	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));
	g_return_if_fail (G_IS_OBJECT (object));
	g_return_if_fail (property_name != NULL && value != NULL);
	g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (object), adaptor->type));

	/* The base class provides an implementation */
	GLADE_WIDGET_ADAPTOR_GET_CLASS
		(adaptor)->get_property (adaptor, object, property_name, value);
}


/**
 * glade_widget_adaptor_verify_property:
 * @adaptor:       A #GladeWidgetAdaptor
 * @object:        The #GObject
 * @property_name: The property identifier
 * @value:         The #GValue
 *
 * This delagate function is always called whenever setting any
 * properties with the exception of load time, and copy/paste time
 * (basicly the two places where we recreate a hierarchy that we
 * already know "works") its basicly an optional backend provided
 * boundry checker for properties.
 *
 * Returns: whether or not its OK to set @value on @object, this function
 * will silently return TRUE if the class did not provide a verify function.
 */
gboolean
glade_widget_adaptor_verify_property    (GladeWidgetAdaptor *adaptor,
					 GObject            *object,
					 const gchar        *property_name,
					 const GValue       *value)
{
	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), FALSE);
	g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
	g_return_val_if_fail (property_name != NULL && value != NULL, FALSE);
	g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (object), adaptor->type), FALSE);

	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->verify_property)
		return GLADE_WIDGET_ADAPTOR_GET_CLASS
			(adaptor)->verify_property (adaptor, object, property_name, value);

	return TRUE;
}

/**
 * glade_widget_adaptor_add:
 * @adaptor:   A #GladeWidgetAdaptor
 * @container: The #GObject container
 * @child:     The #GObject child
 *
 * Adds @child to @container.
 */
void
glade_widget_adaptor_add (GladeWidgetAdaptor *adaptor,
			  GObject            *container,
			  GObject            *child)
{
	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));
	g_return_if_fail (G_IS_OBJECT (container));
	g_return_if_fail (G_IS_OBJECT (child));
	g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (container), adaptor->type));

	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->add)
		GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->add (adaptor, container, child);
	else
		g_critical ("No add() support in adaptor %s", adaptor->name);
}


/**
 * glade_widget_adaptor_remove:
 * @adaptor:   A #GladeWidgetAdaptor
 * @container: The #GObject container
 * @child:     The #GObject child
 *
 * Removes @child from @container.
 */
void
glade_widget_adaptor_remove (GladeWidgetAdaptor *adaptor,
			     GObject            *container,
			     GObject            *child)
{
	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));
	g_return_if_fail (G_IS_OBJECT (container));
	g_return_if_fail (G_IS_OBJECT (child));
	g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (container), adaptor->type));

	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->remove)
		GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->remove (adaptor, container, child);
	else
		g_critical ("No remove() support in adaptor %s", adaptor->name);
}

/**
 * glade_widget_adaptor_get_children:
 * @adaptor:   A #GladeWidgetAdaptor
 * @container: The #GObject container
 *
 * Lists the children of @container.
 *
 * Returns: A #GList of children
 */
GList *
glade_widget_adaptor_get_children (GladeWidgetAdaptor *adaptor,
				   GObject            *container)
{
	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), NULL);
	g_return_val_if_fail (G_IS_OBJECT (container), NULL);
	g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (container), adaptor->type), NULL);

	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->get_children)
		return GLADE_WIDGET_ADAPTOR_GET_CLASS
			(adaptor)->get_children (adaptor, container);

	/* Dont complain here if no implementation is found */

	return NULL;
}

/**
 * glade_widget_adaptor_has_child:
 * @adaptor:   A #GladeWidgetAdaptor
 * @container: The #GObject container
 * @child:     The #GObject child
 *
 * Returns whether @child is infact inside @container.
 */
gboolean
glade_widget_adaptor_has_child (GladeWidgetAdaptor *adaptor,
				GObject            *container,
				GObject            *child)
{
	GList *list, *children = NULL;
	gboolean found = FALSE;

	children = glade_widget_adaptor_get_children (adaptor, container);

	for (list = children; list && list->data; list = list->next)
	{
		if (list->data == child)
		{
			found = TRUE;
			break;
		}
	}

	g_list_free (children);
	return found;
}

/**
 * glade_widget_adaptor_child_set_property:
 * @adaptor:       A #GladeWidgetAdaptor
 * @container:     The #GObject container
 * @child:         The #GObject 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_adaptor_child_set_property (GladeWidgetAdaptor *adaptor,
					 GObject            *container,
					 GObject            *child,
					 const gchar        *property_name,
					 const GValue       *value)
{
	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));
	g_return_if_fail (G_IS_OBJECT (container));
	g_return_if_fail (G_IS_OBJECT (child));
	g_return_if_fail (property_name != NULL && value != NULL);
	g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (container), adaptor->type));

	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->child_set_property)
		GLADE_WIDGET_ADAPTOR_GET_CLASS
			(adaptor)->child_set_property (adaptor, container, child,
						       property_name, value);
	else
		g_critical ("No child_set_property() support in adaptor %s", adaptor->name);

}

/**
 * glade_widget_adaptor_child_get_property:
 * @adaptor:       A #GladeWidgetAdaptor
 * @container:     The #GObject container
 * @child:         The #GObject child
 * @property_name: The id of the property
 * @value:         The @GValue
 *
 * Gets @child's packing property identified by @property_name.
 */
void
glade_widget_adaptor_child_get_property (GladeWidgetAdaptor *adaptor,
					 GObject            *container,
					 GObject            *child,
					 const gchar        *property_name,
					 GValue             *value)
{
	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));
	g_return_if_fail (G_IS_OBJECT (container));
	g_return_if_fail (G_IS_OBJECT (child));
	g_return_if_fail (property_name != NULL && value != NULL);
	g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (container), adaptor->type));

	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->child_get_property)
		GLADE_WIDGET_ADAPTOR_GET_CLASS
			(adaptor)->child_get_property (adaptor, container, child,
						       property_name, value);
	else
		g_critical ("No child_set_property() support in adaptor %s", adaptor->name);
}

/**
 * glade_widget_adaptor_child_verify_property:
 * @adaptor:       A #GladeWidgetAdaptor
 * @container:     The #GObject container
 * @child:         The #GObject child
 * @property_name: The id of the property
 * @value:         The @GValue
 *
 * This delagate function is always called whenever setting any
 * properties with the exception of load time, and copy/paste time
 * (basicly the two places where we recreate a hierarchy that we
 * already know "works") its basicly an optional backend provided
 * boundry checker for properties.
 *
 * Returns: whether or not its OK to set @value on @object, this function
 * will silently return TRUE if the class did not provide a verify function.
 */
gboolean
glade_widget_adaptor_child_verify_property (GladeWidgetAdaptor *adaptor,
					    GObject            *container,
					    GObject            *child,
					    const gchar        *property_name,
					    const GValue       *value)
{
	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), FALSE);
	g_return_val_if_fail (G_IS_OBJECT (container), FALSE);
	g_return_val_if_fail (G_IS_OBJECT (child), FALSE);
	g_return_val_if_fail (property_name != NULL && value != NULL, FALSE);
	g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (container), adaptor->type), FALSE);

	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->child_verify_property)
		return GLADE_WIDGET_ADAPTOR_GET_CLASS
			(adaptor)->child_verify_property (adaptor, 
							  container, child,
							  property_name, value);

	return TRUE;
}


/**
 * glade_widget_adaptor_replace_child:
 * @adaptor: A #GladeWidgetAdaptor
 * @container: The #GObject container
 * @old_obj: The old #GObject child
 * @new_obj: The new #GObject child
 *
 * Replaces @old_obj with @new_obj in @container while positioning
 * @new_obj where @old_obj was and assigning it appropriate packing 
 * property values.
 */
void
glade_widget_adaptor_replace_child (GladeWidgetAdaptor *adaptor,
				    GObject            *container,
				    GObject            *old_obj,
				    GObject            *new_obj)
{
	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));
	g_return_if_fail (G_IS_OBJECT (container));
	g_return_if_fail (G_IS_OBJECT (old_obj));
	g_return_if_fail (G_IS_OBJECT (new_obj));
	g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (container), adaptor->type));

	if (GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->replace_child)
		GLADE_WIDGET_ADAPTOR_GET_CLASS 
			(adaptor)->replace_child (adaptor, container, old_obj, new_obj);
	else
		g_critical ("No replace_child() support in adaptor %s", adaptor->name);
}

/**
 * glade_widget_adaptor_query:
 * @adaptor: A #GladeWidgetAdaptor
 *
 * Returns: whether the user needs to be queried for
 * certain properties upon creation of this class.
 */
gboolean
glade_widget_adaptor_query (GladeWidgetAdaptor *adaptor)
{
	GladePropertyClass *pclass;
	GList *l;

	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), FALSE);

	for (l = adaptor->properties; l; l = l->next)
	{
		pclass = l->data;

		if (pclass->query)
			return TRUE;
	}

	return FALSE;
}

/**
 * glade_widget_adaptor_get_packing_default:
 * @child_adaptor:  A #GladeWidgetAdaptor
 * @container_adaptor: The #GladeWidgetAdaptor for the parent object
 * @id:    The string property identifier
 *
 * Gets the default value for @property_id on a widget governed by
 * @child_adaptor when parented in a widget governed by @parent_adaptor
 *
 * Returns: a string representing the default value for @property_id
 */
G_CONST_RETURN gchar *
glade_widget_adaptor_get_packing_default (GladeWidgetAdaptor *child_adaptor,
					  GladeWidgetAdaptor *container_adaptor,
					  const gchar        *id)
{
	GladeChildPacking *packing = NULL;
	GList             *l;

	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (child_adaptor), NULL);
	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (container_adaptor), NULL);

	if ((packing = 
	     glade_widget_adaptor_get_child_packing (child_adaptor,
						     container_adaptor->name)) != NULL)
	{
		for (l = packing->packing_defaults; l; l = l->next)
		{
			GladePackingDefault *def = l->data;
			
			if (strcmp (def->id, id) == 0)
				return def->value;
		}
	}
	return NULL;
}

/**
 * glade_widget_adaptor_is_container:
 * @adaptor: A #GladeWidgetAdaptor
 *
 * Checks whether or not this adaptor has support
 * to interface with child objects.
 *
 * Returns whether or not @adaptor is a container
 */
gboolean
glade_widget_adaptor_is_container (GladeWidgetAdaptor *adaptor)
{

	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), FALSE);

	/* A GWA container must at least implement add/remove/get_children
	 */
	return 	(GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->add &&
		 GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->remove &&
		 GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->get_children);
}

static const gchar *
gwa_action_path_get_id (const gchar *action_path)
{
	const gchar *id;
	
	if ((id = g_strrstr (action_path, "/")) && id[1] != '\0')
		return &id[1];
	else
		return action_path;
}

static GWActionClass *
gwa_action_lookup (GList *actions, const gchar *action_id)
{
	GList *l;
	
	for (l = actions; l; l = g_list_next (l))
	{
		GWActionClass *action = l->data;
		if (strcmp (action->id, action_id) == 0)
			return action;
	}
	
	return NULL;
}

static GWActionClass *
gwa_action_get_last_group (GList *actions, const gchar *action_path)
{
	gchar **tokens = g_strsplit (action_path, "/", 0);
	GWActionClass *group = NULL;
	gint i;
	
	for (i = 0; tokens[i] && tokens[i+1]; i++)
	{
		if ((group = gwa_action_lookup (actions, tokens[i])) == NULL)
		{
			g_strfreev (tokens);
			return NULL;
		}
		actions = group->actions;
	}
	
	g_strfreev (tokens);
	return group;
}

static gboolean
glade_widget_adaptor_action_add_real (GList **list,
				      const gchar *action_path,
				      const gchar *label,
				      const gchar *stock,
				      gboolean important)
{
	GWActionClass *action, *group;
	const gchar *id;
	
	id = gwa_action_path_get_id (action_path);
	
	if ((group = gwa_action_get_last_group (*list, action_path)))
		list = &group->actions;

	if ((action = gwa_action_lookup (*list, id)))
	{
		/* Update parent's label/stock */
		if (label && action->label)
		{
			g_free (action->label);
			if (strcmp (label, "") == 0) label = NULL;
			action->label = (label) ? g_strdup (label) : NULL;
		}
		if (stock && action->stock)
		{
			g_free (action->stock);
			if (strcmp (stock, "") == 0) stock = NULL;
			action->stock = (stock) ? g_strdup (stock) : NULL;
		}
	}
	else
	{
		/* New Action */
		action = g_new0 (GWActionClass, 1);
		action->path = g_strdup (action_path);
		action->id = (gchar*) gwa_action_path_get_id (action->path);
	
		if (label && strcmp (label, "") == 0) label = NULL;
		if (stock && strcmp (stock, "") == 0) stock = NULL;
		
		action->label = (label) ? g_strdup (label) : NULL;
		action->stock = (stock) ? g_strdup (stock) : NULL;
	}
	
	action->important = important;
	
	*list = g_list_append (*list, action);
	
	return TRUE;
}

/**
 * glade_widget_adaptor_action_add:
 * @adaptor: A #GladeWidgetAdaptor
 * @action_path: The identifier of this action in the action tree
 * @label: A translated label to show in the UI for this action
 * @stock: If set, this stock item will be shown in the UI along side the label.
 * @important: if this action is important.
 *
 * Add an action to @adaptor.
 * If the action is present then it overrides label and stock
 *
 * Returns: whether or not the action was added/updated.
 */
gboolean
glade_widget_adaptor_action_add (GladeWidgetAdaptor *adaptor,
				 const gchar *action_path,
				 const gchar *label,
				 const gchar *stock,
				 gboolean important)
{
	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), FALSE);
	g_return_val_if_fail (action_path != NULL, FALSE);
	
	return glade_widget_adaptor_action_add_real (&adaptor->actions,
						     action_path,
						     label,
						     stock,
						     important);
}

/**
 * glade_widget_adaptor_pack_action_add:
 * @adaptor: A #GladeWidgetAdaptor
 * @action_path: The identifier of this action in the action tree
 * @label: A translated label to show in the UI for this action
 * @stock: If set, this stock item will be shown in the UI along side the label.
 * @important: if this action is important.
 *
 * Add a packing action to @adaptor.
 * If the action is present then it overrides label and stock
 *
 * Returns: whether or not the action was added/updated.
 */
gboolean
glade_widget_adaptor_pack_action_add (GladeWidgetAdaptor *adaptor,
				      const gchar *action_path,
				      const gchar *label,
				      const gchar *stock,
				      gboolean important)
{
	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), FALSE);
	g_return_val_if_fail (action_path != NULL, FALSE);
	
	return glade_widget_adaptor_action_add_real (&adaptor->packing_actions,
						     action_path,
						     label,
						     stock,
						     important);
}

static gboolean
glade_widget_adaptor_action_remove_real (GList **list, const gchar *action_path)
{
	GWActionClass *action, *group;
	const gchar *id;
	
	id = gwa_action_path_get_id (action_path);
	
	if ((group = gwa_action_get_last_group (*list, action_path)))
		list = &group->actions;

	if ((action = gwa_action_lookup (*list, id)) == NULL) return FALSE;
	
	*list = g_list_remove (*list, action);
	
	glade_widget_action_class_free (action);
	
	return TRUE;
}

/**
 * glade_widget_adaptor_action_remove:
 * @adaptor: A #GladeWidgetAdaptor
 * @action_path: The identifier of this action in the action tree
 *
 * Remove an @adaptor's action.
 *
 * Returns: whether or not the action was removed.
 */
gboolean
glade_widget_adaptor_action_remove (GladeWidgetAdaptor *adaptor,
				    const gchar *action_path)
{
	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), FALSE);
	g_return_val_if_fail (action_path != NULL, FALSE);
	
	return glade_widget_adaptor_action_remove_real (&adaptor->actions, 
							action_path);
}

/**
 * glade_widget_adaptor_pack_action_remove:
 * @adaptor: A #GladeWidgetAdaptor
 * @action_path: The identifier of this action in the action tree
 *
 * Remove an @adaptor's packing action.
 *
 * Returns: whether or not the action was removed.
 */
gboolean
glade_widget_adaptor_pack_action_remove (GladeWidgetAdaptor *adaptor,
					 const gchar *action_path)
{
	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), FALSE);
	g_return_val_if_fail (action_path != NULL, FALSE);
	
	return glade_widget_adaptor_action_remove_real (&adaptor->packing_actions, 
							action_path);
}


/**
 * glade_widget_adaptor_pack_actions_new:
 * @adaptor: A #GladeWidgetAdaptor
 *
 * Create a list of packing actions.
 *
 * Returns: a new list of GladeWidgetAction.
 */
GList *
glade_widget_adaptor_pack_actions_new (GladeWidgetAdaptor *adaptor)
{
	GList *l, *list = NULL;
	
	g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), NULL);
	
	for (l = adaptor->packing_actions; l; l = g_list_next (l))
	{
		GWActionClass *action = l->data;
		GObject *obj = g_object_new (GLADE_TYPE_WIDGET_ACTION,
					     "class", action, NULL);
		
		list = g_list_prepend (list, GLADE_WIDGET_ACTION (obj));
	}
	return g_list_reverse (list);
}

/**
 * glade_widget_adaptor_action_activate:
 * @adaptor:   A #GladeWidgetAdaptor
 * @object:    The #GObject
 * @action_path: The action identifier in the action tree
 *
 * An adaptor function to be called on widget actions.
 */
void
glade_widget_adaptor_action_activate (GladeWidgetAdaptor *adaptor,
				      GObject            *object,
				      const gchar        *action_path)
{
	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));
	g_return_if_fail (G_IS_OBJECT (object));
	g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (object), adaptor->type));

	GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->action_activate (adaptor, object, action_path);
}

/**
 * glade_widget_adaptor_child_action_activate:
 * @adaptor:   A #GladeWidgetAdaptor
 * @object:    The #GObject
 * @action_path: The action identifier in the action tree
 *
 * An adaptor function to be called on widget actions.
 */
void
glade_widget_adaptor_child_action_activate (GladeWidgetAdaptor *adaptor,
					    GObject            *container,
					    GObject            *object,
					    const gchar        *action_path)
{
	g_return_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor));
	g_return_if_fail (G_IS_OBJECT (container));
	g_return_if_fail (G_IS_OBJECT (object));
	g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (container), adaptor->type));

	GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->child_action_activate (adaptor, container, object, action_path);
}


syntax highlighted by Code2HTML, v. 0.9.1