gtkitemfactory.c 43.4 KB
Newer Older
Tim Janik's avatar
Tim Janik committed
1 2 3 4 5 6 7
/* 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
8
 * modify it under the terms of the GNU Lesser General Public
Tim Janik's avatar
Tim Janik committed
9 10 11 12 13 14
 * 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
15
 * Lesser General Public License for more details.
Tim Janik's avatar
Tim Janik committed
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
Tim Janik's avatar
Tim Janik committed
18 19 20 21
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
22 23

/*
24
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
25 26 27 28 29
 * 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/. 
 */

30
#include	<config.h>
31

32
#include 	"gtkalias.h"
Tim Janik's avatar
Tim Janik committed
33 34 35 36 37 38
#include	"gtkitemfactory.h"
#include	"gtk/gtkmenubar.h"
#include	"gtk/gtkmenu.h"
#include	"gtk/gtkmenuitem.h"
#include	"gtk/gtkradiomenuitem.h"
#include	"gtk/gtkcheckmenuitem.h"
39
#include	"gtk/gtkimagemenuitem.h"
40
#include	"gtk/gtktearoffmenuitem.h"
41
#include	"gtk/gtkaccelmap.h"
Tim Janik's avatar
Tim Janik committed
42
#include	"gtk/gtkaccellabel.h"
43
#include        "gdk/gdkkeysyms.h"
44 45 46
#include	"gtk/gtkimage.h"
#include	"gtk/gtkstock.h"
#include	"gtk/gtkiconfactory.h"
Tim Janik's avatar
Tim Janik committed
47 48
#include	<string.h>
#include	<fcntl.h>
49
#ifdef HAVE_UNISTD_H
Tim Janik's avatar
Tim Janik committed
50
#include	<unistd.h>
51
#endif
52
#include	<stdio.h>
Tim Janik's avatar
Tim Janik committed
53

Kristian Rietveld's avatar
Kristian Rietveld committed
54 55 56 57
#undef GTK_DISABLE_DEPRECATED
#include	"gtk/gtkoptionmenu.h"
#define GTK_DISABLE_DEPRECATED

Tim Janik's avatar
Tim Janik committed
58 59 60 61 62 63 64
/* --- defines --- */
#define		ITEM_FACTORY_STRING	((gchar*) item_factory_string)
#define		ITEM_BLOCK_SIZE		(128)


/* --- structures --- */
typedef struct	_GtkIFCBData		GtkIFCBData;
65
typedef struct  _GtkIFDumpData		GtkIFDumpData;
Tim Janik's avatar
Tim Janik committed
66 67
struct _GtkIFCBData
{
68 69 70 71
  GtkItemFactoryCallback  func;
  guint			  callback_type;
  gpointer		  func_data;
  guint			  callback_action;
Tim Janik's avatar
Tim Janik committed
72 73 74 75 76 77 78
};


/* --- 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);
79
static void	gtk_item_factory_finalize		(GObject	      *object);
Tim Janik's avatar
Tim Janik committed
80 81 82 83


/* --- static variables --- */
static GtkItemFactoryClass *gtk_item_factory_class = NULL;
84
static gpointer          parent_class = NULL;
Tim Janik's avatar
Tim Janik committed
85 86 87
static const gchar	*item_factory_string = "Gtk-<ItemFactory>";
static GMemChunk	*ifactory_item_chunks = NULL;
static GMemChunk	*ifactory_cb_data_chunks = NULL;
88 89 90
static GQuark		 quark_popup_data = 0;
static GQuark		 quark_if_menu_pos = 0;
static GQuark		 quark_item_factory = 0;
91 92 93
static GQuark		 quark_item_path = 0;
static GQuark		 quark_action = 0;
static GQuark		 quark_accel_group = 0;
94 95 96 97 98
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;
99 100
static GQuark		 quark_type_image_item = 0;
static GQuark		 quark_type_stock_item = 0;
101
static GQuark		 quark_type_tearoff_item = 0;
102 103 104
static GQuark		 quark_type_separator_item = 0;
static GQuark		 quark_type_branch = 0;
static GQuark		 quark_type_last_branch = 0;
Tim Janik's avatar
Tim Janik committed
105 106 107


/* --- functions --- */
Manish Singh's avatar
Manish Singh committed
108
GType
Tim Janik's avatar
Tim Janik committed
109 110
gtk_item_factory_get_type (void)
{
Manish Singh's avatar
Manish Singh committed
111
  static GType item_factory_type = 0;
Tim Janik's avatar
Tim Janik committed
112 113 114
  
  if (!item_factory_type)
    {
Manish Singh's avatar
Manish Singh committed
115
      static const GTypeInfo item_factory_info =
Tim Janik's avatar
Tim Janik committed
116 117
      {
	sizeof (GtkItemFactoryClass),
Manish Singh's avatar
Manish Singh committed
118 119 120 121 122 123 124 125
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_item_factory_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkItemFactory),
	0,
	(GInstanceInitFunc) gtk_item_factory_init,
Tim Janik's avatar
Tim Janik committed
126 127
      };
      
Manish Singh's avatar
Manish Singh committed
128 129 130
      item_factory_type =
	g_type_register_static (GTK_TYPE_OBJECT, "GtkItemFactory",
				&item_factory_info, 0);
Tim Janik's avatar
Tim Janik committed
131 132 133 134 135 136 137 138
    }
  
  return item_factory_type;
}

static void
gtk_item_factory_class_init (GtkItemFactoryClass  *class)
{
139
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
140
  GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
Tim Janik's avatar
Tim Janik committed
141 142

  gtk_item_factory_class = class;
143
  parent_class = g_type_class_peek_parent (class);
Tim Janik's avatar
Tim Janik committed
144

145 146
  gobject_class->finalize = gtk_item_factory_finalize;

Tim Janik's avatar
Tim Janik committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160
  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);

161 162 163 164 165 166 167 168 169 170 171
  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>");
172 173
  quark_type_image_item         = g_quark_from_static_string ("<ImageItem>");
  quark_type_stock_item         = g_quark_from_static_string ("<StockItem>");
174
  quark_type_separator_item	= g_quark_from_static_string ("<Separator>");
175
  quark_type_tearoff_item	= g_quark_from_static_string ("<Tearoff>");
176 177
  quark_type_branch		= g_quark_from_static_string ("<Branch>");
  quark_type_last_branch	= g_quark_from_static_string ("<LastBranch>");
Tim Janik's avatar
Tim Janik committed
178 179 180 181 182 183 184 185 186 187 188 189
}

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

  object = GTK_OBJECT (ifactory);

  ifactory->path = NULL;
  ifactory->accel_group = NULL;
  ifactory->widget = NULL;
190
  ifactory->items = NULL;
191 192 193
  ifactory->translate_func = NULL;
  ifactory->translate_data = NULL;
  ifactory->translate_notify = NULL;
Tim Janik's avatar
Tim Janik committed
194 195
}

Matthias Clasen's avatar
Matthias Clasen committed
196 197 198 199
/**
 * 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
200 201
 * @path: the factory path of the new item factory, a string of the form 
 *    <literal>"&lt;name&gt;"</literal>
Matthias Clasen's avatar
Matthias Clasen committed
202 203 204 205 206
 * @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.
207 208
 *
 * Beware that the returned object does not have a floating reference.
Matthias Clasen's avatar
Matthias Clasen committed
209
 */
Tim Janik's avatar
Tim Janik committed
210
GtkItemFactory*
Manish Singh's avatar
Manish Singh committed
211
gtk_item_factory_new (GType	     container_type,
Tim Janik's avatar
Tim Janik committed
212 213 214 215 216 217 218
		      const gchar   *path,
		      GtkAccelGroup *accel_group)
{
  GtkItemFactory *ifactory;

  g_return_val_if_fail (path != NULL, NULL);

Manish Singh's avatar
Manish Singh committed
219
  ifactory = g_object_new (GTK_TYPE_ITEM_FACTORY, NULL);
Tim Janik's avatar
Tim Janik committed
220 221 222 223 224 225 226 227 228 229 230 231 232
  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;

233 234
  if (data->callback_type == 1)
    {
235
      GtkItemFactoryCallback1 func1 = (GtkItemFactoryCallback1) data->func;
236 237 238 239
      func1 (data->func_data, data->callback_action, widget);
    }
  else if (data->callback_type == 2)
    {
240
      GtkItemFactoryCallback2 func2 = (GtkItemFactoryCallback2) data->func;
241 242
      func2 (widget, data->func_data, data->callback_action);
    }
Tim Janik's avatar
Tim Janik committed
243 244 245 246 247 248 249
}

static void
gtk_item_factory_item_remove_widget (GtkWidget		*widget,
				     GtkItemFactoryItem *item)
{
  item->widgets = g_slist_remove (item->widgets, widget);
Manish Singh's avatar
Manish Singh committed
250 251
  g_object_set_qdata (G_OBJECT (widget), quark_item_factory, NULL);
  g_object_set_qdata (G_OBJECT (widget), quark_item_path, NULL);
Tim Janik's avatar
Tim Janik committed
252 253
}

254 255 256 257 258
/**
 * 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
259 260
 * @keyval:           key value of the accelerator
 * @modifiers:        modifier combination of the accelerator
261 262 263 264 265 266 267
 *
 * 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
268 269 270
 * factory. 
 *
 * Deprecated: The recommended API for this purpose are the functions 
271 272 273
 * 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.
274
 */
275 276 277 278
void
gtk_item_factory_add_foreign (GtkWidget      *accel_widget,
			      const gchar    *full_path,
			      GtkAccelGroup  *accel_group,
279 280
			      guint           keyval,
			      GdkModifierType modifiers)
Tim Janik's avatar
Tim Janik committed
281 282 283 284
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;

285 286
  g_return_if_fail (GTK_IS_WIDGET (accel_widget));
  g_return_if_fail (full_path != NULL);
Tim Janik's avatar
Tim Janik committed
287

288
  class = gtk_type_class (GTK_TYPE_ITEM_FACTORY);
Tim Janik's avatar
Tim Janik committed
289

290
  keyval = keyval != GDK_VoidSymbol ? keyval : 0;
291 292

  item = g_hash_table_lookup (class->item_ht, full_path);
Tim Janik's avatar
Tim Janik committed
293 294 295 296
  if (!item)
    {
      item = g_chunk_new (GtkItemFactoryItem, ifactory_item_chunks);

297
      item->path = g_strdup (full_path);
Tim Janik's avatar
Tim Janik committed
298 299 300 301 302
      item->widgets = NULL;
      
      g_hash_table_insert (class->item_ht, item->path, item);
    }

303
  item->widgets = g_slist_prepend (item->widgets, accel_widget);
Manish Singh's avatar
Manish Singh committed
304 305 306 307
  g_signal_connect (accel_widget,
		    "destroy",
		    G_CALLBACK (gtk_item_factory_item_remove_widget),
		    item);
Tim Janik's avatar
Tim Janik committed
308

309
  /* set the item path for the widget
Tim Janik's avatar
Tim Janik committed
310
   */
Manish Singh's avatar
Manish Singh committed
311
  g_object_set_qdata (G_OBJECT (accel_widget), quark_item_path, item->path);
312 313 314
  gtk_widget_set_name (accel_widget, item->path);
  if (accel_group)
    {
Manish Singh's avatar
Manish Singh committed
315 316 317 318 319
      g_object_ref (accel_group);
      g_object_set_qdata_full (G_OBJECT (accel_widget),
			       quark_accel_group,
			       accel_group,
			       g_object_unref);
320 321
    }
  else
Manish Singh's avatar
Manish Singh committed
322
    g_object_set_qdata (G_OBJECT (accel_widget), quark_accel_group, NULL);
Tim Janik's avatar
Tim Janik committed
323 324 325

  /* install defined accelerators
   */
Manish Singh's avatar
Manish Singh committed
326
  if (g_signal_lookup ("activate", G_TYPE_FROM_INSTANCE (accel_widget)))
Tim Janik's avatar
Tim Janik committed
327
    {
328
      if (accel_group)
329
	{
330
	  gtk_accel_map_add_entry (full_path, keyval, modifiers);
331
	  gtk_widget_set_accel_path (accel_widget, full_path, accel_group);
332
	}
Tim Janik's avatar
Tim Janik committed
333
    }
334
}
Tim Janik's avatar
Tim Janik committed
335

336 337 338 339 340
static void
ifactory_cb_data_free (gpointer mem)
{
  g_mem_chunk_free (ifactory_cb_data_chunks, mem);
}
Tim Janik's avatar
Tim Janik committed
341

342 343 344 345 346 347 348 349 350 351 352 353 354 355
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;
356 357
  guint keyval;
  GdkModifierType mods;
358 359 360 361
  
  g_return_if_fail (widget != NULL);
  g_return_if_fail (item_type != NULL);

362
  class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);
363 364 365 366 367

  /* set accelerator group on menu widgets
   */
  if (GTK_IS_MENU (widget))
    gtk_menu_set_accel_group ((GtkMenu*) widget, ifactory->accel_group);
Tim Janik's avatar
Tim Janik committed
368

Kristian Rietveld's avatar
Kristian Rietveld committed
369
  /* connect callback if necessary
Tim Janik's avatar
Tim Janik committed
370 371 372 373 374 375 376
   */
  if (callback)
    {
      GtkIFCBData *data;

      data = g_chunk_new (GtkIFCBData, ifactory_cb_data_chunks);
      data->func = callback;
377
      data->callback_type = callback_type;
Tim Janik's avatar
Tim Janik committed
378 379 380
      data->func_data = callback_data;
      data->callback_action = callback_action;

Manish Singh's avatar
Manish Singh committed
381 382 383 384 385 386 387
      g_object_weak_ref (G_OBJECT (widget),
			 (GWeakNotify) ifactory_cb_data_free,
			 data);
      g_signal_connect (widget,
			"activate",
			G_CALLBACK (gtk_item_factory_callback_marshal),
			data);
Tim Janik's avatar
Tim Janik committed
388
    }
389 390 391 392

  /* link the widget into its item-entry
   * and keep back pointer on both the item factory and the widget
   */
Manish Singh's avatar
Manish Singh committed
393 394
  g_object_set_qdata (G_OBJECT (widget), quark_action, GUINT_TO_POINTER (callback_action));
  g_object_set_qdata (G_OBJECT (widget), quark_item_factory, ifactory);
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
  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);
Tim Janik's avatar
Tim Janik committed
411 412
}

Matthias Clasen's avatar
Matthias Clasen committed
413 414 415 416 417
/**
 * 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
418 419
 * @path: the factory path of @ifactory, a string of the form 
 *    <literal>"&lt;name&gt;"</literal>
Matthias Clasen's avatar
Matthias Clasen committed
420 421 422 423 424
 * @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.
 */  
Tim Janik's avatar
Tim Janik committed
425 426
void
gtk_item_factory_construct (GtkItemFactory	*ifactory,
Manish Singh's avatar
Manish Singh committed
427
			    GType		 container_type,
Tim Janik's avatar
Tim Janik committed
428 429 430 431 432 433 434 435
			    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);
Manish Singh's avatar
Manish Singh committed
436 437
  if (!g_type_is_a (container_type, GTK_TYPE_OPTION_MENU))
    g_return_if_fail (g_type_is_a (container_type, GTK_TYPE_MENU_SHELL));
Tim Janik's avatar
Tim Janik committed
438 439 440 441 442 443 444 445 446 447 448 449

  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;
Manish Singh's avatar
Manish Singh committed
450
      g_object_ref (ifactory->accel_group);
Tim Janik's avatar
Tim Janik committed
451 452 453 454 455
    }
  else
    ifactory->accel_group = gtk_accel_group_new ();

  ifactory->path = g_strdup (path);
Tim Janik's avatar
Tim Janik committed
456 457 458
  ifactory->widget = g_object_connect (gtk_widget_new (container_type, NULL),
				       "signal::destroy", gtk_widget_destroyed, &ifactory->widget,
				       NULL);
Manish Singh's avatar
Manish Singh committed
459
  g_object_ref (ifactory);
Tim Janik's avatar
Tim Janik committed
460
  gtk_object_sink (GTK_OBJECT (ifactory));
461

Tim Janik's avatar
Tim Janik committed
462 463
  gtk_item_factory_add_item (ifactory,
			     "", NULL,
464
			     NULL, 0, NULL, 0,
Tim Janik's avatar
Tim Janik committed
465 466 467 468
			     ITEM_FACTORY_STRING,
			     ifactory->widget);
}

Matthias Clasen's avatar
Matthias Clasen committed
469 470
/**
 * gtk_item_factory_from_path:
471 472
 * @path: a string starting with a factory path of the form 
 *   <literal>"&lt;name&gt;"</literal>
Matthias Clasen's avatar
Matthias Clasen committed
473 474
 * @returns: the #GtkItemFactory created for the given factory path, or %NULL 
 *
475 476 477
 * 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().
Matthias Clasen's avatar
Matthias Clasen committed
478
 */
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
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);
506 507 508

  g_free (fname);

509 510 511 512 513 514
  if (item && item->widgets)
    return gtk_item_factory_from_widget (item->widgets->data);

  return NULL;
}

Tim Janik's avatar
Tim Janik committed
515
static void
516
gtk_item_factory_destroy (GtkObject *object)
Tim Janik's avatar
Tim Janik committed
517 518 519 520 521 522 523 524 525 526
{
  GtkItemFactory *ifactory;
  GSList *slist;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (object));

  ifactory = (GtkItemFactory*) object;

  if (ifactory->widget)
    {
527
      GtkObject *dobj;
Tim Janik's avatar
Tim Janik committed
528

529
      dobj = GTK_OBJECT (ifactory->widget);
Tim Janik's avatar
Tim Janik committed
530

Manish Singh's avatar
Manish Singh committed
531
      g_object_ref (dobj);
532 533
      gtk_object_sink (dobj);
      gtk_object_destroy (dobj);
Manish Singh's avatar
Manish Singh committed
534
      g_object_unref (dobj);
Tim Janik's avatar
Tim Janik committed
535 536 537 538

      ifactory->widget = NULL;
    }

539 540 541 542 543 544
  for (slist = ifactory->items; slist; slist = slist->next)
    {
      GtkItemFactoryItem *item = slist->data;
      GSList *link;
      
      for (link = item->widgets; link; link = link->next)
Manish Singh's avatar
Manish Singh committed
545 546
	if (g_object_get_qdata (link->data, quark_item_factory) == ifactory)
	  g_object_set_qdata (link->data, quark_item_factory, NULL);
547 548 549
    }
  g_slist_free (ifactory->items);
  ifactory->items = NULL;
Tim Janik's avatar
Tim Janik committed
550

551
  GTK_OBJECT_CLASS (parent_class)->destroy (object);
Tim Janik's avatar
Tim Janik committed
552 553 554
}

static void
555
gtk_item_factory_finalize (GObject *object)
Tim Janik's avatar
Tim Janik committed
556 557 558 559 560 561 562
{
  GtkItemFactory *ifactory;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (object));

  ifactory = GTK_ITEM_FACTORY (object);

Manish Singh's avatar
Manish Singh committed
563
  g_object_unref (ifactory->accel_group);
Tim Janik's avatar
Tim Janik committed
564 565 566
  g_free (ifactory->path);
  g_assert (ifactory->widget == NULL);

567
  if (ifactory->translate_notify)
568 569
    ifactory->translate_notify (ifactory->translate_data);
  
570
  G_OBJECT_CLASS (parent_class)->finalize (object);
Tim Janik's avatar
Tim Janik committed
571 572
}

Matthias Clasen's avatar
Matthias Clasen committed
573 574 575 576 577 578 579
/**
 * 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.
 */
Tim Janik's avatar
Tim Janik committed
580 581 582
GtkItemFactory*
gtk_item_factory_from_widget (GtkWidget	       *widget)
{
583 584
  GtkItemFactory *ifactory;

Tim Janik's avatar
Tim Janik committed
585 586
  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

Manish Singh's avatar
Manish Singh committed
587 588
  ifactory = g_object_get_qdata (G_OBJECT (widget), quark_item_factory);

589 590 591 592
  if (ifactory == NULL && GTK_IS_MENU_ITEM (widget) &&
      GTK_MENU_ITEM (widget)->submenu != NULL) 
    {
      GtkWidget *menu = GTK_MENU_ITEM (widget)->submenu;
Manish Singh's avatar
Manish Singh committed
593
      ifactory = g_object_get_qdata (G_OBJECT (menu), quark_item_factory);
594 595 596
    }

  return ifactory;
Tim Janik's avatar
Tim Janik committed
597 598
}

Matthias Clasen's avatar
Matthias Clasen committed
599 600 601
/**
 * gtk_item_factory_path_from_widget:
 * @widget: a widget
602 603
 * @returns: the full path to @widget if it has been created by an item
 *   factory, %NULL otherwise. This value is owned by GTK+ and must not be
604
 *   modified or freed.
Matthias Clasen's avatar
Matthias Clasen committed
605 606 607 608 609 610
 * 
 * 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.)
 */
611
G_CONST_RETURN gchar*
Tim Janik's avatar
Tim Janik committed
612 613
gtk_item_factory_path_from_widget (GtkWidget	    *widget)
{
614 615
  gchar* path;

Tim Janik's avatar
Tim Janik committed
616 617
  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

Manish Singh's avatar
Manish Singh committed
618
  path = g_object_get_qdata (G_OBJECT (widget), quark_item_path);
619 620 621 622 623

  if (path == NULL && GTK_IS_MENU_ITEM (widget) &&
      GTK_MENU_ITEM (widget)->submenu != NULL) 
    {
      GtkWidget *menu = GTK_MENU_ITEM (widget)->submenu;
Manish Singh's avatar
Manish Singh committed
624
      path = g_object_get_qdata (G_OBJECT (menu), quark_item_path);
625 626 627
    }

  return path;
Tim Janik's avatar
Tim Janik committed
628 629
}

Matthias Clasen's avatar
Matthias Clasen committed
630 631 632 633
/**
 * gtk_item_factory_create_items:
 * @ifactory: a #GtkItemFactory
 * @n_entries: the length of @entries
634
 * @entries: an array of #GtkItemFactoryEntry<!-- -->s whose @callback members
Matthias Clasen's avatar
Matthias Clasen committed
635 636 637 638 639
 *    must by of type #GtkItemFactoryCallback1
 * @callback_data: data passed to the callback functions of all entries
 *
 * Creates the menu items from the @entries.
 */
Tim Janik's avatar
Tim Janik committed
640
void
641 642 643 644
gtk_item_factory_create_items (GtkItemFactory	   *ifactory,
			       guint		    n_entries,
			       GtkItemFactoryEntry *entries,
			       gpointer		    callback_data)
645 646 647 648
{
  gtk_item_factory_create_items_ac (ifactory, n_entries, entries, callback_data, 1);
}

Matthias Clasen's avatar
Matthias Clasen committed
649 650 651 652
/**
 * gtk_item_factory_create_items_ac:
 * @ifactory: a #GtkItemFactory
 * @n_entries: the length of @entries
653
 * @entries: an array of #GtkItemFactoryEntry<!-- -->s 
Matthias Clasen's avatar
Matthias Clasen committed
654
 * @callback_data: data passed to the callback functions of all entries
655
 * @callback_type: 1 if the callback functions in @entries are of type
Matthias Clasen's avatar
Matthias Clasen committed
656 657 658 659
 *    #GtkItemFactoryCallback1, 2 if they are of type #GtkItemFactoryCallback2 
 *
 * Creates the menu items from the @entries.
 */
660
void
661 662 663 664 665
gtk_item_factory_create_items_ac (GtkItemFactory      *ifactory,
				  guint		       n_entries,
				  GtkItemFactoryEntry *entries,
				  gpointer	       callback_data,
				  guint		       callback_type)
Tim Janik's avatar
Tim Janik committed
666 667 668 669
{
  guint i;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
670 671 672 673 674
  g_return_if_fail (callback_type >= 1 && callback_type <= 2);

  if (n_entries == 0)
    return;

Tim Janik's avatar
Tim Janik committed
675 676 677
  g_return_if_fail (entries != NULL);

  for (i = 0; i < n_entries; i++)
678
    gtk_item_factory_create_item (ifactory, entries + i, callback_data, callback_type);
Tim Janik's avatar
Tim Janik committed
679 680
}

Matthias Clasen's avatar
Matthias Clasen committed
681 682 683 684 685 686 687 688 689 690 691 692 693
/**
 * 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.
 */
Tim Janik's avatar
Tim Janik committed
694
GtkWidget*
695 696
gtk_item_factory_get_widget (GtkItemFactory *ifactory,
			     const gchar    *path)
Tim Janik's avatar
Tim Janik committed
697 698 699 700 701 702 703
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;

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

704
  class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);
Tim Janik's avatar
Tim Janik committed
705

706 707 708 709 710 711 712 713 714 715
  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);
    }
Tim Janik's avatar
Tim Janik committed
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730

  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;
}

Matthias Clasen's avatar
Matthias Clasen committed
731 732 733 734 735 736 737 738
/**
 * 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
 *
739
 * Obtains the widget which was constructed from the #GtkItemFactoryEntry
Matthias Clasen's avatar
Matthias Clasen committed
740
 * with the given @action.
741 742 743
 *
 * If there are multiple items with the same action, the result is 
 * undefined.
Matthias Clasen's avatar
Matthias Clasen committed
744
 */
Tim Janik's avatar
Tim Janik committed
745
GtkWidget*
746 747
gtk_item_factory_get_widget_by_action (GtkItemFactory *ifactory,
				       guint	       action)
Tim Janik's avatar
Tim Janik committed
748 749 750 751 752
{
  GSList *slist;

  g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);

753
  for (slist = ifactory->items; slist; slist = slist->next)
Tim Janik's avatar
Tim Janik committed
754
    {
755 756
      GtkItemFactoryItem *item = slist->data;
      GSList *link;
Tim Janik's avatar
Tim Janik committed
757

758
      for (link = item->widgets; link; link = link->next)
Manish Singh's avatar
Manish Singh committed
759 760
	if (g_object_get_qdata (link->data, quark_item_factory) == ifactory &&
	    g_object_get_qdata (link->data, quark_action) == GUINT_TO_POINTER (action))
761
	  return link->data;
Tim Janik's avatar
Tim Janik committed
762 763 764 765 766
    }

  return NULL;
}

Matthias Clasen's avatar
Matthias Clasen committed
767 768 769 770 771 772 773 774 775 776 777 778 779
/** 
 * 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.
 */
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
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;
}

Matthias Clasen's avatar
Matthias Clasen committed
797 798 799 800 801 802 803 804 805 806 807 808

/**
 * 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.
 */
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
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;
}

825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
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;
}

879
static gboolean
880 881 882 883 884
gtk_item_factory_parse_path (GtkItemFactory *ifactory,
			     gchar          *str,
			     gchar         **path,
			     gchar         **parent_path,
			     gchar         **item)
885
{
886
  gchar *translation;
887
  gchar *p, *q;
888
  
889 890 891 892 893
  *path = g_strdup (str);

  p = q = *path;
  while (*p)
    {
894 895 896 897 898 899 900 901 902
      if (*p == '_')
	{
	  if (p[1] == '_')
	    {
	      p++;
	      *q++ = '_';
	    }
	}
      else
903 904 905 906 907 908 909 910
	{
	  *q++ = *p;
	}
      p++;
    }
  *q = 0;

  *parent_path = g_strdup (*path);
911
  p = item_factory_find_separator_r (*parent_path);
912 913 914 915 916 917 918
  if (!p)
    {
      g_warning ("GtkItemFactory: invalid entry path `%s'", str);
      return FALSE;
    }
  *p = 0;

919 920 921 922 923
  if (ifactory->translate_func)
    translation = ifactory->translate_func (str, ifactory->translate_data);
  else
    translation = str;
			      
924
  p = item_factory_find_separator_r (translation);
925 926 927 928
  if (p)
    p++;
  else
    p = translation;
929

930
  *item = item_factory_unescape_label (p);
931 932 933 934

  return TRUE;
}

Matthias Clasen's avatar
Matthias Clasen committed
935 936 937 938 939
/**
 * 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
940
 * @callback_type: 1 if the callback function of @entry is of type
Matthias Clasen's avatar
Matthias Clasen committed
941 942 943 944
 *    #GtkItemFactoryCallback1, 2 if it is of type #GtkItemFactoryCallback2 
 *
 * Creates an item for @entry.
 */
Tim Janik's avatar
Tim Janik committed
945 946 947
void
gtk_item_factory_create_item (GtkItemFactory	     *ifactory,
			      GtkItemFactoryEntry    *entry,
948 949
			      gpointer		      callback_data,
			      guint		      callback_type)
Tim Janik's avatar
Tim Janik committed
950
{
951
  GtkOptionMenu *option_menu = NULL;
Tim Janik's avatar
Tim Janik committed
952 953
  GtkWidget *parent;
  GtkWidget *widget;
954
  GtkWidget *image;
Tim Janik's avatar
Tim Janik committed
955
  GSList *radio_group;
956
  gchar *name;
Tim Janik's avatar
Tim Janik committed
957
  gchar *parent_path;
958
  gchar *path;
959
  gchar *accelerator;
Tim Janik's avatar
Tim Janik committed
960
  guint type_id;
Manish Singh's avatar
Manish Singh committed
961
  GType type;
962
  gchar *item_type_path;
963 964
  GtkStockItem stock_item;
      
Tim Janik's avatar
Tim Janik committed
965 966 967
  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  g_return_if_fail (entry != NULL);
  g_return_if_fail (entry->path != NULL);
968
  g_return_if_fail (entry->path[0] == '/');
969
  g_return_if_fail (callback_type >= 1 && callback_type <= 2);
Tim Janik's avatar
Tim Janik committed
970 971

  if (!entry->item_type ||
972 973
      entry->item_type[0] == 0)
    {
974
      item_type_path = "<Item>";
975
      type_id = quark_type_item;
976
    }
Tim Janik's avatar
Tim Janik committed
977
  else
978 979
    {
      item_type_path = entry->item_type;
Manish Singh's avatar
Manish Singh committed
980
      type_id = g_quark_try_string (item_type_path);
981
    }
Tim Janik's avatar
Tim Janik committed
982 983

  radio_group = NULL;
984
  if (type_id == quark_type_item)
985
    type = GTK_TYPE_MENU_ITEM;
986
  else if (type_id == quark_type_title)
987
    type = GTK_TYPE_MENU_ITEM;
988
  else if (type_id == quark_type_radio_item)
989
    type = GTK_TYPE_RADIO_MENU_ITEM;
990
  else if (type_id == quark_type_check_item)
991
    type = GTK_TYPE_CHECK_MENU_ITEM;
992 993 994 995
  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;
996 997
  else if (type_id == quark_type_tearoff_item)
    type = GTK_TYPE_TEAROFF_MENU_ITEM;
998
  else if (type_id == quark_type_toggle_item)
999
    type = GTK_TYPE_CHECK_MENU_ITEM;
1000
  else if (type_id == quark_type_separator_item)
1001
    type = GTK_TYPE_MENU_ITEM;
1002
  else if (type_id == quark_type_branch)
1003
    type = GTK_TYPE_MENU_ITEM;
1004
  else if (type_id == quark_type_last_branch)
1005
    type = GTK_TYPE_MENU_ITEM;
Tim Janik's avatar
Tim Janik committed
1006 1007 1008 1009
  else
    {
      GtkWidget *radio_link;

1010
      radio_link = gtk_item_factory_get_widget (ifactory, item_type_path);
Tim Janik's avatar
Tim Janik committed
1011 1012
      if (radio_link && GTK_IS_RADIO_MENU_ITEM (radio_link))
	{
1013
	  type = GTK_TYPE_RADIO_MENU_ITEM;
Manish Singh's avatar
Manish Singh committed
1014
	  radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (radio_link));
Tim Janik's avatar
Tim Janik committed
1015 1016 1017 1018 1019
	}
      else
	{
	  g_warning ("GtkItemFactory: entry path `%s' has invalid type `%s'",
		     entry->path,
1020
		     item_type_path);
Tim Janik's avatar
Tim Janik committed
1021 1022 1023 1024
	  return;
	}
    }

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

Tim Janik's avatar
Tim Janik committed
1029 1030 1031 1032
  parent = gtk_item_factory_get_widget (ifactory, parent_path);
  if (!parent)
    {
      GtkItemFactoryEntry pentry;
1033
      gchar *ppath, *p;
Tim Janik's avatar
Tim Janik committed
1034

1035
      ppath = g_strdup (entry->path);
1036
      p = item_factory_find_separator_r (ppath);
1037 1038 1039
      g_return_if_fail (p != NULL);
      *p = 0;
      pentry.path = ppath;
Tim Janik's avatar
Tim Janik committed
1040 1041 1042 1043 1044
      pentry.accelerator = NULL;
      pentry.callback = NULL;
      pentry.callback_action = 0;
      pentry.item_type = "<Branch>";

1045
      gtk_item_factory_create_item (ifactory, &pentry, NULL, 1);
1046
      g_free (ppath);
Tim Janik's avatar
Tim Janik committed
1047 1048

      parent = gtk_item_factory_get_widget (ifactory, parent_path);
1049
      g_return_if_fail (parent != NULL);
Tim Janik's avatar
Tim Janik committed
1050 1051
    }

1052 1053 1054 1055
  if (GTK_IS_OPTION_MENU (parent))
    {
      option_menu = GTK_OPTION_MENU (parent);
      if (!option_menu->menu)
1056 1057 1058 1059 1060 1061 1062 1063
	{
	  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);
	}
1064 1065
      parent = option_menu->menu;
    }
1066
  g_free (parent_path);
1067
			      
1068
  g_return_if_fail (GTK_IS_CONTAINER (parent));
1069

1070 1071
  accelerator = entry->accelerator;
  
Tim Janik's avatar
Tim Janik committed
1072
  widget = gtk_widget_new (type,
1073 1074 1075 1076
			   "visible", TRUE,
			   "sensitive", (type_id != quark_type_separator_item &&
					 type_id != quark_type_title),
			   "parent", parent,
Tim Janik's avatar
Tim Janik committed
1077
			   NULL);
1078 1079
  if (option_menu && !option_menu->menu_item)
    gtk_option_menu_set_history (option_menu, 0);
Tim Janik's avatar
Tim Janik committed
1080

1081
  if (GTK_IS_RADIO_MENU_ITEM (widget))
Tim Janik's avatar
Tim Janik committed
1082
    gtk_radio_menu_item_set_group (GTK_RADIO_MENU_ITEM (widget), radio_group);
1083
  if (type_id == quark_type_image_item)
1084 1085
    {
      GdkPixbuf *pixbuf = NULL;
1086
      image = NULL;
1087
      if (entry->extra_data)
1088
	{
1089 1090 1091 1092 1093 1094
	  pixbuf = gdk_pixbuf_new_from_inline (-1,
					       entry->extra_data,
					       FALSE,
					       NULL);
	  if (pixbuf)
	    image = gtk_image_new_from_pixbuf (pixbuf);
1095
	}
1096
      if (image)
1097 1098 1099 1100
	{
	  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (widget), image);
	  gtk_widget_show (image);
	}
1101
      if (pixbuf)
Manish Singh's avatar
Manish Singh committed
1102
	g_object_unref (pixbuf);
1103 1104 1105 1106
    }
  if (type_id == quark_type_stock_item)
    {
      image = gtk_image_new_from_stock (entry->extra_data, GTK_ICON_SIZE_MENU);
1107
      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (widget), image);
1108 1109
      gtk_widget_show (image);

1110 1111 1112 1113 1114 1115
      if (gtk_stock_lookup (entry->extra_data, &stock_item))
	{
	  if (!accelerator)
	    accelerator = gtk_accelerator_name (stock_item.keyval, stock_item.modifier);
	}
    }
1116 1117 1118 1119 1120

  /* install underline accelerators for this item
   */
  if (type_id != quark_type_separator_item && 
      type_id != quark_type_tearoff_item &&
1121
      *name)
Tim Janik's avatar
Tim Janik committed
1122 1123
    {
      GtkWidget *label;
1124 1125
      
      label = gtk_widget_new (GTK_TYPE_ACCEL_LABEL,
1126 1127 1128 1129
			      "visible", TRUE,
			      "parent", widget,
			      "accel_widget", widget,
			      "xalign", 0.0,
1130
			      NULL);
1131
      gtk_label_set_text_with_mnemonic (GTK_LABEL (label), name);
Tim Janik's avatar
Tim Janik committed
1132
    }
1133
  
1134
  g_free (name);
1135
  
1136 1137
  if (type_id == quark_type_branch ||
      type_id == quark_type_last_branch)
Tim Janik's avatar
Tim Janik committed
1138
    {
1139 1140
      gchar *p;

1141 1142 1143
      if (entry->callback)
	g_warning ("gtk_item_factory_create_item(): Can't specify a callback on a branch: \"%s\"",
		   entry->path);
1144
      if (type_id == quark_type_last_branch)
1145
	gtk_menu_item_set_right_justified (GTK_MENU_ITEM (widget), TRUE);
1146
      
Tim Janik's avatar
Tim Janik committed
1147
      parent = widget;
1148 1149 1150 1151
      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);
1152
      
Tim Janik's avatar
Tim Janik committed
1153 1154 1155 1156
      gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), widget);
    }	   
  
  gtk_item_factory_add_item (ifactory,
1157
			     path, accelerator,
1158
			     (type_id == quark_type_branch ||
1159 1160
			      type_id == quark_type_last_branch) ?
			      (GtkItemFactoryCallback) NULL : entry->callback,
1161
			     entry->callback_action, callback_data,
1162
			     callback_type,
1163
			     item_type_path,
Tim Janik's avatar
Tim Janik committed
1164
			     widget);
1165 1166
  if (accelerator != entry->accelerator)
    g_free (accelerator);
1167
  g_free (path);
Tim Janik's avatar
Tim Janik committed
1168 1169
}

Matthias Clasen's avatar
Matthias Clasen committed
1170 1171 1172
/**
 * gtk_item_factory_create_menu_entries:
 * @n_entries: the length of @entries
1173
 * @entries: an array of #GtkMenuEntry<!-- -->s 
Matthias Clasen's avatar
Matthias Clasen committed
1174 1175 1176
 *
 * Creates the menu items from the @entries.
 */
Tim Janik's avatar
Tim Janik committed
1177
void
1178 1179 1180
gtk_item_factory_create_menu_entries (guint              n_entries,
				      GtkMenuEntry      *entries)
{
1181 1182
  static GPatternSpec *pspec_separator = NULL;
  static GPatternSpec *pspec_check = NULL;
1183 1184 1185 1186 1187 1188
  guint i;

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

1189
  if (!pspec_separator)
1190
    {
1191 1192
      pspec_separator = g_pattern_spec_new ("*<separator>*");
      pspec_check = g_pattern_spec_new ("*<check>*");
1193
    }
1194 1195 1196 1197 1198 1199

  for (i = 0; i < n_entries; i++)
    {
      GtkItemFactory *ifactory;
      GtkItemFactoryEntry entry;
      gchar *path;
1200
      gchar *cpath;
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214

      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++;
1215
      cpath = NULL;
1216 1217 1218 1219 1220

      entry.path = path;
      entry.accelerator = entries[i].accelerator;
      entry.callback = entries[i].callback;
      entry.callback_action = 0;
1221
      if (g_pattern_match_string (pspec_separator, path))
1222
	entry.item_type = "<Separator>";
1223
      else if (!g_pattern_match_string (pspec_check, path))
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242
	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;
1243
	  entry.item_type = "<ToggleItem>";
1244 1245 1246
	  entry.path = cpath;
	}
      
1247 1248
      gtk_item_factory_create_item (ifactory, &entry, entries[i].callback_data, 2);
      entries[i].widget = gtk_item_factory_get_widget (ifactory, entries[i].path);
1249
      g_free (cpath);
1250 1251 1252
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
1253 1254 1255 1256 1257 1258 1259 1260
/**
 * 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.
 */
1261 1262 1263
void
gtk_item_factories_path_delete (const gchar *ifactory_path,
				const gchar *path)
Tim Janik's avatar
Tim Janik committed
1264 1265 1266 1267 1268 1269 1270 1271
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;

  g_return_if_fail (path != NULL);

  class = gtk_type_class (GTK_TYPE_ITEM_FACTORY);

1272 1273 1274 1275 1276
  if (path[0] == '<')
    item = g_hash_table_lookup (class->item_ht, (gpointer) path);
  else
    {
      gchar *fpath;
Tim Janik's avatar
Tim Janik committed
1277

1278 1279 1280 1281 1282 1283 1284
      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);
    }
  
Tim Janik's avatar
Tim Janik committed
1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
  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);
Manish Singh's avatar
Manish Singh committed
1297
	  g_object_ref (widget);
Tim Janik's avatar
Tim Janik committed
1298 1299 1300 1301 1302 1303 1304 1305
	}

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

	  widget = slist->data;
	  gtk_widget_destroy (widget);
Manish Singh's avatar
Manish Singh committed
1306
	  g_object_unref (widget);
Tim Janik's avatar
Tim Janik committed
1307 1308 1309 1310 1311
	}
      g_slist_free (widget_list);
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
1312 1313 1314 1315 1316 1317 1318 1319
/**
 * 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.
 */
Tim Janik's avatar
Tim Janik committed
1320 1321 1322 1323 1324
void
gtk_item_factory_delete_item (GtkItemFactory         *ifactory,
			      const gchar            *path)
{
  GtkItemFactoryClass *class;
1325
  GtkWidget *widget;
Tim Janik's avatar
Tim Janik committed
1326 1327 1328 1329

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

1330
  class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);
Tim Janik's avatar
Tim Janik committed
1331

1332
  widget = gtk_item_factory_get_widget (ifactory, path);
Tim Janik's avatar
Tim Janik committed
1333

1334
  if (widget)
Tim Janik's avatar
Tim Janik committed
1335
    {
1336 1337
      if (GTK_IS_MENU (widget))
	widget = gtk_menu_get_attach_widget (GTK_MENU (widget));
Tim Janik's avatar
Tim Janik committed
1338

1339
      gtk_widget_destroy (widget);
Tim Janik's avatar
Tim Janik committed
1340 1341 1342
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
1343 1344 1345 1346 1347 1348 1349 1350
/**
 * 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.
 */
Tim Janik's avatar
Tim Janik committed
1351 1352 1353 1354
void
gtk_item_factory_delete_entry (GtkItemFactory         *ifactory,
			       GtkItemFactoryEntry    *entry)
{
1355 1356 1357 1358
  gchar *path;
  gchar *parent_path;
  gchar *name;

Tim Janik's avatar
Tim Janik committed
1359 1360
  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  g_return_if_fail (entry != NULL);
1361 1362 1363 1364 1365 1366 1367 1368
  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);
Tim Janik's avatar
Tim Janik committed
1369

1370 1371 1372
  g_free (path);
  g_free (parent_path);
  g_free (name);
Tim Janik's avatar
Tim Janik committed
1373 1374
}

Matthias Clasen's avatar
Matthias Clasen committed
1375 1376
/**
 * gtk_item_factory_delete_entries:
1377
 * @ifactory: a #GtkItemFactory
Matthias Clasen's avatar
Matthias Clasen committed
1378
 * @n_entries: the length of @entries
1379
 * @entries: an array of #GtkItemFactoryEntry<!-- -->s 
Matthias Clasen's avatar
Matthias Clasen committed
1380 1381 1382 1383
 *
 * Deletes the menu items which were created from the @entries by the given
 * item factory.
 */
Tim Janik's avatar
Tim Janik committed
1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
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++)
1396
    gtk_item_factory_delete_entry (ifactory, entries + i);
Tim Janik's avatar
Tim Janik committed
1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
}

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

static void
gtk_item_factory_menu_pos (GtkMenu  *menu,
			   gint     *x,
			   gint     *y,
Havoc Pennington's avatar
Havoc Pennington committed
1409
                           gboolean *push_in,
Tim Janik's avatar
Tim Janik committed
1410 1411 1412 1413 1414 1415 1416 1417
			   gpointer  func_data)
{
  MenuPos *mpos = func_data;

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

Matthias Clasen's avatar
Matthias Clasen committed
1418 1419
/**
 * gtk_item_factory_popup_data_from_widget:
1420
 * @widget: a widget
Matthias Clasen's avatar
Matthias Clasen committed
1421 1422 1423 1424 1425 1426 1427 1428
 * @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.
 */
Tim Janik's avatar
Tim Janik committed
1429
gpointer
1430
gtk_item_factory_popup_data_from_widget (GtkWidget *widget)
Tim Janik's avatar
Tim Janik committed
1431 1432 1433 1434 1435 1436 1437
{
  GtkItemFactory *ifactory;
  
  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

  ifactory = gtk_item_factory_from_widget (widget);
  if (ifactory)
Manish Singh's avatar
Manish Singh committed
1438
    return g_object_get_qdata (G_OBJECT (ifactory), quark_popup_data);
Tim Janik's avatar
Tim Janik committed
1439 1440 1441 1442

  return NULL;
}

Matthias Clasen's avatar
Matthias Clasen committed
1443 1444 1445 1446 1447 1448 1449 1450 1451
/**
 * 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.
 */
Tim Janik's avatar
Tim Janik committed
1452 1453 1454 1455 1456
gpointer
gtk_item_factory_popup_data (GtkItemFactory *ifactory)
{
  g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);

Manish Singh's avatar
Manish Singh committed
1457
  return g_object_get_qdata (G_OBJECT (ifactory), quark_popup_data);
Tim Janik's avatar
Tim Janik committed
1458 1459 1460 1461 1462 1463
}

static void
ifactory_delete_popup_data (GtkObject	   *object,
			    GtkItemFactory *ifactory)
{
Manish Singh's avatar
Manish Singh committed
1464 1465 1466 1467
  g_signal_handlers_disconnect_by_func (object,
					ifactory_delete_popup_data,
					ifactory);
  g_object_set_qdata (G_OBJECT (ifactory), quark_popup_data, NULL);
Tim Janik's avatar
Tim Janik committed
1468 1469
}

Matthias Clasen's avatar
Matthias Clasen committed
1470 1471
/**
 * gtk_item_factory_popup:
1472
 * @ifactory: a #GtkItemFactory of type #GTK_TYPE_MENU (see gtk_item_factory_new())
Matthias Clasen's avatar
Matthias Clasen committed
1473 1474
 * @x: the x position 
 * @y: the y position
Soeren Sandmann's avatar
Soeren Sandmann committed
1475
 * @mouse_button: the mouse button which was pressed to initiate the popup
1476
 * @time_: the time at which the activation event occurred
Matthias Clasen's avatar
Matthias Clasen committed
1477 1478
 *
 * Pops up the menu constructed from the item factory at (@x, @y).
Soeren Sandmann's avatar
Soeren Sandmann committed
1479 1480 1481 1482 1483 1484
 *
 * The @mouse_button parameter should be the mouse button pressed to initiate
 * the menu popup. If the menu popup was initiated by something other than
 * a mouse button press, such as a mouse button release or a keypress,
 * @mouse_button should be 0.
 *
1485
 * The @time_ parameter should be the time stamp of the event that
Soeren Sandmann's avatar
Soeren Sandmann committed
1486 1487 1488
 * initiated the popup. If such an event is not available, use
 * gtk_get_current_event_time() instead.
 *
1489
 * The operation of the @mouse_button and the @time_ parameter is the same
Soeren Sandmann's avatar
Soeren Sandmann committed
1490
 * as the @button and @activation_time parameters for gtk_menu_popup().
Matthias Clasen's avatar
Matthias Clasen committed
1491
 */
Tim Janik's avatar
Tim Janik committed
1492 1493 1494 1495 1496 1497 1498 1499 1500 1501
void
gtk_item_factory_popup (GtkItemFactory		*ifactory,
			guint			 x,
			guint			 y,
			guint			 mouse_button,
			guint32			 time)
{