/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * GtkItemFactory: Flexible item factory with automatic rc handling
 * Copyright (C) 1998 Tim Janik
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

#include	"config.h"

#include	"gtkitemfactory.h"
#include	"gtk/gtksignal.h"
#include	"gtk/gtkoptionmenu.h"
#include	"gtk/gtkmenubar.h"
#include	"gtk/gtkmenu.h"
#include	"gtk/gtkmenuitem.h"
#include	"gtk/gtkradiomenuitem.h"
#include	"gtk/gtkcheckmenuitem.h"
#include	"gtk/gtkimagemenuitem.h"
#include	"gtk/gtktearoffmenuitem.h"
#include	"gtk/gtkaccelmap.h"
#include	"gtk/gtkaccellabel.h"
#include        "gdk/gdkkeysyms.h"
#include	"gtk/gtkimage.h"
#include	"gtk/gtkstock.h"
#include	"gtk/gtkiconfactory.h"
#include	<string.h>
#include	<fcntl.h>
#ifdef HAVE_UNISTD_H
#include	<unistd.h>
#endif
#include	<stdio.h>

/* --- defines --- */
#define		ITEM_FACTORY_STRING	((gchar*) item_factory_string)
#define		ITEM_BLOCK_SIZE		(128)


/* --- structures --- */
typedef struct	_GtkIFCBData		GtkIFCBData;
typedef struct  _GtkIFDumpData		GtkIFDumpData;
struct _GtkIFCBData
{
  GtkItemFactoryCallback  func;
  guint			  callback_type;
  gpointer		  func_data;
  guint			  callback_action;
};


/* --- prototypes --- */
static void	gtk_item_factory_class_init		(GtkItemFactoryClass  *klass);
static void	gtk_item_factory_init			(GtkItemFactory	      *ifactory);
static void	gtk_item_factory_destroy		(GtkObject	      *object);
static void	gtk_item_factory_finalize		(GObject	      *object);


/* --- static variables --- */
static GtkItemFactoryClass *gtk_item_factory_class = NULL;
static gpointer          parent_class = NULL;
static const gchar	*item_factory_string = "Gtk-<ItemFactory>";
static GMemChunk	*ifactory_item_chunks = NULL;
static GMemChunk	*ifactory_cb_data_chunks = NULL;
static GQuark		 quark_popup_data = 0;
static GQuark		 quark_if_menu_pos = 0;
static GQuark		 quark_item_factory = 0;
static GQuark		 quark_item_path = 0;
static GQuark		 quark_action = 0;
static GQuark		 quark_accel_group = 0;
static GQuark		 quark_type_item = 0;
static GQuark		 quark_type_title = 0;
static GQuark		 quark_type_radio_item = 0;
static GQuark		 quark_type_check_item = 0;
static GQuark		 quark_type_toggle_item = 0;
static GQuark		 quark_type_image_item = 0;
static GQuark		 quark_type_stock_item = 0;
static GQuark		 quark_type_tearoff_item = 0;
static GQuark		 quark_type_separator_item = 0;
static GQuark		 quark_type_branch = 0;
static GQuark		 quark_type_last_branch = 0;


/* --- functions --- */
GtkType
gtk_item_factory_get_type (void)
{
  static GtkType item_factory_type = 0;
  
  if (!item_factory_type)
    {
      static const GtkTypeInfo item_factory_info =
      {
	"GtkItemFactory",
	sizeof (GtkItemFactory),
	sizeof (GtkItemFactoryClass),
	(GtkClassInitFunc) gtk_item_factory_class_init,
	(GtkObjectInitFunc) gtk_item_factory_init,
	/* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
        (GtkClassInitFunc) NULL,
      };
      
      item_factory_type = gtk_type_unique (GTK_TYPE_OBJECT, &item_factory_info);
    }
  
  return item_factory_type;
}

static void
gtk_item_factory_class_init (GtkItemFactoryClass  *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
  GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);

  gtk_item_factory_class = class;
  parent_class = g_type_class_peek_parent (class);

  gobject_class->finalize = gtk_item_factory_finalize;

  object_class->destroy = gtk_item_factory_destroy;

  class->item_ht = g_hash_table_new (g_str_hash, g_str_equal);
  ifactory_item_chunks =
    g_mem_chunk_new ("GtkItemFactoryItem",
		     sizeof (GtkItemFactoryItem),
		     sizeof (GtkItemFactoryItem) * ITEM_BLOCK_SIZE,
		     G_ALLOC_ONLY);
  ifactory_cb_data_chunks =
    g_mem_chunk_new ("GtkIFCBData",
		     sizeof (GtkIFCBData),
		     sizeof (GtkIFCBData) * ITEM_BLOCK_SIZE,
		     G_ALLOC_AND_FREE);

  quark_popup_data		= g_quark_from_static_string ("GtkItemFactory-popup-data");
  quark_if_menu_pos		= g_quark_from_static_string ("GtkItemFactory-menu-position");
  quark_item_factory		= g_quark_from_static_string ("GtkItemFactory");
  quark_item_path		= g_quark_from_static_string ("GtkItemFactory-path");
  quark_action			= g_quark_from_static_string ("GtkItemFactory-action");
  quark_accel_group		= g_quark_from_static_string ("GtkAccelGroup");
  quark_type_item		= g_quark_from_static_string ("<Item>");
  quark_type_title		= g_quark_from_static_string ("<Title>");
  quark_type_radio_item		= g_quark_from_static_string ("<RadioItem>");
  quark_type_check_item		= g_quark_from_static_string ("<CheckItem>");
  quark_type_toggle_item	= g_quark_from_static_string ("<ToggleItem>");
  quark_type_image_item         = g_quark_from_static_string ("<ImageItem>");
  quark_type_stock_item         = g_quark_from_static_string ("<StockItem>");
  quark_type_separator_item	= g_quark_from_static_string ("<Separator>");
  quark_type_tearoff_item	= g_quark_from_static_string ("<Tearoff>");
  quark_type_branch		= g_quark_from_static_string ("<Branch>");
  quark_type_last_branch	= g_quark_from_static_string ("<LastBranch>");
}

static void
gtk_item_factory_init (GtkItemFactory	    *ifactory)
{
  GtkObject *object;

  object = GTK_OBJECT (ifactory);

  ifactory->path = NULL;
  ifactory->accel_group = NULL;
  ifactory->widget = NULL;
  ifactory->items = NULL;
  ifactory->translate_func = NULL;
  ifactory->translate_data = NULL;
  ifactory->translate_notify = NULL;
}

/**
 * gtk_item_factory_new:
 * @container_type: the kind of menu to create; can be
 *    #GTK_TYPE_MENU_BAR, #GTK_TYPE_MENU or #GTK_TYPE_OPTION_MENU
 * @path: the factory path of the new item factory, a string of the form 
 *    <literal>"&lt;name&gt;"</literal>
 * @accel_group: a #GtkAccelGroup to which the accelerators for the
 *    menu items will be added, or %NULL to create a new one
 * @returns: a new #GtkItemFactory
 * 
 * Creates a new #GtkItemFactory.
 */
GtkItemFactory*
gtk_item_factory_new (GtkType	     container_type,
		      const gchar   *path,
		      GtkAccelGroup *accel_group)
{
  GtkItemFactory *ifactory;

  g_return_val_if_fail (path != NULL, NULL);

  ifactory = gtk_type_new (GTK_TYPE_ITEM_FACTORY);
  gtk_item_factory_construct (ifactory, container_type, path, accel_group);

  return ifactory;
}

static void
gtk_item_factory_callback_marshal (GtkWidget *widget,
				   gpointer   func_data)
{
  GtkIFCBData *data;

  data = func_data;

  if (data->callback_type == 1)
    {
      GtkItemFactoryCallback1 func1 = (GtkItemFactoryCallback1) data->func;
      func1 (data->func_data, data->callback_action, widget);
    }
  else if (data->callback_type == 2)
    {
      GtkItemFactoryCallback2 func2 = (GtkItemFactoryCallback2) data->func;
      func2 (widget, data->func_data, data->callback_action);
    }
}

static void
gtk_item_factory_item_remove_widget (GtkWidget		*widget,
				     GtkItemFactoryItem *item)
{
  item->widgets = g_slist_remove (item->widgets, widget);
  gtk_object_remove_data_by_id (GTK_OBJECT (widget), quark_item_factory);
  gtk_object_remove_data_by_id (GTK_OBJECT (widget), quark_item_path);
}

/**
 * gtk_item_factory_add_foreign:
 * @accel_widget:     widget to install an accelerator on 
 * @full_path:	      the full path for the @accel_widget 
 * @accel_group:      the accelerator group to install the accelerator in
 * @keyval:           key value of the accelerator
 * @modifiers:        modifier combination of the accelerator
 *
 * Installs an accelerator for @accel_widget in @accel_group, that causes
 * the ::activate signal to be emitted if the accelerator is activated.
 * 
 * This function can be used to make widgets participate in the accel
 * saving/restoring functionality provided by gtk_accel_map_save() and
 * gtk_accel_map_load(), even if they haven't been created by an item
 * factory. The recommended API for this purpose are the functions 
 * gtk_menu_item_set_accel_path() and gtk_widget_set_accel_path(); don't 
 * use gtk_item_factory_add_foreign() in new code, since it is likely to
 * be removed in the future.
 */
void
gtk_item_factory_add_foreign (GtkWidget      *accel_widget,
			      const gchar    *full_path,
			      GtkAccelGroup  *accel_group,
			      guint           keyval,
			      GdkModifierType modifiers)
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;

  g_return_if_fail (GTK_IS_WIDGET (accel_widget));
  g_return_if_fail (full_path != NULL);

  class = gtk_type_class (GTK_TYPE_ITEM_FACTORY);

  keyval = keyval != GDK_VoidSymbol ? keyval : 0;

  item = g_hash_table_lookup (class->item_ht, full_path);
  if (!item)
    {
      item = g_chunk_new (GtkItemFactoryItem, ifactory_item_chunks);

      item->path = g_strdup (full_path);
      item->widgets = NULL;
      
      g_hash_table_insert (class->item_ht, item->path, item);
    }

  item->widgets = g_slist_prepend (item->widgets, accel_widget);
  gtk_signal_connect (GTK_OBJECT (accel_widget),
		      "destroy",
		      GTK_SIGNAL_FUNC (gtk_item_factory_item_remove_widget),
		      item);

  /* set the item path for the widget
   */
  gtk_object_set_data_by_id (GTK_OBJECT (accel_widget), 
                             quark_item_path, item->path);
  gtk_widget_set_name (accel_widget, item->path);
  if (accel_group)
    {
      gtk_accel_group_ref (accel_group);
      gtk_object_set_data_by_id_full (GTK_OBJECT (accel_widget),
				      quark_accel_group,
				      accel_group,
				      (GtkDestroyNotify) gtk_accel_group_unref);
    }
  else
    gtk_object_set_data_by_id (GTK_OBJECT (accel_widget), 
                               quark_accel_group, NULL);

  /* install defined accelerators
   */
  if (gtk_signal_lookup ("activate", GTK_OBJECT_TYPE (accel_widget)))
    {
      if (accel_group)
	{
	  gtk_accel_map_add_entry (full_path, keyval, modifiers);
	  gtk_widget_set_accel_path (accel_widget, full_path, accel_group);
	}
    }
}

static void
ifactory_cb_data_free (gpointer mem)
{
  g_mem_chunk_free (ifactory_cb_data_chunks, mem);
}

static void
gtk_item_factory_add_item (GtkItemFactory		*ifactory,
			   const gchar			*path,
			   const gchar			*accelerator,
			   GtkItemFactoryCallback	callback,
			   guint			callback_action,
			   gpointer			callback_data,
			   guint			callback_type,
			   gchar			*item_type,
			   GtkWidget			*widget)
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;
  gchar *fpath;
  guint keyval, mods;
  
  g_return_if_fail (widget != NULL);
  g_return_if_fail (item_type != NULL);

  class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);

  /* set accelerator group on menu widgets
   */
  if (GTK_IS_MENU (widget))
    gtk_menu_set_accel_group ((GtkMenu*) widget, ifactory->accel_group);

  /* connect callback if neccessary
   */
  if (callback)
    {
      GtkIFCBData *data;

      data = g_chunk_new (GtkIFCBData, ifactory_cb_data_chunks);
      data->func = callback;
      data->callback_type = callback_type;
      data->func_data = callback_data;
      data->callback_action = callback_action;

      gtk_object_weakref (GTK_OBJECT (widget),
			  ifactory_cb_data_free,
			  data);
      gtk_signal_connect (GTK_OBJECT (widget),
			  "activate",
			  GTK_SIGNAL_FUNC (gtk_item_factory_callback_marshal),
			  data);
    }

  /* link the widget into its item-entry
   * and keep back pointer on both the item factory and the widget
   */
  gtk_object_set_data_by_id (GTK_OBJECT (widget), quark_action, GUINT_TO_POINTER (callback_action));
  gtk_object_set_data_by_id (GTK_OBJECT (widget), quark_item_factory, ifactory);
  if (accelerator)
    gtk_accelerator_parse (accelerator, &keyval, &mods);
  else
    {
      keyval = 0;
      mods = 0;
    }
  fpath = g_strconcat (ifactory->path, path, NULL);
  gtk_item_factory_add_foreign (widget, fpath, ifactory->accel_group, keyval, mods);
  item = g_hash_table_lookup (class->item_ht, fpath);
  g_free (fpath);

  g_return_if_fail (item != NULL);

  if (!g_slist_find (ifactory->items, item))
    ifactory->items = g_slist_prepend (ifactory->items, item);
}

/**
 * gtk_item_factory_construct:
 * @ifactory: a #GtkItemFactory
 * @container_type: the kind of menu to create; can be
 *    #GTK_TYPE_MENU_BAR, #GTK_TYPE_MENU or #GTK_TYPE_OPTION_MENU
 * @path: the factory path of @ifactory, a string of the form 
 *    <literal>"&lt;name&gt;"</literal>
 * @accel_group: a #GtkAccelGroup to which the accelerators for the
 *    menu items will be added, or %NULL to create a new one
 * 
 * Initializes an item factory.
 */  
void
gtk_item_factory_construct (GtkItemFactory	*ifactory,
			    GtkType		 container_type,
			    const gchar		*path,
			    GtkAccelGroup	*accel_group)
{
  guint len;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  g_return_if_fail (ifactory->accel_group == NULL);
  g_return_if_fail (path != NULL);
  if (!gtk_type_is_a (container_type, GTK_TYPE_OPTION_MENU))
    g_return_if_fail (gtk_type_is_a (container_type, GTK_TYPE_MENU_SHELL));

  len = strlen (path);

  if (path[0] != '<' && path[len - 1] != '>')
    {
      g_warning ("GtkItemFactory: invalid factory path `%s'", path);
      return;
    }

  if (accel_group)
    {
      ifactory->accel_group = accel_group;
      gtk_accel_group_ref (ifactory->accel_group);
    }
  else
    ifactory->accel_group = gtk_accel_group_new ();

  ifactory->path = g_strdup (path);
  ifactory->widget = g_object_connect (gtk_widget_new (container_type, NULL),
				       "signal::destroy", gtk_widget_destroyed, &ifactory->widget,
				       NULL);
  gtk_object_ref (GTK_OBJECT (ifactory));
  gtk_object_sink (GTK_OBJECT (ifactory));

  gtk_item_factory_add_item (ifactory,
			     "", NULL,
			     NULL, 0, NULL, 0,
			     ITEM_FACTORY_STRING,
			     ifactory->widget);
}

/**
 * gtk_item_factory_from_path:
 * @path: a string starting with a factory path of the form 
 *   <literal>"&lt;name&gt;"</literal>
 * @returns: the #GtkItemFactory created for the given factory path, or %NULL 
 *
 * Finds an item factory which has been constructed using the 
 * <literal>"&lt;name&gt;"</literal> prefix of @path as the @path argument 
 * for gtk_item_factory_new().
 */
GtkItemFactory*
gtk_item_factory_from_path (const gchar      *path)
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;
  gchar *fname;
  guint i;

  g_return_val_if_fail (path != NULL, NULL);
  g_return_val_if_fail (path[0] == '<', NULL);

  class = gtk_type_class (GTK_TYPE_ITEM_FACTORY);

  i = 0;
  while (path[i] && path[i] != '>')
    i++;
  if (path[i] != '>')
    {
      g_warning ("gtk_item_factory_from_path(): invalid factory path \"%s\"",
		 path);
      return NULL;
    }
  fname = g_new (gchar, i + 2);
  g_memmove (fname, path, i + 1);
  fname[i + 1] = 0;

  item = g_hash_table_lookup (class->item_ht, fname);

  g_free (fname);

  if (item && item->widgets)
    return gtk_item_factory_from_widget (item->widgets->data);

  return NULL;
}

static void
gtk_item_factory_destroy (GtkObject *object)
{
  GtkItemFactory *ifactory;
  GSList *slist;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (object));

  ifactory = (GtkItemFactory*) object;

  if (ifactory->widget)
    {
      GtkObject *dobj;

      dobj = GTK_OBJECT (ifactory->widget);

      gtk_object_ref (dobj);
      gtk_object_sink (dobj);
      gtk_object_destroy (dobj);
      gtk_object_unref (dobj);

      ifactory->widget = NULL;
    }

  for (slist = ifactory->items; slist; slist = slist->next)
    {
      GtkItemFactoryItem *item = slist->data;
      GSList *link;
      
      for (link = item->widgets; link; link = link->next)
	if (gtk_object_get_data_by_id (link->data, quark_item_factory) == ifactory)
	  gtk_object_remove_data_by_id (link->data, quark_item_factory);
    }
  g_slist_free (ifactory->items);
  ifactory->items = NULL;

  GTK_OBJECT_CLASS (parent_class)->destroy (object);
}

static void
gtk_item_factory_finalize (GObject *object)
{
  GtkItemFactory *ifactory;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (object));

  ifactory = GTK_ITEM_FACTORY (object);

  gtk_accel_group_unref (ifactory->accel_group);
  g_free (ifactory->path);
  g_assert (ifactory->widget == NULL);

  if (ifactory->translate_notify)
    ifactory->translate_notify (ifactory->translate_data);
  
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

/**
 * gtk_item_factory_from_widget:
 * @widget: a widget
 * @returns: the item factory from which @widget was created, or %NULL
 *
 * Obtains the item factory from which a widget was created.
 */
GtkItemFactory*
gtk_item_factory_from_widget (GtkWidget	       *widget)
{
  GtkItemFactory *ifactory;

  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

  ifactory = gtk_object_get_data_by_id (GTK_OBJECT (widget), quark_item_factory);
  if (ifactory == NULL && GTK_IS_MENU_ITEM (widget) &&
      GTK_MENU_ITEM (widget)->submenu != NULL) 
    {
      GtkWidget *menu = GTK_MENU_ITEM (widget)->submenu;
      ifactory = gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_item_factory);
    }

  return ifactory;
}

/**
 * gtk_item_factory_path_from_widget:
 * @widget: a widget
 * @returns: the full path to @widget if it been created by an item factory, 
 *   %NULL otherwise. This value is owned by GTK+ and must not be
 *   modified or freed.
 * 
 * If @widget has been created by an item factory, returns the full path
 * to it. (The full path of a widget is the concatenation of the factory 
 * path specified in gtk_item_factory_new() with the path specified in the 
 * #GtkItemFactoryEntry from which the widget was created.)
 */
G_CONST_RETURN gchar*
gtk_item_factory_path_from_widget (GtkWidget	    *widget)
{
  gchar* path;

  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

  path = gtk_object_get_data_by_id (GTK_OBJECT (widget), quark_item_path);

  if (path == NULL && GTK_IS_MENU_ITEM (widget) &&
      GTK_MENU_ITEM (widget)->submenu != NULL) 
    {
      GtkWidget *menu = GTK_MENU_ITEM (widget)->submenu;
      path = gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_item_path);
    }

  return path;
}

/**
 * gtk_item_factory_create_items:
 * @ifactory: a #GtkItemFactory
 * @n_entries: the length of @entries
 * @entries: an array of #GtkItemFactoryEntry<!>s whose @callback members
 *    must by of type #GtkItemFactoryCallback1
 * @callback_data: data passed to the callback functions of all entries
 *
 * Creates the menu items from the @entries.
 */
void
gtk_item_factory_create_items (GtkItemFactory	   *ifactory,
			       guint		    n_entries,
			       GtkItemFactoryEntry *entries,
			       gpointer		    callback_data)
{
  gtk_item_factory_create_items_ac (ifactory, n_entries, entries, callback_data, 1);
}

/**
 * gtk_item_factory_create_items_ac:
 * @ifactory: a #GtkItemFactory
 * @n_entries: the length of @entries
 * @entries: an array of #GtkItemFactoryEntry<!>s 
 * @callback_data: data passed to the callback functions of all entries
 * @callback_type: 1 if the callback functions in @entries are of type
 *    #GtkItemFactoryCallback1, 2 if they are of type #GtkItemFactoryCallback2 
 *
 * Creates the menu items from the @entries.
 */
void
gtk_item_factory_create_items_ac (GtkItemFactory      *ifactory,
				  guint		       n_entries,
				  GtkItemFactoryEntry *entries,
				  gpointer	       callback_data,
				  guint		       callback_type)
{
  guint i;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  g_return_if_fail (callback_type >= 1 && callback_type <= 2);

  if (n_entries == 0)
    return;

  g_return_if_fail (entries != NULL);

  for (i = 0; i < n_entries; i++)
    gtk_item_factory_create_item (ifactory, entries + i, callback_data, callback_type);
}

/**
 * gtk_item_factory_get_widget:
 * @ifactory: a #GtkItemFactory
 * @path: the path to the widget
 * @returns: the widget for the given path, or %NULL if @path doesn't lead
 *   to a widget
 *
 * Obtains the widget which corresponds to @path. 
 *
 * If the widget corresponding to @path is a menu item which opens a 
 * submenu, then the submenu is returned. If you are interested in the menu 
 * item, use gtk_item_factory_get_item() instead.
 */
GtkWidget*
gtk_item_factory_get_widget (GtkItemFactory *ifactory,
			     const gchar    *path)
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;

  g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);
  g_return_val_if_fail (path != NULL, NULL);

  class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);

  if (path[0] == '<')
    item = g_hash_table_lookup (class->item_ht, (gpointer) path);
  else
    {
      gchar *fpath;

      fpath = g_strconcat (ifactory->path, path, NULL);
      item = g_hash_table_lookup (class->item_ht, fpath);
      g_free (fpath);
    }

  if (item)
    {
      GSList *slist;

      for (slist = item->widgets; slist; slist = slist->next)
	{
	  if (gtk_item_factory_from_widget (slist->data) == ifactory)
	    return slist->data;
	}
    }

  return NULL;
}

/**
 * gtk_item_factory_get_widget_by_action:
 * @ifactory: a #GtkItemFactory
 * @action: an action as specified in the @callback_action field
 *   of #GtkItemFactoryEntry
 * @returns: the widget which corresponds to the given action, or %NULL
 *   if no widget was found
 *
 * Obtains the widget which was constructed from the #GtkItemFactoryEntry
 * with the given @action.
 *
 * If there are multiple items with the same action, the result is 
 * undefined.
 */
GtkWidget*
gtk_item_factory_get_widget_by_action (GtkItemFactory *ifactory,
				       guint	       action)
{
  GSList *slist;

  g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);

  for (slist = ifactory->items; slist; slist = slist->next)
    {
      GtkItemFactoryItem *item = slist->data;
      GSList *link;

      for (link = item->widgets; link; link = link->next)
	if (gtk_object_get_data_by_id (link->data, quark_item_factory) == ifactory &&
	    gtk_object_get_data_by_id (link->data, quark_action) == GUINT_TO_POINTER (action))
	  return link->data;
    }

  return NULL;
}

/** 
 * gtk_item_factory_get_item:
 * @ifactory: a #GtkItemFactory
 * @path: the path to the menu item
 * @returns: the menu item for the given path, or %NULL if @path doesn't
 *   lead to a menu item
 *
 * Obtains the menu item which corresponds to @path. 
 *
 * If the widget corresponding to @path is a menu item which opens a 
 * submenu, then the item is returned. If you are interested in the submenu, 
 * use gtk_item_factory_get_widget() instead.
 */
GtkWidget*
gtk_item_factory_get_item (GtkItemFactory *ifactory,
			   const gchar    *path)
{
  GtkWidget *widget;

  g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);
  g_return_val_if_fail (path != NULL, NULL);

  widget = gtk_item_factory_get_widget (ifactory, path);

  if (GTK_IS_MENU (widget))
    widget = gtk_menu_get_attach_widget (GTK_MENU (widget));

  return GTK_IS_ITEM (widget) ? widget : NULL;
}


/**
 * gtk_item_factory_get_item_by_action:
 * @ifactory: a #GtkItemFactory
 * @action: an action as specified in the @callback_action field
 *   of #GtkItemFactoryEntry
 * @returns: the menu item which corresponds to the given action, or %NULL
 *   if no menu item was found
 *
 * Obtains the menu item which was constructed from the first 
 * #GtkItemFactoryEntry with the given @action.
 */
GtkWidget*
gtk_item_factory_get_item_by_action (GtkItemFactory *ifactory,
				     guint	     action)
{
  GtkWidget *widget;

  g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);

  widget = gtk_item_factory_get_widget_by_action (ifactory, action);

  if (GTK_IS_MENU (widget))
    widget = gtk_menu_get_attach_widget (GTK_MENU (widget));

  return GTK_IS_ITEM (widget) ? widget : NULL;
}

static char *
item_factory_find_separator_r (char *path)
{
  gchar *result = NULL;
  gboolean escaped = FALSE;

  while (*path)
    {
      if (escaped)
	escaped = FALSE;
      else
	{
	  if (*path == '\\')
	    escaped = TRUE;
	  else if (*path == '/')
	    result = path;
	}
      
      path++;
    }

  return result;
}

static char *
item_factory_unescape_label (const char *label)
{
  char *new = g_malloc (strlen (label) + 1);
  char *p = new;
  gboolean escaped = FALSE;
  
  while (*label)
    {
      if (escaped)
	{
	  *p++ = *label;
	  escaped = FALSE;
	}
      else
	{
	  if (*label == '\\')
	    escaped = TRUE;
	  else
	    *p++ = *label;
	}
      
      label++;
    }

  *p = '\0';

  return new;
}

static gboolean
gtk_item_factory_parse_path (GtkItemFactory *ifactory,
			     gchar          *str,
			     gchar         **path,
			     gchar         **parent_path,
			     gchar         **item)
{
  gchar *translation;
  gchar *p, *q;
  
  *path = g_strdup (str);

  p = q = *path;
  while (*p)
    {
      if (*p == '_')
	{
	  if (p[1] == '_')
	    {
	      p++;
	      *q++ = '_';
	    }
	}
      else
	{
	  *q++ = *p;
	}
      p++;
    }
  *q = 0;

  *parent_path = g_strdup (*path);
  p = item_factory_find_separator_r (*parent_path);
  if (!p)
    {
      g_warning ("GtkItemFactory: invalid entry path `%s'", str);
      return FALSE;
    }
  *p = 0;

  if (ifactory->translate_func)
    translation = ifactory->translate_func (str, ifactory->translate_data);
  else
    translation = str;
			      
  p = item_factory_find_separator_r (translation);
  if (p)
    p++;
  else
    p = translation;

  *item = item_factory_unescape_label (p);

  return TRUE;
}

/**
 * gtk_item_factory_create_item:
 * @ifactory: a #GtkItemFactory
 * @entry: the #GtkItemFactoryEntry to create an item for
 * @callback_data: data passed to the callback function of @entry
 * @callback_type: 1 if the callback function of @entry is of type
 *    #GtkItemFactoryCallback1, 2 if it is of type #GtkItemFactoryCallback2 
 *
 * Creates an item for @entry.
 */
void
gtk_item_factory_create_item (GtkItemFactory	     *ifactory,
			      GtkItemFactoryEntry    *entry,
			      gpointer		      callback_data,
			      guint		      callback_type)
{
  GtkOptionMenu *option_menu = NULL;
  GtkWidget *parent;
  GtkWidget *widget;
  GtkWidget *image;
  GSList *radio_group;
  gchar *name;
  gchar *parent_path;
  gchar *path;
  gchar *accelerator;
  guint type_id;
  GtkType type;
  gchar *item_type_path;
  GtkStockItem stock_item;
      
  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  g_return_if_fail (entry != NULL);
  g_return_if_fail (entry->path != NULL);
  g_return_if_fail (entry->path[0] == '/');
  g_return_if_fail (callback_type >= 1 && callback_type <= 2);

  if (!entry->item_type ||
      entry->item_type[0] == 0)
    {
      item_type_path = "<Item>";
      type_id = quark_type_item;
    }
  else
    {
      item_type_path = entry->item_type;
      type_id = gtk_object_data_try_key (item_type_path);
    }

  radio_group = NULL;
  if (type_id == quark_type_item)
    type = GTK_TYPE_MENU_ITEM;
  else if (type_id == quark_type_title)
    type = GTK_TYPE_MENU_ITEM;
  else if (type_id == quark_type_radio_item)
    type = GTK_TYPE_RADIO_MENU_ITEM;
  else if (type_id == quark_type_check_item)
    type = GTK_TYPE_CHECK_MENU_ITEM;
  else if (type_id == quark_type_image_item)
    type = GTK_TYPE_IMAGE_MENU_ITEM;
  else if (type_id == quark_type_stock_item)
    type = GTK_TYPE_IMAGE_MENU_ITEM;
  else if (type_id == quark_type_tearoff_item)
    type = GTK_TYPE_TEAROFF_MENU_ITEM;
  else if (type_id == quark_type_toggle_item)
    type = GTK_TYPE_CHECK_MENU_ITEM;
  else if (type_id == quark_type_separator_item)
    type = GTK_TYPE_MENU_ITEM;
  else if (type_id == quark_type_branch)
    type = GTK_TYPE_MENU_ITEM;
  else if (type_id == quark_type_last_branch)
    type = GTK_TYPE_MENU_ITEM;
  else
    {
      GtkWidget *radio_link;

      radio_link = gtk_item_factory_get_widget (ifactory, item_type_path);
      if (radio_link && GTK_IS_RADIO_MENU_ITEM (radio_link))
	{
	  type = GTK_TYPE_RADIO_MENU_ITEM;
	  radio_group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (radio_link));
	}
      else
	{
	  g_warning ("GtkItemFactory: entry path `%s' has invalid type `%s'",
		     entry->path,
		     item_type_path);
	  return;
	}
    }

  if (!gtk_item_factory_parse_path (ifactory, entry->path, 
				    &path, &parent_path, &name))
    return;

  parent = gtk_item_factory_get_widget (ifactory, parent_path);
  if (!parent)
    {
      GtkItemFactoryEntry pentry;
      gchar *ppath, *p;

      ppath = g_strdup (entry->path);
      p = item_factory_find_separator_r (ppath);
      g_return_if_fail (p != NULL);
      *p = 0;
      pentry.path = ppath;
      pentry.accelerator = NULL;
      pentry.callback = NULL;
      pentry.callback_action = 0;
      pentry.item_type = "<Branch>";

      gtk_item_factory_create_item (ifactory, &pentry, NULL, 1);
      g_free (ppath);

      parent = gtk_item_factory_get_widget (ifactory, parent_path);
      g_return_if_fail (parent != NULL);
    }

  if (GTK_IS_OPTION_MENU (parent))
    {
      option_menu = GTK_OPTION_MENU (parent);
      if (!option_menu->menu)
	{
	  GtkWidget *menu = g_object_new (GTK_TYPE_MENU, NULL);
	  gchar *p = g_strconcat (ifactory->path, parent_path, NULL);

	  gtk_menu_set_accel_path (GTK_MENU (menu), p);
	  g_free (p);
	  gtk_option_menu_set_menu (option_menu, menu);
	}
      parent = option_menu->menu;
    }
  g_free (parent_path);
			      
  g_return_if_fail (GTK_IS_CONTAINER (parent));

  accelerator = entry->accelerator;
  
  widget = gtk_widget_new (type,
			   "visible", TRUE,
			   "sensitive", (type_id != quark_type_separator_item &&
					 type_id != quark_type_title),
			   "parent", parent,
			   NULL);
  if (option_menu && !option_menu->menu_item)
    gtk_option_menu_set_history (option_menu, 0);

  if (type == GTK_TYPE_RADIO_MENU_ITEM)
    gtk_radio_menu_item_set_group (GTK_RADIO_MENU_ITEM (widget), radio_group);
  if (GTK_IS_CHECK_MENU_ITEM (widget))
    gtk_check_menu_item_set_show_toggle (GTK_CHECK_MENU_ITEM (widget), TRUE);
  if (type_id == quark_type_image_item)
    {
      GdkPixbuf *pixbuf = NULL;

      image = NULL;
      pixbuf = gdk_pixbuf_new_from_inline (-1,
					   entry->extra_data,
					   FALSE,
					   NULL);
      if (pixbuf)
	image = gtk_image_new_from_pixbuf (pixbuf);
      if (image)
	{
	  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (widget), image);
	  gtk_widget_show (image);
	}
      if (pixbuf)
	g_object_unref (G_OBJECT (pixbuf));
    }
  if (type_id == quark_type_stock_item)
    {
      image = gtk_image_new_from_stock (entry->extra_data, GTK_ICON_SIZE_MENU);
      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (widget), image);
      gtk_widget_show (image);

      if (gtk_stock_lookup (entry->extra_data, &stock_item))
	{
	  if (!accelerator)
	    accelerator = gtk_accelerator_name (stock_item.keyval, stock_item.modifier);
	}
    }

  /* install underline accelerators for this item
   */
  if (type_id != quark_type_separator_item && 
      type_id != quark_type_tearoff_item &&
      *name)
    {
      GtkWidget *label;
      
      label = gtk_widget_new (GTK_TYPE_ACCEL_LABEL,
			      "visible", TRUE,
			      "parent", widget,
			      "accel_widget", widget,
			      "xalign", 0.0,
			      NULL);
      gtk_label_set_text_with_mnemonic (GTK_LABEL (label), name);
    }
  
  g_free (name);
  
  if (type_id == quark_type_branch ||
      type_id == quark_type_last_branch)
    {
      gchar *p;

      if (entry->callback)
	g_warning ("gtk_item_factory_create_item(): Can't specify a callback on a branch: \"%s\"",
		   entry->path);
      if (type_id == quark_type_last_branch)
	gtk_menu_item_set_right_justified (GTK_MENU_ITEM (widget), TRUE);
      
      parent = widget;
      widget = gtk_widget_new (GTK_TYPE_MENU, NULL);
      p = g_strconcat (ifactory->path, path, NULL);
      gtk_menu_set_accel_path (GTK_MENU (widget), p);
      g_free (p);
      
      gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), widget);
    }	   
  
  gtk_item_factory_add_item (ifactory,
			     path, accelerator,
			     (type_id == quark_type_branch ||
			      type_id == quark_type_last_branch) ?
			      (GtkItemFactoryCallback) NULL : entry->callback,
			     entry->callback_action, callback_data,
			     callback_type,
			     item_type_path,
			     widget);
  if (accelerator != entry->accelerator)
    g_free (accelerator);
  g_free (path);
}

/**
 * gtk_item_factory_create_menu_entries:
 * @n_entries: the length of @entries
 * @entries: an array of #GtkMenuEntry<!>s 
 *
 * Creates the menu items from the @entries.
 */
void
gtk_item_factory_create_menu_entries (guint              n_entries,
				      GtkMenuEntry      *entries)
{
  static GPatternSpec *pspec_separator = NULL;
  static GPatternSpec *pspec_check = NULL;
  guint i;

  if (!n_entries)
    return;
  g_return_if_fail (entries != NULL);

  if (!pspec_separator)
    {
      pspec_separator = g_pattern_spec_new ("*<separator>*");
      pspec_check = g_pattern_spec_new ("*<check>*");
    }

  for (i = 0; i < n_entries; i++)
    {
      GtkItemFactory *ifactory;
      GtkItemFactoryEntry entry;
      gchar *path;
      gchar *cpath;

      path = entries[i].path;
      ifactory = gtk_item_factory_from_path (path);
      if (!ifactory)
	{
	  g_warning ("gtk_item_factory_create_menu_entries(): "
		     "entry[%u] refers to unknown item factory: \"%s\"",
		     i, entries[i].path);
	  continue;
	}

      while (*path != '>')
	path++;
      path++;
      cpath = NULL;

      entry.path = path;
      entry.accelerator = entries[i].accelerator;
      entry.callback = entries[i].callback;
      entry.callback_action = 0;
      if (g_pattern_match_string (pspec_separator, path))
	entry.item_type = "<Separator>";
      else if (!g_pattern_match_string (pspec_check, path))
	entry.item_type = NULL;
      else
	{
	  gboolean in_brace = FALSE;
	  gchar *c;
	  
	  cpath = g_new (gchar, strlen (path));
	  c = cpath;
	  while (*path != 0)
	    {
	      if (*path == '<')
		in_brace = TRUE;
	      else if (*path == '>')
		in_brace = FALSE;
	      else if (!in_brace)
		*(c++) = *path;
	      path++;
	    }
	  *c = 0;
	  entry.item_type = "<ToggleItem>";
	  entry.path = cpath;
	}
      
      gtk_item_factory_create_item (ifactory, &entry, entries[i].callback_data, 2);
      entries[i].widget = gtk_item_factory_get_widget (ifactory, entries[i].path);
      g_free (cpath);
    }
}

/**
 * gtk_item_factories_path_delete:
 * @ifactory_path: a factory path to prepend to @path. May be %NULL if @path
 *   starts with a factory path
 * @path: a path 
 * 
 * Deletes all widgets constructed from the specified path.
 */
void
gtk_item_factories_path_delete (const gchar *ifactory_path,
				const gchar *path)
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;

  g_return_if_fail (path != NULL);

  class = gtk_type_class (GTK_TYPE_ITEM_FACTORY);

  if (path[0] == '<')
    item = g_hash_table_lookup (class->item_ht, (gpointer) path);
  else
    {
      gchar *fpath;

      g_return_if_fail (ifactory_path != NULL);
      
      fpath = g_strconcat (ifactory_path, path, NULL);
      item = g_hash_table_lookup (class->item_ht, fpath);
      g_free (fpath);
    }
  
  if (item)
    {
      GSList *widget_list;
      GSList *slist;

      widget_list = NULL;
      for (slist = item->widgets; slist; slist = slist->next)
	{
	  GtkWidget *widget;

	  widget = slist->data;
	  widget_list = g_slist_prepend (widget_list, widget);
	  gtk_widget_ref (widget);
	}

      for (slist = widget_list; slist; slist = slist->next)
	{
	  GtkWidget *widget;

	  widget = slist->data;
	  gtk_widget_destroy (widget);
	  gtk_widget_unref (widget);
	}
      g_slist_free (widget_list);
    }
}

/**
 * gtk_item_factory_delete_item:
 * @ifactory: a #GtkItemFactory
 * @path: a path
 *
 * Deletes the menu item which was created for @path by the given
 * item factory.
 */
void
gtk_item_factory_delete_item (GtkItemFactory         *ifactory,
			      const gchar            *path)
{
  GtkItemFactoryClass *class;
  GtkWidget *widget;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  g_return_if_fail (path != NULL);

  class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);

  widget = gtk_item_factory_get_widget (ifactory, path);

  if (widget)
    {
      if (GTK_IS_MENU (widget))
	widget = gtk_menu_get_attach_widget (GTK_MENU (widget));

      gtk_widget_destroy (widget);
    }
}

/**
 * gtk_item_factory_delete_entry:
 * @ifactory: a #GtkItemFactory
 * @entry: a #GtkItemFactoryEntry
 *
 * Deletes the menu item which was created from @entry by the given
 * item factory.
 */
void
gtk_item_factory_delete_entry (GtkItemFactory         *ifactory,
			       GtkItemFactoryEntry    *entry)
{
  gchar *path;
  gchar *parent_path;
  gchar *name;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  g_return_if_fail (entry != NULL);
  g_return_if_fail (entry->path != NULL);
  g_return_if_fail (entry->path[0] == '/');

  if (!gtk_item_factory_parse_path (ifactory, entry->path, 
				    &path, &parent_path, &name))
    return;
  
  gtk_item_factory_delete_item (ifactory, path);

  g_free (path);
  g_free (parent_path);
  g_free (name);
}

/**
 * gtk_item_factory_delete_entries:
 * @ifactory: a #GtkItemFactory
 * @n_entries: the length of @entries
 * @entries: an array of #GtkItemFactoryEntry<!>s 
 *
 * Deletes the menu items which were created from the @entries by the given
 * item factory.
 */
void
gtk_item_factory_delete_entries (GtkItemFactory         *ifactory,
				 guint                   n_entries,
				 GtkItemFactoryEntry    *entries)
{
  guint i;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  if (n_entries > 0)
    g_return_if_fail (entries != NULL);

  for (i = 0; i < n_entries; i++)
    gtk_item_factory_delete_entry (ifactory, entries + i);
}

typedef struct
{
  guint x;
  guint y;
} MenuPos;

static void
gtk_item_factory_menu_pos (GtkMenu  *menu,
			   gint     *x,
			   gint     *y,
                           gboolean *push_in,
			   gpointer  func_data)
{
  MenuPos *mpos = func_data;

  *x = mpos->x;
  *y = mpos->y;
}

/**
 * gtk_item_factory_popup_data_from_widget:
 * @widget: a widget
 * @returns: @popup_data associated with the item factory from
 *   which @widget was created, or %NULL if @widget wasn't created
 *   by an item factory
 *
 * Obtains the @popup_data which was passed to 
 * gtk_item_factory_popup_with_data(). This data is available until the menu
 * is popped down again.
 */
gpointer
gtk_item_factory_popup_data_from_widget (GtkWidget *widget)
{
  GtkItemFactory *ifactory;
  
  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

  ifactory = gtk_item_factory_from_widget (widget);
  if (ifactory)
    return gtk_object_get_data_by_id (GTK_OBJECT (ifactory), quark_popup_data);

  return NULL;
}

/**
 * gtk_item_factory_popup_data:
 * @ifactory: a #GtkItemFactory
 * @returns: @popup_data associated with @ifactory
 *
 * Obtains the @popup_data which was passed to 
 * gtk_item_factory_popup_with_data(). This data is available until the menu
 * is popped down again.
 */
gpointer
gtk_item_factory_popup_data (GtkItemFactory *ifactory)
{
  g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);

  return gtk_object_get_data_by_id (GTK_OBJECT (ifactory), quark_popup_data);
}

static void
ifactory_delete_popup_data (GtkObject	   *object,
			    GtkItemFactory *ifactory)
{
  gtk_signal_disconnect_by_func (object,
				 GTK_SIGNAL_FUNC (ifactory_delete_popup_data),
				 ifactory);
  gtk_object_remove_data_by_id (GTK_OBJECT (ifactory), quark_popup_data);
}

/**
 * gtk_item_factory_popup:
 * @ifactory: a #GtkItemFactory of type #GTK_TYPE_MENU (see gtk_item_factory_new())
 * @x: the x position 
 * @y: the y position
 * @mouse_button: the mouse button which was pressed to initiate this action
 * @time: a timestamp for this action
 *
 * Pops up the menu constructed from the item factory at (@x, @y).
 */
void
gtk_item_factory_popup (GtkItemFactory		*ifactory,
			guint			 x,
			guint			 y,
			guint			 mouse_button,
			guint32			 time)
{
  gtk_item_factory_popup_with_data (ifactory, NULL, NULL, x, y, mouse_button, time);
}

/**
 * gtk_item_factory_popup_with_data:
 * @ifactory: a #GtkItemFactory of type #GTK_TYPE_MENU (see gtk_item_factory_new())
 * @popup_data: data available for callbacks while the menu is posted
 * @destroy: a #GtkDestroyNotify function to be called on @popup_data when
 *  the menu is unposted
 * @x: the x position 
 * @y: the y position
 * @mouse_button: the mouse button which was pressed to initiate this action
 * @time: a timestamp for this action
 *
 * Pops up the menu constructed from the item factory at (@x, @y). Callbacks
 * can access the @popup_data while the menu is posted via 
 * gtk_item_factory_popup_data() and gtk_item_factory_popup_data_from_widget().
 */
void
gtk_item_factory_popup_with_data (GtkItemFactory	*ifactory,
				  gpointer		 popup_data,
				  GtkDestroyNotify	 destroy,
				  guint			 x,
				  guint			 y,
				  guint			 mouse_button,
				  guint32		 time)
{
  MenuPos *mpos;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  g_return_if_fail (GTK_IS_MENU (ifactory->widget));
  
  mpos = gtk_object_get_data_by_id (GTK_OBJECT (ifactory->widget), quark_if_menu_pos);
  
  if (!mpos)
    {
      mpos = g_new0 (MenuPos, 1);
      gtk_object_set_data_by_id_full (GTK_OBJECT (ifactory->widget),
				      quark_if_menu_pos,
				      mpos,
				      g_free);
    }
  
  mpos->x = x;
  mpos->y = y;
  
  if (popup_data != NULL)
    {
      gtk_object_set_data_by_id_full (GTK_OBJECT (ifactory),
				      quark_popup_data,
				      popup_data,
				      destroy);
      gtk_signal_connect (GTK_OBJECT (ifactory->widget),
			  "selection-done",
			  GTK_SIGNAL_FUNC (ifactory_delete_popup_data),
			  ifactory);
    }
  
  gtk_menu_popup (GTK_MENU (ifactory->widget),
		  NULL, NULL,
		  gtk_item_factory_menu_pos, mpos,
		  mouse_button, time);
}

/**
 * gtk_item_factory_set_translate_func:
 * @ifactory: a #GtkItemFactory
 * @func: the #GtkTranslateFunc function to be used to translate path elements 
 * @data: data to pass to @func and @notify
 * @notify: a #GtkDestroyNotify function to be called when @ifactory is 
 *   destroyed and when the translation function is changed again
 * 
 * Sets a function to be used for translating the path elements before they
 * are displayed. 
 */ 
void
gtk_item_factory_set_translate_func (GtkItemFactory      *ifactory,
				     GtkTranslateFunc     func,
				     gpointer             data,
				     GtkDestroyNotify     notify)
{
  g_return_if_fail (ifactory != NULL);
  
  if (ifactory->translate_notify)
    ifactory->translate_notify (ifactory->translate_data);
      
  ifactory->translate_func = func;
  ifactory->translate_data = data;
  ifactory->translate_notify = notify;
}
