gtkitemfactory.c 42.9 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Tim Janik's avatar
Tim Janik committed
2 3 4 5 6 7
 * 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

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

56
#include "gtkalias.h"
Kristian Rietveld's avatar
Kristian Rietveld committed
57

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


/* --- prototypes --- */
static void	gtk_item_factory_destroy		(GtkObject	      *object);
77
static void	gtk_item_factory_finalize		(GObject	      *object);
Tim Janik's avatar
Tim Janik committed
78 79 80


/* --- static variables --- */
81
static const gchar	 item_factory_string[] = "Gtk-<ItemFactory>";
82 83 84
static GQuark		 quark_popup_data = 0;
static GQuark		 quark_if_menu_pos = 0;
static GQuark		 quark_item_factory = 0;
85 86 87
static GQuark		 quark_item_path = 0;
static GQuark		 quark_action = 0;
static GQuark		 quark_accel_group = 0;
88 89 90 91 92
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;
93 94
static GQuark		 quark_type_image_item = 0;
static GQuark		 quark_type_stock_item = 0;
95
static GQuark		 quark_type_tearoff_item = 0;
96 97 98
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
99

Matthias Clasen's avatar
Matthias Clasen committed
100
G_DEFINE_TYPE (GtkItemFactory, gtk_item_factory, GTK_TYPE_OBJECT)
Tim Janik's avatar
Tim Janik committed
101 102 103 104 105

/* --- functions --- */
static void
gtk_item_factory_class_init (GtkItemFactoryClass  *class)
{
106
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
107
  GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
Tim Janik's avatar
Tim Janik committed
108

109 110
  gobject_class->finalize = gtk_item_factory_finalize;

Tim Janik's avatar
Tim Janik committed
111 112 113 114
  object_class->destroy = gtk_item_factory_destroy;

  class->item_ht = g_hash_table_new (g_str_hash, g_str_equal);

115 116 117 118 119 120 121 122 123 124 125
  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>");
126 127
  quark_type_image_item         = g_quark_from_static_string ("<ImageItem>");
  quark_type_stock_item         = g_quark_from_static_string ("<StockItem>");
128
  quark_type_separator_item	= g_quark_from_static_string ("<Separator>");
129
  quark_type_tearoff_item	= g_quark_from_static_string ("<Tearoff>");
130 131
  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
132 133 134 135 136 137 138 139
}

static void
gtk_item_factory_init (GtkItemFactory	    *ifactory)
{
  ifactory->path = NULL;
  ifactory->accel_group = NULL;
  ifactory->widget = NULL;
140
  ifactory->items = NULL;
141 142 143
  ifactory->translate_func = NULL;
  ifactory->translate_data = NULL;
  ifactory->translate_notify = NULL;
Tim Janik's avatar
Tim Janik committed
144 145
}

Matthias Clasen's avatar
Matthias Clasen committed
146 147 148 149
/**
 * 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
150
 * @path: the factory path of the new item factory, a string of the form
151
 *    <literal>"&lt;name&gt;"</literal>
152
 * @accel_group: (allow-none): a #GtkAccelGroup to which the accelerators for the
Matthias Clasen's avatar
Matthias Clasen committed
153 154
 *    menu items will be added, or %NULL to create a new one
 * @returns: a new #GtkItemFactory
155
 *
Matthias Clasen's avatar
Matthias Clasen committed
156
 * Creates a new #GtkItemFactory.
157 158
 *
 * Beware that the returned object does not have a floating reference.
Matthias Clasen's avatar
Matthias Clasen committed
159
 *
160
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
161
 */
Tim Janik's avatar
Tim Janik committed
162
GtkItemFactory*
Manish Singh's avatar
Manish Singh committed
163
gtk_item_factory_new (GType	     container_type,
Tim Janik's avatar
Tim Janik committed
164 165 166 167 168 169 170
		      const gchar   *path,
		      GtkAccelGroup *accel_group)
{
  GtkItemFactory *ifactory;

  g_return_val_if_fail (path != NULL, NULL);

Manish Singh's avatar
Manish Singh committed
171
  ifactory = g_object_new (GTK_TYPE_ITEM_FACTORY, NULL);
Tim Janik's avatar
Tim Janik committed
172 173 174 175 176 177 178 179 180 181 182 183 184
  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;

185 186
  if (data->callback_type == 1)
    {
187
      GtkItemFactoryCallback1 func1 = (GtkItemFactoryCallback1) data->func;
188 189 190 191
      func1 (data->func_data, data->callback_action, widget);
    }
  else if (data->callback_type == 2)
    {
192
      GtkItemFactoryCallback2 func2 = (GtkItemFactoryCallback2) data->func;
193 194
      func2 (widget, data->func_data, data->callback_action);
    }
Tim Janik's avatar
Tim Janik committed
195 196 197 198 199 200 201
}

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
202 203
  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
204 205
}

206 207 208 209 210
/**
 * 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
211 212
 * @keyval:           key value of the accelerator
 * @modifiers:        modifier combination of the accelerator
213 214 215 216 217 218 219
 *
 * 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
220 221
 * factory. 
 *
Matthias Clasen's avatar
Matthias Clasen committed
222
 * Deprecated: 2.4: The recommended API for this purpose are the functions 
223 224 225
 * 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.
226
 */
227 228 229 230
void
gtk_item_factory_add_foreign (GtkWidget      *accel_widget,
			      const gchar    *full_path,
			      GtkAccelGroup  *accel_group,
231 232
			      guint           keyval,
			      GdkModifierType modifiers)
Tim Janik's avatar
Tim Janik committed
233 234 235 236
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;

237 238
  g_return_if_fail (GTK_IS_WIDGET (accel_widget));
  g_return_if_fail (full_path != NULL);
Tim Janik's avatar
Tim Janik committed
239

240
  class = gtk_type_class (GTK_TYPE_ITEM_FACTORY);
Tim Janik's avatar
Tim Janik committed
241

242
  keyval = keyval != GDK_VoidSymbol ? keyval : 0;
243 244

  item = g_hash_table_lookup (class->item_ht, full_path);
Tim Janik's avatar
Tim Janik committed
245 246
  if (!item)
    {
247
      item = g_slice_new (GtkItemFactoryItem);
Tim Janik's avatar
Tim Janik committed
248

249
      item->path = g_strdup (full_path);
Tim Janik's avatar
Tim Janik committed
250 251 252 253 254
      item->widgets = NULL;
      
      g_hash_table_insert (class->item_ht, item->path, item);
    }

255
  item->widgets = g_slist_prepend (item->widgets, accel_widget);
Manish Singh's avatar
Manish Singh committed
256 257 258 259
  g_signal_connect (accel_widget,
		    "destroy",
		    G_CALLBACK (gtk_item_factory_item_remove_widget),
		    item);
Tim Janik's avatar
Tim Janik committed
260

261
  /* set the item path for the widget
Tim Janik's avatar
Tim Janik committed
262
   */
Manish Singh's avatar
Manish Singh committed
263
  g_object_set_qdata (G_OBJECT (accel_widget), quark_item_path, item->path);
264 265 266
  gtk_widget_set_name (accel_widget, item->path);
  if (accel_group)
    {
Manish Singh's avatar
Manish Singh committed
267 268 269 270 271
      g_object_ref (accel_group);
      g_object_set_qdata_full (G_OBJECT (accel_widget),
			       quark_accel_group,
			       accel_group,
			       g_object_unref);
272 273
    }
  else
Manish Singh's avatar
Manish Singh committed
274
    g_object_set_qdata (G_OBJECT (accel_widget), quark_accel_group, NULL);
Tim Janik's avatar
Tim Janik committed
275 276 277

  /* install defined accelerators
   */
Manish Singh's avatar
Manish Singh committed
278
  if (g_signal_lookup ("activate", G_TYPE_FROM_INSTANCE (accel_widget)))
Tim Janik's avatar
Tim Janik committed
279
    {
280
      if (accel_group)
281
	{
282
	  gtk_accel_map_add_entry (full_path, keyval, modifiers);
283
	  gtk_widget_set_accel_path (accel_widget, full_path, accel_group);
284
	}
Tim Janik's avatar
Tim Janik committed
285
    }
286
}
Tim Janik's avatar
Tim Janik committed
287

288 289 290
static void
ifactory_cb_data_free (gpointer mem)
{
291
  g_slice_free (GtkIFCBData, mem);
292
}
Tim Janik's avatar
Tim Janik committed
293

294 295 296 297 298 299 300 301 302 303 304 305 306 307
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;
308 309
  guint keyval;
  GdkModifierType mods;
310 311 312 313
  
  g_return_if_fail (widget != NULL);
  g_return_if_fail (item_type != NULL);

314
  class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);
315 316 317 318 319

  /* 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
320

Kristian Rietveld's avatar
Kristian Rietveld committed
321
  /* connect callback if necessary
Tim Janik's avatar
Tim Janik committed
322 323 324 325 326
   */
  if (callback)
    {
      GtkIFCBData *data;

327
      data = g_slice_new (GtkIFCBData);
Tim Janik's avatar
Tim Janik committed
328
      data->func = callback;
329
      data->callback_type = callback_type;
Tim Janik's avatar
Tim Janik committed
330 331 332
      data->func_data = callback_data;
      data->callback_action = callback_action;

Manish Singh's avatar
Manish Singh committed
333 334 335 336 337 338 339
      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
340
    }
341 342 343 344

  /* 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
345 346
  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);
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
  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
363 364
}

Matthias Clasen's avatar
Matthias Clasen committed
365 366 367 368 369
/**
 * 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
370 371
 * @path: the factory path of @ifactory, a string of the form 
 *    <literal>"&lt;name&gt;"</literal>
Matthias Clasen's avatar
Matthias Clasen committed
372 373 374 375
 * @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.
Matthias Clasen's avatar
Matthias Clasen committed
376
 *
377
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
378
 */  
Tim Janik's avatar
Tim Janik committed
379 380
void
gtk_item_factory_construct (GtkItemFactory	*ifactory,
Manish Singh's avatar
Manish Singh committed
381
			    GType		 container_type,
Tim Janik's avatar
Tim Janik committed
382 383 384 385 386 387 388 389
			    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
390 391
  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
392 393 394 395 396 397 398 399 400 401 402 403

  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
404
      g_object_ref (ifactory->accel_group);
Tim Janik's avatar
Tim Janik committed
405 406 407 408 409
    }
  else
    ifactory->accel_group = gtk_accel_group_new ();

  ifactory->path = g_strdup (path);
410
  ifactory->widget = g_object_connect (g_object_new (container_type, NULL),
Tim Janik's avatar
Tim Janik committed
411 412
				       "signal::destroy", gtk_widget_destroyed, &ifactory->widget,
				       NULL);
413
  g_object_ref_sink (ifactory);
414

Tim Janik's avatar
Tim Janik committed
415 416
  gtk_item_factory_add_item (ifactory,
			     "", NULL,
417
			     NULL, 0, NULL, 0,
Tim Janik's avatar
Tim Janik committed
418 419 420 421
			     ITEM_FACTORY_STRING,
			     ifactory->widget);
}

Matthias Clasen's avatar
Matthias Clasen committed
422 423
/**
 * gtk_item_factory_from_path:
424 425
 * @path: a string starting with a factory path of the form 
 *   <literal>"&lt;name&gt;"</literal>
426
 * @returns: (allow-none): the #GtkItemFactory created for the given factory path, or %NULL 
Matthias Clasen's avatar
Matthias Clasen committed
427
 *
428 429 430
 * 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
431
 *
432
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
433
 */
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
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);
461 462 463

  g_free (fname);

464 465 466 467 468 469
  if (item && item->widgets)
    return gtk_item_factory_from_widget (item->widgets->data);

  return NULL;
}

Tim Janik's avatar
Tim Janik committed
470
static void
471
gtk_item_factory_destroy (GtkObject *object)
Tim Janik's avatar
Tim Janik committed
472
{
473
  GtkItemFactory *ifactory = (GtkItemFactory*) object;
Tim Janik's avatar
Tim Janik committed
474 475 476 477
  GSList *slist;

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

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

482
      g_object_ref_sink (dobj);
483
      gtk_object_destroy (dobj);
Manish Singh's avatar
Manish Singh committed
484
      g_object_unref (dobj);
Tim Janik's avatar
Tim Janik committed
485 486 487 488

      ifactory->widget = NULL;
    }

489 490 491 492 493 494
  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
495 496
	if (g_object_get_qdata (link->data, quark_item_factory) == ifactory)
	  g_object_set_qdata (link->data, quark_item_factory, NULL);
497 498 499
    }
  g_slist_free (ifactory->items);
  ifactory->items = NULL;
Tim Janik's avatar
Tim Janik committed
500

Matthias Clasen's avatar
Matthias Clasen committed
501
  GTK_OBJECT_CLASS (gtk_item_factory_parent_class)->destroy (object);
Tim Janik's avatar
Tim Janik committed
502 503 504
}

static void
505
gtk_item_factory_finalize (GObject *object)
Tim Janik's avatar
Tim Janik committed
506
{
507
  GtkItemFactory *ifactory = GTK_ITEM_FACTORY (object);
Tim Janik's avatar
Tim Janik committed
508

Matthias Clasen's avatar
Matthias Clasen committed
509 510 511
  if (ifactory->accel_group)
    g_object_unref (ifactory->accel_group);

Tim Janik's avatar
Tim Janik committed
512 513 514
  g_free (ifactory->path);
  g_assert (ifactory->widget == NULL);

515
  if (ifactory->translate_notify)
516 517
    ifactory->translate_notify (ifactory->translate_data);
  
Matthias Clasen's avatar
Matthias Clasen committed
518
  G_OBJECT_CLASS (gtk_item_factory_parent_class)->finalize (object);
Tim Janik's avatar
Tim Janik committed
519 520
}

Matthias Clasen's avatar
Matthias Clasen committed
521 522 523
/**
 * gtk_item_factory_from_widget:
 * @widget: a widget
524
 * @returns: (allow-none): the item factory from which @widget was created, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
525 526
 *
 * Obtains the item factory from which a widget was created.
Matthias Clasen's avatar
Matthias Clasen committed
527
 *
528
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
529
 */
Tim Janik's avatar
Tim Janik committed
530 531 532
GtkItemFactory*
gtk_item_factory_from_widget (GtkWidget	       *widget)
{
533 534
  GtkItemFactory *ifactory;

Tim Janik's avatar
Tim Janik committed
535 536
  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

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

539 540 541 542
  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
543
      ifactory = g_object_get_qdata (G_OBJECT (menu), quark_item_factory);
544 545 546
    }

  return ifactory;
Tim Janik's avatar
Tim Janik committed
547 548
}

Matthias Clasen's avatar
Matthias Clasen committed
549 550 551
/**
 * gtk_item_factory_path_from_widget:
 * @widget: a widget
552 553
 * @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
554
 *   modified or freed.
Matthias Clasen's avatar
Matthias Clasen committed
555 556 557 558 559
 * 
 * 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.)
Matthias Clasen's avatar
Matthias Clasen committed
560
 *
561
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
562
 */
563
G_CONST_RETURN gchar*
Tim Janik's avatar
Tim Janik committed
564 565
gtk_item_factory_path_from_widget (GtkWidget	    *widget)
{
566 567
  gchar* path;

Tim Janik's avatar
Tim Janik committed
568 569
  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

Manish Singh's avatar
Manish Singh committed
570
  path = g_object_get_qdata (G_OBJECT (widget), quark_item_path);
571 572 573 574 575

  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
576
      path = g_object_get_qdata (G_OBJECT (menu), quark_item_path);
577 578 579
    }

  return path;
Tim Janik's avatar
Tim Janik committed
580 581
}

Matthias Clasen's avatar
Matthias Clasen committed
582 583 584 585
/**
 * gtk_item_factory_create_items:
 * @ifactory: a #GtkItemFactory
 * @n_entries: the length of @entries
586
 * @entries: an array of #GtkItemFactoryEntry<!-- -->s whose @callback members
Matthias Clasen's avatar
Matthias Clasen committed
587 588 589 590
 *    must by of type #GtkItemFactoryCallback1
 * @callback_data: data passed to the callback functions of all entries
 *
 * Creates the menu items from the @entries.
Matthias Clasen's avatar
Matthias Clasen committed
591
 *
592
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
593
 */
Tim Janik's avatar
Tim Janik committed
594
void
595 596 597 598
gtk_item_factory_create_items (GtkItemFactory	   *ifactory,
			       guint		    n_entries,
			       GtkItemFactoryEntry *entries,
			       gpointer		    callback_data)
599 600 601 602
{
  gtk_item_factory_create_items_ac (ifactory, n_entries, entries, callback_data, 1);
}

Matthias Clasen's avatar
Matthias Clasen committed
603 604 605 606
/**
 * gtk_item_factory_create_items_ac:
 * @ifactory: a #GtkItemFactory
 * @n_entries: the length of @entries
607
 * @entries: an array of #GtkItemFactoryEntry<!-- -->s 
Matthias Clasen's avatar
Matthias Clasen committed
608
 * @callback_data: data passed to the callback functions of all entries
609
 * @callback_type: 1 if the callback functions in @entries are of type
Matthias Clasen's avatar
Matthias Clasen committed
610 611 612
 *    #GtkItemFactoryCallback1, 2 if they are of type #GtkItemFactoryCallback2 
 *
 * Creates the menu items from the @entries.
Matthias Clasen's avatar
Matthias Clasen committed
613
 *
614
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
615
 */
616
void
617 618 619 620 621
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
622 623 624 625
{
  guint i;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
626 627 628 629 630
  g_return_if_fail (callback_type >= 1 && callback_type <= 2);

  if (n_entries == 0)
    return;

Tim Janik's avatar
Tim Janik committed
631 632 633
  g_return_if_fail (entries != NULL);

  for (i = 0; i < n_entries; i++)
634
    gtk_item_factory_create_item (ifactory, entries + i, callback_data, callback_type);
Tim Janik's avatar
Tim Janik committed
635 636
}

Matthias Clasen's avatar
Matthias Clasen committed
637 638 639 640
/**
 * gtk_item_factory_get_widget:
 * @ifactory: a #GtkItemFactory
 * @path: the path to the widget
641
 * @returns: (allow-none): the widget for the given path, or %NULL if @path doesn't lead
Matthias Clasen's avatar
Matthias Clasen committed
642 643 644 645 646 647 648
 *   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.
Matthias Clasen's avatar
Matthias Clasen committed
649
 *
650
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
651
 */
Tim Janik's avatar
Tim Janik committed
652
GtkWidget*
653 654
gtk_item_factory_get_widget (GtkItemFactory *ifactory,
			     const gchar    *path)
Tim Janik's avatar
Tim Janik committed
655 656 657 658 659 660 661
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;

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

662
  class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);
Tim Janik's avatar
Tim Janik committed
663

664 665 666 667 668 669 670 671 672 673
  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
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688

  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
689 690 691 692 693
/**
 * gtk_item_factory_get_widget_by_action:
 * @ifactory: a #GtkItemFactory
 * @action: an action as specified in the @callback_action field
 *   of #GtkItemFactoryEntry
694
 * @returns: (allow-none): the widget which corresponds to the given action, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
695 696
 *   if no widget was found
 *
697
 * Obtains the widget which was constructed from the #GtkItemFactoryEntry
Matthias Clasen's avatar
Matthias Clasen committed
698
 * with the given @action.
699 700 701
 *
 * If there are multiple items with the same action, the result is 
 * undefined.
Matthias Clasen's avatar
Matthias Clasen committed
702
 *
703
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
704
 */
Tim Janik's avatar
Tim Janik committed
705
GtkWidget*
706 707
gtk_item_factory_get_widget_by_action (GtkItemFactory *ifactory,
				       guint	       action)
Tim Janik's avatar
Tim Janik committed
708 709 710 711 712
{
  GSList *slist;

  g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);

713
  for (slist = ifactory->items; slist; slist = slist->next)
Tim Janik's avatar
Tim Janik committed
714
    {
715 716
      GtkItemFactoryItem *item = slist->data;
      GSList *link;
Tim Janik's avatar
Tim Janik committed
717

718
      for (link = item->widgets; link; link = link->next)
Manish Singh's avatar
Manish Singh committed
719 720
	if (g_object_get_qdata (link->data, quark_item_factory) == ifactory &&
	    g_object_get_qdata (link->data, quark_action) == GUINT_TO_POINTER (action))
721
	  return link->data;
Tim Janik's avatar
Tim Janik committed
722 723 724 725 726
    }

  return NULL;
}

Matthias Clasen's avatar
Matthias Clasen committed
727 728 729 730
/** 
 * gtk_item_factory_get_item:
 * @ifactory: a #GtkItemFactory
 * @path: the path to the menu item
731
 * @returns: (allow-none): the menu item for the given path, or %NULL if @path doesn't
Matthias Clasen's avatar
Matthias Clasen committed
732 733 734 735 736 737 738
 *   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.
Matthias Clasen's avatar
Matthias Clasen committed
739
 *
740
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
741
 */
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
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
759 760 761 762 763 764

/**
 * gtk_item_factory_get_item_by_action:
 * @ifactory: a #GtkItemFactory
 * @action: an action as specified in the @callback_action field
 *   of #GtkItemFactoryEntry
765
 * @returns: (allow-none): the menu item which corresponds to the given action, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
766 767 768 769
 *   if no menu item was found
 *
 * Obtains the menu item which was constructed from the first 
 * #GtkItemFactoryEntry with the given @action.
Matthias Clasen's avatar
Matthias Clasen committed
770
 *
771
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
772
 */
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
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;
}

789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
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;
}

843
static gboolean
844 845 846 847 848
gtk_item_factory_parse_path (GtkItemFactory *ifactory,
			     gchar          *str,
			     gchar         **path,
			     gchar         **parent_path,
			     gchar         **item)
849
{
850
  gchar *translation;
851
  gchar *p, *q;
852
  
853 854 855 856 857
  *path = g_strdup (str);

  p = q = *path;
  while (*p)
    {
858 859 860 861 862 863 864 865 866
      if (*p == '_')
	{
	  if (p[1] == '_')
	    {
	      p++;
	      *q++ = '_';
	    }
	}
      else
867 868 869 870 871 872 873 874
	{
	  *q++ = *p;
	}
      p++;
    }
  *q = 0;

  *parent_path = g_strdup (*path);
875
  p = item_factory_find_separator_r (*parent_path);
876 877 878 879 880 881 882
  if (!p)
    {
      g_warning ("GtkItemFactory: invalid entry path `%s'", str);
      return FALSE;
    }
  *p = 0;

883 884 885 886 887
  if (ifactory->translate_func)
    translation = ifactory->translate_func (str, ifactory->translate_data);
  else
    translation = str;
			      
888
  p = item_factory_find_separator_r (translation);
889 890 891 892
  if (p)
    p++;
  else
    p = translation;
893

894
  *item = item_factory_unescape_label (p);
895 896 897 898

  return TRUE;
}

Matthias Clasen's avatar
Matthias Clasen committed
899 900 901 902 903
/**
 * 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
904
 * @callback_type: 1 if the callback function of @entry is of type
Matthias Clasen's avatar
Matthias Clasen committed
905 906 907
 *    #GtkItemFactoryCallback1, 2 if it is of type #GtkItemFactoryCallback2 
 *
 * Creates an item for @entry.
Matthias Clasen's avatar
Matthias Clasen committed
908
 *
909
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
910
 */
Tim Janik's avatar
Tim Janik committed
911 912 913
void
gtk_item_factory_create_item (GtkItemFactory	     *ifactory,
			      GtkItemFactoryEntry    *entry,
914 915
			      gpointer		      callback_data,
			      guint		      callback_type)
Tim Janik's avatar
Tim Janik committed
916
{
917
  GtkOptionMenu *option_menu = NULL;
Tim Janik's avatar
Tim Janik committed
918 919
  GtkWidget *parent;
  GtkWidget *widget;
920
  GtkWidget *image;
Tim Janik's avatar
Tim Janik committed
921
  GSList *radio_group;
922
  gchar *name;
Tim Janik's avatar
Tim Janik committed
923
  gchar *parent_path;
924
  gchar *path;
925
  gchar *accelerator;
Tim Janik's avatar
Tim Janik committed
926
  guint type_id;
Manish Singh's avatar
Manish Singh committed
927
  GType type;
928
  gchar *item_type_path;
929 930
  GtkStockItem stock_item;
      
Tim Janik's avatar
Tim Janik committed
931 932 933
  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  g_return_if_fail (entry != NULL);
  g_return_if_fail (entry->path != NULL);
934
  g_return_if_fail (entry->path[0] == '/');
935
  g_return_if_fail (callback_type >= 1 && callback_type <= 2);
Tim Janik's avatar
Tim Janik committed
936 937

  if (!entry->item_type ||
938 939
      entry->item_type[0] == 0)
    {
940
      item_type_path = "<Item>";
941
      type_id = quark_type_item;
942
    }
Tim Janik's avatar
Tim Janik committed
943
  else
944 945
    {
      item_type_path = entry->item_type;
Manish Singh's avatar
Manish Singh committed
946
      type_id = g_quark_try_string (item_type_path);
947
    }
Tim Janik's avatar
Tim Janik committed
948 949

  radio_group = NULL;
950
  if (type_id == quark_type_item)
951
    type = GTK_TYPE_MENU_ITEM;
952
  else if (type_id == quark_type_title)
953
    type = GTK_TYPE_MENU_ITEM;
954
  else if (type_id == quark_type_radio_item)
955
    type = GTK_TYPE_RADIO_MENU_ITEM;
956
  else if (type_id == quark_type_check_item)
957
    type = GTK_TYPE_CHECK_MENU_ITEM;
958 959 960 961
  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;
962 963
  else if (type_id == quark_type_tearoff_item)
    type = GTK_TYPE_TEAROFF_MENU_ITEM;
964
  else if (type_id == quark_type_toggle_item)
965
    type = GTK_TYPE_CHECK_MENU_ITEM;
966
  else if (type_id == quark_type_separator_item)
967
    type = GTK_TYPE_MENU_ITEM;
968
  else if (type_id == quark_type_branch)
969
    type = GTK_TYPE_MENU_ITEM;
970
  else if (type_id == quark_type_last_branch)
971
    type = GTK_TYPE_MENU_ITEM;
Tim Janik's avatar
Tim Janik committed
972 973 974 975
  else
    {
      GtkWidget *radio_link;

976
      radio_link = gtk_item_factory_get_widget (ifactory, item_type_path);
Tim Janik's avatar
Tim Janik committed
977 978
      if (radio_link && GTK_IS_RADIO_MENU_ITEM (radio_link))
	{
979
	  type = GTK_TYPE_RADIO_MENU_ITEM;
Manish Singh's avatar
Manish Singh committed
980
	  radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (radio_link));
Tim Janik's avatar
Tim Janik committed
981 982 983 984 985
	}
      else
	{
	  g_warning ("GtkItemFactory: entry path `%s' has invalid type `%s'",
		     entry->path,
986
		     item_type_path);
Tim Janik's avatar
Tim Janik committed
987 988 989 990
	  return;
	}
    }

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

Tim Janik's avatar
Tim Janik committed
995 996 997 998
  parent = gtk_item_factory_get_widget (ifactory, parent_path);
  if (!parent)
    {
      GtkItemFactoryEntry pentry;
999
      gchar *ppath, *p;
Tim Janik's avatar
Tim Janik committed
1000

1001
      ppath = g_strdup (entry->path);
1002
      p = item_factory_find_separator_r (ppath);
1003 1004 1005
      g_return_if_fail (p != NULL);
      *p = 0;
      pentry.path = ppath;
Tim Janik's avatar
Tim Janik committed
1006 1007 1008 1009 1010
      pentry.accelerator = NULL;
      pentry.callback = NULL;
      pentry.callback_action = 0;
      pentry.item_type = "<Branch>";

1011
      gtk_item_factory_create_item (ifactory, &pentry, NULL, 1);
1012
      g_free (ppath);
Tim Janik's avatar
Tim Janik committed
1013 1014

      parent = gtk_item_factory_get_widget (ifactory, parent_path);
1015
      g_return_if_fail (parent != NULL);
Tim Janik's avatar
Tim Janik committed
1016 1017
    }

1018 1019 1020 1021
  if (GTK_IS_OPTION_MENU (parent))
    {
      option_menu = GTK_OPTION_MENU (parent);
      if (!option_menu->menu)
1022 1023 1024 1025 1026 1027 1028 1029
	{
	  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);
	}
1030 1031
      parent = option_menu->menu;
    }
1032
  g_free (parent_path);
1033
			      
1034
  g_return_if_fail (GTK_IS_CONTAINER (parent));
1035

1036 1037
  accelerator = entry->accelerator;
  
1038
  widget = g_object_new (type,
1039 1040 1041 1042
			   "visible", TRUE,
			   "sensitive", (type_id != quark_type_separator_item &&
					 type_id != quark_type_title),
			   "parent", parent,
Tim Janik's avatar
Tim Janik committed
1043
			   NULL);
1044 1045
  if (option_menu && !option_menu->menu_item)
    gtk_option_menu_set_history (option_menu, 0);
Tim Janik's avatar
Tim Janik committed
1046

1047
  if (GTK_IS_RADIO_MENU_ITEM (widget))
Tim Janik's avatar
Tim Janik committed
1048
    gtk_radio_menu_item_set_group (GTK_RADIO_MENU_ITEM (widget), radio_group);
1049
  if (type_id == quark_type_image_item)
1050 1051
    {
      GdkPixbuf *pixbuf = NULL;
1052
      image = NULL;
1053
      if (entry->extra_data)
1054
	{
1055 1056 1057 1058 1059 1060
	  pixbuf = gdk_pixbuf_new_from_inline (-1,
					       entry->extra_data,
					       FALSE,
					       NULL);
	  if (pixbuf)
	    image = gtk_image_new_from_pixbuf (pixbuf);
1061
	}
1062
      if (image)
1063 1064 1065 1066
	{
	  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (widget), image);
	  gtk_widget_show (image);
	}
1067
      if (pixbuf)
Manish Singh's avatar
Manish Singh committed
1068
	g_object_unref (pixbuf);
1069 1070 1071 1072
    }
  if (type_id == quark_type_stock_item)
    {
      image = gtk_image_new_from_stock (entry->extra_data, GTK_ICON_SIZE_MENU);
1073
      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (widget), image);
1074 1075
      gtk_widget_show (image);

1076 1077 1078 1079 1080 1081
      if (gtk_stock_lookup (entry->extra_data, &stock_item))
	{
	  if (!accelerator)
	    accelerator = gtk_accelerator_name (stock_item.keyval, stock_item.modifier);
	}
    }
1082 1083 1084 1085 1086

  /* install underline accelerators for this item
   */
  if (type_id != quark_type_separator_item && 
      type_id != quark_type_tearoff_item &&
1087
      *name)
Tim Janik's avatar
Tim Janik committed
1088 1089
    {
      GtkWidget *label;
1090
      
1091
      label = g_object_new (GTK_TYPE_ACCEL_LABEL,
1092 1093
			      "visible", TRUE,
			      "parent", widget,
1094
			      "accel-widget", widget,
1095
			      "xalign", 0.0,
1096
			      NULL);
1097
      gtk_label_set_text_with_mnemonic (GTK_LABEL (label), name);
Tim Janik's avatar
Tim Janik committed
1098
    }
1099
  
1100
  g_free (name);
1101
  
1102 1103
  if (type_id == quark_type_branch ||
      type_id == quark_type_last_branch)
Tim Janik's avatar
Tim Janik committed
1104
    {
1105 1106
      gchar *p;

1107 1108 1109
      if (entry->callback)
	g_warning ("gtk_item_factory_create_item(): Can't specify a callback on a branch: \"%s\"",
		   entry->path);
1110
      if (type_id == quark_type_last_branch)
1111
	gtk_menu_item_set_right_justified (GTK_MENU_ITEM (widget), TRUE);
1112
      
Tim Janik's avatar
Tim Janik committed
1113
      parent = widget;
1114
      widget = g_object_new (GTK_TYPE_MENU, NULL);
1115 1116 1117
      p = g_strconcat (ifactory->path, path, NULL);
      gtk_menu_set_accel_path (GTK_MENU (widget), p);
      g_free (p);
1118
      
Tim Janik's avatar
Tim Janik committed
1119 1120 1121 1122
      gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), widget);
    }	   
  
  gtk_item_factory_add_item (ifactory,
1123
			     path, accelerator,
1124
			     (type_id == quark_type_branch ||
1125 1126
			      type_id == quark_type_last_branch) ?
			      (GtkItemFactoryCallback) NULL : entry->callback,
1127
			     entry->callback_action, callback_data,
1128
			     callback_type,
1129
			     item_type_path,
Tim Janik's avatar
Tim Janik committed
1130
			     widget);
1131 1132
  if (accelerator != entry->accelerator)
    g_free (accelerator);
1133
  g_free (path);
Tim Janik's avatar
Tim Janik committed
1134 1135
}

Matthias Clasen's avatar
Matthias Clasen committed
1136 1137 1138
/**
 * gtk_item_factory_create_menu_entries:
 * @n_entries: the length of @entries
1139
 * @entries: an array of #GtkMenuEntry<!-- -->s 
Matthias Clasen's avatar
Matthias Clasen committed
1140 1141
 *
 * Creates the menu items from the @entries.
Matthias Clasen's avatar
Matthias Clasen committed
1142
 *
1143
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
1144
 */
Tim Janik's avatar
Tim Janik committed
1145
void
1146 1147 1148
gtk_item_factory_create_menu_entries (guint              n_entries,
				      GtkMenuEntry      *entries)
{
1149 1150
  static GPatternSpec *pspec_separator = NULL;
  static GPatternSpec *pspec_check = NULL;
1151 1152 1153 1154 1155 1156
  guint i;

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

1157
  if (!pspec_separator)
1158
    {
1159 1160
      pspec_separator = g_pattern_spec_new ("*<separator>*");
      pspec_check = g_pattern_spec_new ("*<check>*");
1161
    }
1162 1163 1164 1165 1166 1167

  for (i = 0; i < n_entries; i++)
    {
      GtkItemFactory *ifactory;
      GtkItemFactoryEntry entry;
      gchar *path;
1168
      gchar *cpath;
1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182

      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++;
1183
      cpath = NULL;
1184 1185 1186 1187 1188

      entry.path = path;
      entry.accelerator = entries[i].accelerator;
      entry.callback = entries[i].callback;
      entry.callback_action = 0;
1189
      if (g_pattern_match_string (pspec_separator, path))
1190
	entry.item_type = "<Separator>";
1191
      else if (!g_pattern_match_string (pspec_check, path))
1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
	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;
1211
	  entry.item_type = "<ToggleItem>";
1212 1213 1214
	  entry.path = cpath;
	}
      
1215 1216
      gtk_item_factory_create_item (ifactory, &entry, entries[i].callback_data, 2);
      entries[i].widget = gtk_item_factory_get_widget (ifactory, entries[i].path);
1217
      g_free (cpath);
1218 1219 1220
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
1221 1222 1223 1224 1225 1226 1227
/**
 * 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.
Matthias Clasen's avatar
Matthias Clasen committed
1228
 *
1229
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
1230
 */
1231 1232 1233
void
gtk_item_factories_path_delete (const gchar *ifactory_path,
				const gchar *path)
Tim Janik's avatar
Tim Janik committed
1234 1235 1236 1237 1238 1239 1240 1241
{
  GtkItemFactoryClass *class;
  GtkItemFactoryItem *item;

  g_return_if_fail (path != NULL);

  class = gtk_type_class (GTK_TYPE_ITEM_FACTORY);

1242 1243 1244 1245 1246
  if (path[0] == '<')
    item = g_hash_table_lookup (class->item_ht, (gpointer) path);
  else
    {
      gchar *fpath;
Tim Janik's avatar
Tim Janik committed
1247

1248 1249 1250 1251 1252 1253 1254
      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
1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266
  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
1267
	  g_object_ref (widget);
Tim Janik's avatar
Tim Janik committed
1268 1269 1270 1271 1272 1273 1274 1275
	}

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

	  widget = slist->data;
	  gtk_widget_destroy (widget);
Manish Singh's avatar
Manish Singh committed
1276
	  g_object_unref (widget);
Tim Janik's avatar
Tim Janik committed
1277 1278 1279 1280 1281
	}
      g_slist_free (widget_list);
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
1282 1283 1284 1285 1286 1287 1288
/**
 * 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.
Matthias Clasen's avatar
Matthias Clasen committed
1289
 *
1290
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
1291
 */
Tim Janik's avatar
Tim Janik committed
1292 1293 1294 1295
void
gtk_item_factory_delete_item (GtkItemFactory         *ifactory,
			      const gchar            *path)
{
1296
  GtkWidget *widget;
Tim Janik's avatar
Tim Janik committed
1297 1298 1299 1300

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

1301
  widget = gtk_item_factory_get_widget (ifactory, path);
Tim Janik's avatar
Tim Janik committed
1302

1303
  if (widget)
Tim Janik's avatar
Tim Janik committed
1304
    {
1305 1306
      if (GTK_IS_MENU (widget))
	widget = gtk_menu_get_attach_widget (GTK_MENU (widget));
Tim Janik's avatar
Tim Janik committed
1307

1308
      gtk_widget_destroy (widget);
Tim Janik's avatar
Tim Janik committed
1309 1310 1311
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
1312 1313 1314 1315 1316 1317 1318
/**
 * 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.
Matthias Clasen's avatar
Matthias Clasen committed
1319
 *
1320
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
1321
 */
Tim Janik's avatar
Tim Janik committed
1322 1323 1324 1325
void
gtk_item_factory_delete_entry (GtkItemFactory         *ifactory,
			       GtkItemFactoryEntry    *entry)
{
1326 1327 1328 1329
  gchar *path;
  gchar *parent_path;
  gchar *name;

Tim Janik's avatar
Tim Janik committed
1330 1331
  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
  g_return_if_fail (entry != NULL);
1332 1333 1334 1335 1336 1337 1338 1339
  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
1340

1341 1342 1343
  g_free (path);
  g_free (parent_path);
  g_free (name);
Tim Janik's avatar
Tim Janik committed
1344 1345
}

Matthias Clasen's avatar
Matthias Clasen committed
1346 1347
/**
 * gtk_item_factory_delete_entries:
1348
 * @ifactory: a #GtkItemFactory
Matthias Clasen's avatar
Matthias Clasen committed
1349
 * @n_entries: the length of @entries
1350
 * @entries: an array of #GtkItemFactoryEntry<!-- -->s 
Matthias Clasen's avatar
Matthias Clasen committed
1351 1352 1353
 *
 * Deletes the menu items which were created from the @entries by the given
 * item factory.
Matthias Clasen's avatar
Matthias Clasen committed
1354
 *
1355
 * Deprecated: 2.4: Use #GtkUIManager instead.
Matthias Clasen's avatar
Matthias Clasen committed
1356
 */
Tim Janik's avatar
Tim Janik committed
1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368
void
gtk_item_factory_delete_entries (GtkItemFactory         *ifactory,
				 guint                   n_entries,
				 GtkItemFactoryEntry    *entries)
{
  guint i;

  g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));