]> Pileus Git - ~andy/gtk/blob - gtk/gtkitemfactory.c
Revert inline-pixbuf changes until Tim comes back to fix missing files
[~andy/gtk] / gtk / gtkitemfactory.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GtkItemFactory: Flexible item factory with automatic rc handling
5  * Copyright (C) 1998 Tim Janik
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /*
24  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
25  * file for a list of people on the GTK+ Team.  See the ChangeLog
26  * files for a list of changes.  These files are distributed with
27  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
28  */
29
30 #include        "config.h"
31
32 #include        "gtkitemfactory.h"
33 #include        "gtk/gtksignal.h"
34 #include        "gtk/gtkoptionmenu.h"
35 #include        "gtk/gtkmenubar.h"
36 #include        "gtk/gtkmenu.h"
37 #include        "gtk/gtkmenuitem.h"
38 #include        "gtk/gtkradiomenuitem.h"
39 #include        "gtk/gtkcheckmenuitem.h"
40 #include        "gtk/gtkimagemenuitem.h"
41 #include        "gtk/gtktearoffmenuitem.h"
42 #include        "gtk/gtkaccellabel.h"
43 #include        "gdk/gdkkeysyms.h"
44 #include        "gtk/gtkimage.h"
45 #include        "gtk/gtkstock.h"
46 #include        "gtk/gtkiconfactory.h"
47 #include        <string.h>
48 #include        <sys/stat.h>
49 #include        <fcntl.h>
50 #ifdef HAVE_UNISTD_H
51 #include        <unistd.h>
52 #endif
53 #include        <stdio.h>
54
55 #ifdef G_OS_WIN32
56 #include        <io.h>          /* For _open and _close */
57
58 #ifndef S_ISREG
59 #define S_ISREG(mode) ((mode)&_S_IFREG)
60 #endif
61 #endif
62
63
64 /* --- defines --- */
65 #define         ITEM_FACTORY_STRING     ((gchar*) item_factory_string)
66 #define         ITEM_BLOCK_SIZE         (128)
67
68
69 /* --- structures --- */
70 typedef struct  _GtkIFCBData            GtkIFCBData;
71 typedef struct  _GtkIFDumpData          GtkIFDumpData;
72 struct _GtkIFCBData
73 {
74   GtkItemFactoryCallback  func;
75   guint                   callback_type;
76   gpointer                func_data;
77   guint                   callback_action;
78 };
79 struct _GtkIFDumpData
80 {
81   GtkPrintFunc   print_func;
82   gpointer       func_data;
83   guint          modified_only : 1;
84   GPatternSpec  *pspec;
85 };
86
87
88 /* --- prototypes --- */
89 static void     gtk_item_factory_class_init             (GtkItemFactoryClass  *klass);
90 static void     gtk_item_factory_init                   (GtkItemFactory       *ifactory);
91 static void     gtk_item_factory_destroy                (GtkObject            *object);
92 static void     gtk_item_factory_finalize               (GObject              *object);
93
94
95 /* --- static variables --- */
96 static GtkItemFactoryClass *gtk_item_factory_class = NULL;
97 static gpointer          parent_class = NULL;
98 static const gchar      *item_factory_string = "Gtk-<ItemFactory>";
99 static GMemChunk        *ifactory_item_chunks = NULL;
100 static GMemChunk        *ifactory_cb_data_chunks = NULL;
101 static GQuark            quark_popup_data = 0;
102 static GQuark            quark_if_menu_pos = 0;
103 static GQuark            quark_item_factory = 0;
104 static GQuark            quark_item_path = 0;
105 static GQuark            quark_action = 0;
106 static GQuark            quark_accel_group = 0;
107 static GQuark            quark_type_item = 0;
108 static GQuark            quark_type_title = 0;
109 static GQuark            quark_type_radio_item = 0;
110 static GQuark            quark_type_check_item = 0;
111 static GQuark            quark_type_toggle_item = 0;
112 static GQuark            quark_type_image_item = 0;
113 static GQuark            quark_type_stock_item = 0;
114 static GQuark            quark_type_tearoff_item = 0;
115 static GQuark            quark_type_separator_item = 0;
116 static GQuark            quark_type_branch = 0;
117 static GQuark            quark_type_last_branch = 0;
118 static  GScannerConfig  ifactory_scanner_config =
119 {
120   (
121    " \t\n"
122    )                    /* cset_skip_characters */,
123   (
124    G_CSET_a_2_z
125    "_"
126    G_CSET_A_2_Z
127    )                    /* cset_identifier_first */,
128   (
129    G_CSET_a_2_z
130    "-+_0123456789"
131    G_CSET_A_2_Z
132    G_CSET_LATINS
133    G_CSET_LATINC
134    )                    /* cset_identifier_nth */,
135   ( ";\n" )             /* cpair_comment_single */,
136   
137   FALSE                 /* case_sensitive */,
138   
139   TRUE                  /* skip_comment_multi */,
140   TRUE                  /* skip_comment_single */,
141   FALSE                 /* scan_comment_multi */,
142   TRUE                  /* scan_identifier */,
143   FALSE                 /* scan_identifier_1char */,
144   FALSE                 /* scan_identifier_NULL */,
145   TRUE                  /* scan_symbols */,
146   TRUE                  /* scan_binary */,
147   TRUE                  /* scan_octal */,
148   TRUE                  /* scan_float */,
149   TRUE                  /* scan_hex */,
150   FALSE                 /* scan_hex_dollar */,
151   TRUE                  /* scan_string_sq */,
152   TRUE                  /* scan_string_dq */,
153   TRUE                  /* numbers_2_int */,
154   FALSE                 /* int_2_float */,
155   FALSE                 /* identifier_2_string */,
156   TRUE                  /* char_2_token */,
157   FALSE                 /* symbol_2_token */,
158 };
159
160
161 /* --- functions --- */
162 GtkType
163 gtk_item_factory_get_type (void)
164 {
165   static GtkType item_factory_type = 0;
166   
167   if (!item_factory_type)
168     {
169       static const GtkTypeInfo item_factory_info =
170       {
171         "GtkItemFactory",
172         sizeof (GtkItemFactory),
173         sizeof (GtkItemFactoryClass),
174         (GtkClassInitFunc) gtk_item_factory_class_init,
175         (GtkObjectInitFunc) gtk_item_factory_init,
176         /* reserved_1 */ NULL,
177         /* reserved_2 */ NULL,
178         (GtkClassInitFunc) NULL,
179       };
180       
181       item_factory_type = gtk_type_unique (GTK_TYPE_OBJECT, &item_factory_info);
182     }
183   
184   return item_factory_type;
185 }
186
187 static void
188 gtk_item_factory_class_init (GtkItemFactoryClass  *class)
189 {
190   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
191   GtkObjectClass *object_class;
192
193   gtk_item_factory_class = class;
194
195   parent_class = gtk_type_class (GTK_TYPE_OBJECT);
196
197   object_class = (GtkObjectClass*) class;
198
199   gobject_class->finalize = gtk_item_factory_finalize;
200
201   object_class->destroy = gtk_item_factory_destroy;
202
203   class->cpair_comment_single = g_strdup (";\n");
204
205   class->item_ht = g_hash_table_new (g_str_hash, g_str_equal);
206   class->dummy = NULL;
207   ifactory_item_chunks =
208     g_mem_chunk_new ("GtkItemFactoryItem",
209                      sizeof (GtkItemFactoryItem),
210                      sizeof (GtkItemFactoryItem) * ITEM_BLOCK_SIZE,
211                      G_ALLOC_ONLY);
212   ifactory_cb_data_chunks =
213     g_mem_chunk_new ("GtkIFCBData",
214                      sizeof (GtkIFCBData),
215                      sizeof (GtkIFCBData) * ITEM_BLOCK_SIZE,
216                      G_ALLOC_AND_FREE);
217
218   quark_popup_data              = g_quark_from_static_string ("GtkItemFactory-popup-data");
219   quark_if_menu_pos             = g_quark_from_static_string ("GtkItemFactory-menu-position");
220   quark_item_factory            = g_quark_from_static_string ("GtkItemFactory");
221   quark_item_path               = g_quark_from_static_string ("GtkItemFactory-path");
222   quark_action                  = g_quark_from_static_string ("GtkItemFactory-action");
223   quark_accel_group             = g_quark_from_static_string ("GtkAccelGroup");
224   quark_type_item               = g_quark_from_static_string ("<Item>");
225   quark_type_title              = g_quark_from_static_string ("<Title>");
226   quark_type_radio_item         = g_quark_from_static_string ("<RadioItem>");
227   quark_type_check_item         = g_quark_from_static_string ("<CheckItem>");
228   quark_type_toggle_item        = g_quark_from_static_string ("<ToggleItem>");
229   quark_type_image_item         = g_quark_from_static_string ("<ImageItem>");
230   quark_type_stock_item         = g_quark_from_static_string ("<StockItem>");
231   quark_type_tearoff_item       = g_quark_from_static_string ("<Tearoff>");
232   quark_type_separator_item     = g_quark_from_static_string ("<Separator>");
233   quark_type_branch             = g_quark_from_static_string ("<Branch>");
234   quark_type_last_branch        = g_quark_from_static_string ("<LastBranch>");
235 }
236
237 static void
238 gtk_item_factory_init (GtkItemFactory       *ifactory)
239 {
240   GtkObject *object;
241
242   object = GTK_OBJECT (ifactory);
243
244   ifactory->path = NULL;
245   ifactory->accel_group = NULL;
246   ifactory->widget = NULL;
247   ifactory->items = NULL;
248   ifactory->translate_func = NULL;
249   ifactory->translate_data = NULL;
250   ifactory->translate_notify = NULL;
251 }
252
253 GtkItemFactory*
254 gtk_item_factory_new (GtkType        container_type,
255                       const gchar   *path,
256                       GtkAccelGroup *accel_group)
257 {
258   GtkItemFactory *ifactory;
259
260   g_return_val_if_fail (path != NULL, NULL);
261
262   ifactory = gtk_type_new (GTK_TYPE_ITEM_FACTORY);
263   gtk_item_factory_construct (ifactory, container_type, path, accel_group);
264
265   return ifactory;
266 }
267
268 static void
269 gtk_item_factory_callback_marshal (GtkWidget *widget,
270                                    gpointer   func_data)
271 {
272   GtkIFCBData *data;
273
274   data = func_data;
275
276   if (data->callback_type == 1)
277     {
278       GtkItemFactoryCallback1 func1 = (GtkItemFactoryCallback1) data->func;
279       func1 (data->func_data, data->callback_action, widget);
280     }
281   else if (data->callback_type == 2)
282     {
283       GtkItemFactoryCallback2 func2 = (GtkItemFactoryCallback2) data->func;
284       func2 (widget, data->func_data, data->callback_action);
285     }
286 }
287
288 static void
289 gtk_item_factory_propagate_accelerator (GtkItemFactoryItem *item,
290                                         GtkWidget          *exclude)
291 {
292   GSList *widget_list;
293   GSList *slist;
294   
295   if (item->in_propagation)
296     return;
297   
298   item->in_propagation = TRUE;
299   
300   widget_list = NULL;
301   for (slist = item->widgets; slist; slist = slist->next)
302     {
303       GtkWidget *widget;
304       
305       widget = slist->data;
306       
307       if (widget != exclude)
308         {
309           gtk_widget_ref (widget);
310           widget_list = g_slist_prepend (widget_list, widget);
311         }
312     }
313   
314   for (slist = widget_list; slist; slist = slist->next)
315     {
316       GtkWidget *widget;
317       GtkAccelGroup *accel_group;
318       guint signal_id;
319       
320       widget = slist->data;
321       
322       accel_group = gtk_object_get_data_by_id (GTK_OBJECT (widget), quark_accel_group);
323       
324       signal_id = gtk_signal_lookup ("activate", GTK_OBJECT_TYPE (widget));
325       if (signal_id && accel_group)
326         {
327           if (item->accelerator_key)
328             gtk_widget_add_accelerator (widget,
329                                         "activate",
330                                         accel_group,
331                                         item->accelerator_key,
332                                         item->accelerator_mods,
333                                         GTK_ACCEL_VISIBLE);
334           else
335             {
336               GSList *work;
337               
338               work = gtk_accel_group_entries_from_object (GTK_OBJECT (widget));
339               while (work)
340                 {
341                   GtkAccelEntry *ac_entry;
342                   
343                   ac_entry = work->data;
344                   work = work->next;
345                   if (ac_entry->accel_flags & GTK_ACCEL_VISIBLE &&
346                       ac_entry->accel_group == accel_group &&
347                       ac_entry->signal_id == signal_id)
348                     gtk_widget_remove_accelerator (GTK_WIDGET (widget),
349                                                    ac_entry->accel_group,
350                                                    ac_entry->accelerator_key,
351                                                    ac_entry->accelerator_mods);
352                 }
353             }
354         }
355
356       gtk_widget_unref (widget);
357     }
358   
359   g_slist_free (widget_list);
360   
361   item->in_propagation = FALSE;
362 }
363
364 static gint
365 gtk_item_factory_item_add_accelerator (GtkWidget          *widget,
366                                        guint               accel_signal_id,
367                                        GtkAccelGroup      *accel_group,
368                                        guint               accel_key,
369                                        guint               accel_mods,
370                                        GtkAccelFlags       accel_flags,
371                                        GtkItemFactoryItem *item)
372 {
373   if (!item->in_propagation &&
374       g_slist_find (item->widgets, widget) &&
375       accel_signal_id == gtk_signal_lookup ("activate", GTK_OBJECT_TYPE (widget)))
376     {
377       item->accelerator_key = accel_key;
378       item->accelerator_mods = accel_mods;
379       item->modified = TRUE;
380       
381       gtk_item_factory_propagate_accelerator (item, widget);
382     }
383
384   return TRUE;
385 }
386
387 static void
388 gtk_item_factory_item_remove_accelerator (GtkWidget          *widget,
389                                           GtkAccelGroup      *accel_group,
390                                           guint               accel_key,
391                                           guint               accel_mods,
392                                           GtkItemFactoryItem *item)
393 {
394   if (!item->in_propagation &&
395       g_slist_find (item->widgets, widget) &&
396       item->accelerator_key == accel_key &&
397       item->accelerator_mods == accel_mods)
398     {
399       item->accelerator_key = 0;
400       item->accelerator_mods = 0;
401       item->modified = TRUE;
402       
403       gtk_item_factory_propagate_accelerator (item, widget);
404     }
405 }
406
407 static void
408 gtk_item_factory_item_remove_widget (GtkWidget          *widget,
409                                      GtkItemFactoryItem *item)
410 {
411   item->widgets = g_slist_remove (item->widgets, widget);
412   gtk_object_remove_data_by_id (GTK_OBJECT (widget), quark_item_factory);
413   gtk_object_remove_data_by_id (GTK_OBJECT (widget), quark_item_path);
414 }
415
416 void
417 gtk_item_factory_add_foreign (GtkWidget      *accel_widget,
418                               const gchar    *full_path,
419                               GtkAccelGroup  *accel_group,
420                               guint           keyval,
421                               GdkModifierType modifiers)
422 {
423   GtkItemFactoryClass *class;
424   GtkItemFactoryItem *item;
425
426   g_return_if_fail (GTK_IS_WIDGET (accel_widget));
427   g_return_if_fail (full_path != NULL);
428
429   class = gtk_type_class (GTK_TYPE_ITEM_FACTORY);
430
431   keyval = keyval != GDK_VoidSymbol ? keyval : 0;
432
433   item = g_hash_table_lookup (class->item_ht, full_path);
434   if (!item)
435     {
436       item = g_chunk_new (GtkItemFactoryItem, ifactory_item_chunks);
437
438       item->path = g_strdup (full_path);
439       item->accelerator_key = keyval;
440       item->accelerator_mods = modifiers;
441       item->modified = FALSE;
442       item->in_propagation = FALSE;
443       item->dummy = NULL;
444       item->widgets = NULL;
445       
446       g_hash_table_insert (class->item_ht, item->path, item);
447     }
448
449   item->widgets = g_slist_prepend (item->widgets, accel_widget);
450   gtk_signal_connect (GTK_OBJECT (accel_widget),
451                       "destroy",
452                       GTK_SIGNAL_FUNC (gtk_item_factory_item_remove_widget),
453                       item);
454
455   /* set the item path for the widget
456    */
457   gtk_object_set_data_by_id (GTK_OBJECT (accel_widget), quark_item_path, item->path);
458   gtk_widget_set_name (accel_widget, item->path);
459   if (accel_group)
460     {
461       gtk_accel_group_ref (accel_group);
462       gtk_object_set_data_by_id_full (GTK_OBJECT (accel_widget),
463                                       quark_accel_group,
464                                       accel_group,
465                                       (GtkDestroyNotify) gtk_accel_group_unref);
466     }
467   else
468     gtk_object_set_data_by_id (GTK_OBJECT (accel_widget), quark_accel_group, NULL);
469
470   /* install defined accelerators
471    */
472   if (gtk_signal_lookup ("activate", GTK_OBJECT_TYPE (accel_widget)))
473     {
474       if (item->accelerator_key && accel_group)
475         gtk_widget_add_accelerator (accel_widget,
476                                     "activate",
477                                     accel_group,
478                                     item->accelerator_key,
479                                     item->accelerator_mods,
480                                     GTK_ACCEL_VISIBLE);
481       else
482         gtk_widget_remove_accelerators (accel_widget,
483                                         "activate",
484                                         TRUE);
485     }
486
487   /* keep track of accelerator changes
488    */
489   gtk_signal_connect_after (GTK_OBJECT (accel_widget),
490                             "add-accelerator",
491                             GTK_SIGNAL_FUNC (gtk_item_factory_item_add_accelerator),
492                             item);
493   gtk_signal_connect_after (GTK_OBJECT (accel_widget),
494                             "remove-accelerator",
495                             GTK_SIGNAL_FUNC (gtk_item_factory_item_remove_accelerator),
496                             item);
497 }
498
499 static void
500 ifactory_cb_data_free (gpointer mem)
501 {
502   g_mem_chunk_free (ifactory_cb_data_chunks, mem);
503 }
504
505 static void
506 gtk_item_factory_add_item (GtkItemFactory               *ifactory,
507                            const gchar                  *path,
508                            const gchar                  *accelerator,
509                            GtkItemFactoryCallback       callback,
510                            guint                        callback_action,
511                            gpointer                     callback_data,
512                            guint                        callback_type,
513                            gchar                        *item_type,
514                            GtkWidget                    *widget)
515 {
516   GtkItemFactoryClass *class;
517   GtkItemFactoryItem *item;
518   gchar *fpath;
519   guint keyval, mods;
520   
521   g_return_if_fail (widget != NULL);
522   g_return_if_fail (item_type != NULL);
523
524   class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);
525
526   /* set accelerator group on menu widgets
527    */
528   if (GTK_IS_MENU (widget))
529     gtk_menu_set_accel_group ((GtkMenu*) widget, ifactory->accel_group);
530
531   /* connect callback if neccessary
532    */
533   if (callback)
534     {
535       GtkIFCBData *data;
536
537       data = g_chunk_new (GtkIFCBData, ifactory_cb_data_chunks);
538       data->func = callback;
539       data->callback_type = callback_type;
540       data->func_data = callback_data;
541       data->callback_action = callback_action;
542
543       gtk_object_weakref (GTK_OBJECT (widget),
544                           ifactory_cb_data_free,
545                           data);
546       gtk_signal_connect (GTK_OBJECT (widget),
547                           "activate",
548                           GTK_SIGNAL_FUNC (gtk_item_factory_callback_marshal),
549                           data);
550     }
551
552   /* link the widget into its item-entry
553    * and keep back pointer on both the item factory and the widget
554    */
555   gtk_object_set_data_by_id (GTK_OBJECT (widget), quark_action, GUINT_TO_POINTER (callback_action));
556   gtk_object_set_data_by_id (GTK_OBJECT (widget), quark_item_factory, ifactory);
557   if (accelerator)
558     gtk_accelerator_parse (accelerator, &keyval, &mods);
559   else
560     {
561       keyval = 0;
562       mods = 0;
563     }
564   fpath = g_strconcat (ifactory->path, path, NULL);
565   gtk_item_factory_add_foreign (widget, fpath, ifactory->accel_group, keyval, mods);
566   item = g_hash_table_lookup (class->item_ht, fpath);
567   g_free (fpath);
568
569   g_return_if_fail (item != NULL);
570
571   if (!g_slist_find (ifactory->items, item))
572     ifactory->items = g_slist_prepend (ifactory->items, item);
573 }
574
575 void
576 gtk_item_factory_construct (GtkItemFactory      *ifactory,
577                             GtkType              container_type,
578                             const gchar         *path,
579                             GtkAccelGroup       *accel_group)
580 {
581   guint len;
582
583   g_return_if_fail (ifactory != NULL);
584   g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
585   g_return_if_fail (ifactory->accel_group == NULL);
586   g_return_if_fail (path != NULL);
587   if (!gtk_type_is_a (container_type, GTK_TYPE_OPTION_MENU))
588     g_return_if_fail (gtk_type_is_a (container_type, GTK_TYPE_MENU_SHELL));
589
590   len = strlen (path);
591
592   if (path[0] != '<' && path[len - 1] != '>')
593     {
594       g_warning ("GtkItemFactory: invalid factory path `%s'", path);
595       return;
596     }
597
598   if (accel_group)
599     {
600       ifactory->accel_group = accel_group;
601       gtk_accel_group_ref (ifactory->accel_group);
602     }
603   else
604     ifactory->accel_group = gtk_accel_group_new ();
605
606   ifactory->path = g_strdup (path);
607   ifactory->widget = g_object_connect (gtk_widget_new (container_type, NULL),
608                                        "signal::destroy", gtk_widget_destroyed, &ifactory->widget,
609                                        NULL);
610   gtk_object_ref (GTK_OBJECT (ifactory));
611   gtk_object_sink (GTK_OBJECT (ifactory));
612
613   gtk_item_factory_add_item (ifactory,
614                              "", NULL,
615                              NULL, 0, NULL, 0,
616                              ITEM_FACTORY_STRING,
617                              ifactory->widget);
618 }
619
620 GtkItemFactory*
621 gtk_item_factory_from_path (const gchar      *path)
622 {
623   GtkItemFactoryClass *class;
624   GtkItemFactoryItem *item;
625   gchar *fname;
626   guint i;
627
628   g_return_val_if_fail (path != NULL, NULL);
629   g_return_val_if_fail (path[0] == '<', NULL);
630
631   class = gtk_type_class (GTK_TYPE_ITEM_FACTORY);
632
633   i = 0;
634   while (path[i] && path[i] != '>')
635     i++;
636   if (path[i] != '>')
637     {
638       g_warning ("gtk_item_factory_from_path(): invalid factory path \"%s\"",
639                  path);
640       return NULL;
641     }
642   fname = g_new (gchar, i + 2);
643   g_memmove (fname, path, i + 1);
644   fname[i + 1] = 0;
645
646   item = g_hash_table_lookup (class->item_ht, fname);
647
648   g_free (fname);
649
650   if (item && item->widgets)
651     return gtk_item_factory_from_widget (item->widgets->data);
652
653   return NULL;
654 }
655
656 static void
657 gtk_item_factory_destroy (GtkObject *object)
658 {
659   GtkItemFactory *ifactory;
660   GSList *slist;
661
662   g_return_if_fail (object != NULL);
663   g_return_if_fail (GTK_IS_ITEM_FACTORY (object));
664
665   ifactory = (GtkItemFactory*) object;
666
667   if (ifactory->widget)
668     {
669       GtkObject *dobj;
670
671       dobj = GTK_OBJECT (ifactory->widget);
672
673       gtk_object_ref (dobj);
674       gtk_object_sink (dobj);
675       gtk_object_destroy (dobj);
676       gtk_object_unref (dobj);
677
678       ifactory->widget = NULL;
679     }
680
681   for (slist = ifactory->items; slist; slist = slist->next)
682     {
683       GtkItemFactoryItem *item = slist->data;
684       GSList *link;
685       
686       for (link = item->widgets; link; link = link->next)
687         if (gtk_object_get_data_by_id (link->data, quark_item_factory) == ifactory)
688           gtk_object_remove_data_by_id (link->data, quark_item_factory);
689     }
690   g_slist_free (ifactory->items);
691   ifactory->items = NULL;
692
693   GTK_OBJECT_CLASS (parent_class)->destroy (object);
694 }
695
696 static void
697 gtk_item_factory_finalize (GObject *object)
698 {
699   GtkItemFactory *ifactory;
700
701   g_return_if_fail (object != NULL);
702   g_return_if_fail (GTK_IS_ITEM_FACTORY (object));
703
704   ifactory = GTK_ITEM_FACTORY (object);
705
706   gtk_accel_group_unref (ifactory->accel_group);
707   g_free (ifactory->path);
708   g_assert (ifactory->widget == NULL);
709
710   if (ifactory->translate_notify)
711     ifactory->translate_notify (ifactory->translate_data);
712   
713   G_OBJECT_CLASS (parent_class)->finalize (object);
714 }
715
716 GtkItemFactory*
717 gtk_item_factory_from_widget (GtkWidget        *widget)
718 {
719   g_return_val_if_fail (widget != NULL, NULL);
720   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
721
722   return gtk_object_get_data_by_id (GTK_OBJECT (widget), quark_item_factory);
723 }
724
725 gchar*
726 gtk_item_factory_path_from_widget (GtkWidget        *widget)
727 {
728   g_return_val_if_fail (widget != NULL, NULL);
729   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
730
731   return gtk_object_get_data_by_id (GTK_OBJECT (widget), quark_item_path);
732 }
733
734 static gchar *
735 item_factory_escape_path (const gchar *path)
736 {
737   GString *str = g_string_new (NULL);
738
739   while (*path)
740     {
741       gchar c = *path;
742
743       switch (c)
744         {
745         case '\n':
746           g_string_append (str, "\\n");
747           break;
748         case '\r':
749           g_string_append (str, "\\r");
750           break;
751         case '"':
752         case '\\':
753           g_string_append_c (str, '\\');
754           /* Fall through */
755         default:
756           g_string_append_c (str, c);
757         }
758       
759       path++;
760     }
761
762   return g_string_free (str, FALSE);
763 }
764
765 static void
766 gtk_item_factory_foreach (gpointer hash_key,
767                           gpointer value,
768                           gpointer user_data)
769 {
770   GtkItemFactoryItem *item;
771   GtkIFDumpData *data;
772   gchar *string;
773   gchar *path;
774   gchar *name;
775   gchar comment_prefix[2] = "\000\000";
776
777   item = value;
778   data = user_data;
779
780   if (data->pspec && !g_pattern_match_string (data->pspec, item->path))
781     return;
782
783   comment_prefix[0] = gtk_item_factory_class->cpair_comment_single[0];
784
785   path = item_factory_escape_path (hash_key);
786   name = gtk_accelerator_name (item->accelerator_key, item->accelerator_mods);
787   string = g_strconcat (item->modified ? "" : comment_prefix,
788                         "(menu-path \"",
789                         path,
790                         "\" \"",
791                         name,
792                         "\")",
793                         NULL);
794   g_free (path);
795   g_free (name);
796
797   data->print_func (data->func_data, string);
798
799   g_free (string);
800 }
801
802 void
803 gtk_item_factory_dump_items (GPatternSpec        *path_pspec,
804                              gboolean             modified_only,
805                              GtkPrintFunc         print_func,
806                              gpointer             func_data)
807 {
808   GtkIFDumpData data;
809
810   g_return_if_fail (print_func != NULL);
811
812   if (!gtk_item_factory_class)
813     gtk_type_class (GTK_TYPE_ITEM_FACTORY);
814
815   data.print_func = print_func;
816   data.func_data = func_data;
817   data.modified_only = (modified_only != FALSE);
818   data.pspec = path_pspec;
819
820   g_hash_table_foreach (gtk_item_factory_class->item_ht, gtk_item_factory_foreach, &data);
821 }
822
823 void
824 gtk_item_factory_print_func (gpointer     FILE_pointer,
825                              const gchar *string)
826 {
827   FILE *f_out = FILE_pointer;
828
829   g_return_if_fail (FILE_pointer != NULL);
830   g_return_if_fail (string != NULL);
831   
832   fputs (string, f_out);
833   fputc ('\n', f_out);
834 }
835
836 void
837 gtk_item_factory_dump_rc (const gchar  *file_name,
838                           GPatternSpec *path_pspec,
839                           gboolean      modified_only)
840 {
841   FILE *f_out;
842
843   g_return_if_fail (file_name != NULL);
844
845   f_out = fopen (file_name, "w");
846   if (!f_out)
847     return;
848
849   fputs ("; ", f_out);
850   if (g_get_prgname ())
851     fputs (g_get_prgname (), f_out);
852   fputs (" GtkItemFactory rc-file         -*- scheme -*-\n", f_out);
853   fputs ("; this file is an automated menu-path dump\n", f_out);
854   fputs (";\n", f_out);
855
856   gtk_item_factory_dump_items (path_pspec,
857                                modified_only,
858                                gtk_item_factory_print_func,
859                                f_out);
860
861   fclose (f_out);
862 }
863
864 void
865 gtk_item_factory_create_items (GtkItemFactory      *ifactory,
866                                guint                n_entries,
867                                GtkItemFactoryEntry *entries,
868                                gpointer             callback_data)
869 {
870   gtk_item_factory_create_items_ac (ifactory, n_entries, entries, callback_data, 1);
871 }
872
873 void
874 gtk_item_factory_create_items_ac (GtkItemFactory      *ifactory,
875                                   guint                n_entries,
876                                   GtkItemFactoryEntry *entries,
877                                   gpointer             callback_data,
878                                   guint                callback_type)
879 {
880   guint i;
881
882   g_return_if_fail (ifactory != NULL);
883   g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
884   g_return_if_fail (callback_type >= 1 && callback_type <= 2);
885
886   if (n_entries == 0)
887     return;
888
889   g_return_if_fail (entries != NULL);
890
891   for (i = 0; i < n_entries; i++)
892     gtk_item_factory_create_item (ifactory, entries + i, callback_data, callback_type);
893 }
894
895 GtkWidget*
896 gtk_item_factory_get_widget (GtkItemFactory *ifactory,
897                              const gchar    *path)
898 {
899   GtkItemFactoryClass *class;
900   GtkItemFactoryItem *item;
901
902   g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);
903   g_return_val_if_fail (path != NULL, NULL);
904
905   class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);
906
907   if (path[0] == '<')
908     item = g_hash_table_lookup (class->item_ht, (gpointer) path);
909   else
910     {
911       gchar *fpath;
912
913       fpath = g_strconcat (ifactory->path, path, NULL);
914       item = g_hash_table_lookup (class->item_ht, fpath);
915       g_free (fpath);
916     }
917
918   if (item)
919     {
920       GSList *slist;
921
922       for (slist = item->widgets; slist; slist = slist->next)
923         {
924           if (gtk_item_factory_from_widget (slist->data) == ifactory)
925             return slist->data;
926         }
927     }
928
929   return NULL;
930 }
931
932 GtkWidget*
933 gtk_item_factory_get_widget_by_action (GtkItemFactory *ifactory,
934                                        guint           action)
935 {
936   GSList *slist;
937
938   g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);
939
940   for (slist = ifactory->items; slist; slist = slist->next)
941     {
942       GtkItemFactoryItem *item = slist->data;
943       GSList *link;
944
945       for (link = item->widgets; link; link = link->next)
946         if (gtk_object_get_data_by_id (link->data, quark_item_factory) == ifactory &&
947             gtk_object_get_data_by_id (link->data, quark_action) == GUINT_TO_POINTER (action))
948           return link->data;
949     }
950
951   return NULL;
952 }
953
954 GtkWidget*
955 gtk_item_factory_get_item (GtkItemFactory *ifactory,
956                            const gchar    *path)
957 {
958   GtkWidget *widget;
959
960   g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);
961   g_return_val_if_fail (path != NULL, NULL);
962
963   widget = gtk_item_factory_get_widget (ifactory, path);
964
965   if (GTK_IS_MENU (widget))
966     widget = gtk_menu_get_attach_widget (GTK_MENU (widget));
967
968   return GTK_IS_ITEM (widget) ? widget : NULL;
969 }
970
971 GtkWidget*
972 gtk_item_factory_get_item_by_action (GtkItemFactory *ifactory,
973                                      guint           action)
974 {
975   GtkWidget *widget;
976
977   g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);
978
979   widget = gtk_item_factory_get_widget_by_action (ifactory, action);
980
981   if (GTK_IS_MENU (widget))
982     widget = gtk_menu_get_attach_widget (GTK_MENU (widget));
983
984   return GTK_IS_ITEM (widget) ? widget : NULL;
985 }
986
987 static char *
988 item_factory_find_separator_r (char *path)
989 {
990   gchar *result = NULL;
991   gboolean escaped = FALSE;
992
993   while (*path)
994     {
995       if (escaped)
996         escaped = FALSE;
997       else
998         {
999           if (*path == '\\')
1000             escaped = TRUE;
1001           else if (*path == '/')
1002             result = path;
1003         }
1004       
1005       path++;
1006     }
1007
1008   return result;
1009 }
1010
1011 static char *
1012 item_factory_unescape_label (const char *label)
1013 {
1014   char *new = g_malloc (strlen (label) + 1);
1015   char *p = new;
1016   gboolean escaped = FALSE;
1017   
1018   while (*label)
1019     {
1020       if (escaped)
1021         {
1022           *p++ = *label;
1023           escaped = FALSE;
1024         }
1025       else
1026         {
1027           if (*label == '\\')
1028             escaped = TRUE;
1029           else
1030             *p++ = *label;
1031         }
1032       
1033       label++;
1034     }
1035
1036   *p = '\0';
1037
1038   return new;
1039 }
1040
1041 static gboolean
1042 gtk_item_factory_parse_path (GtkItemFactory *ifactory,
1043                              gchar          *str,
1044                              gchar         **path,
1045                              gchar         **parent_path,
1046                              gchar         **item)
1047 {
1048   gchar *translation;
1049   gchar *p, *q;
1050   
1051   *path = g_strdup (str);
1052
1053   p = q = *path;
1054   while (*p)
1055     {
1056       if (*p == '_')
1057         {
1058           if (p[1] == '_')
1059             {
1060               p++;
1061               *q++ = '_';
1062             }
1063         }
1064       else
1065         {
1066           *q++ = *p;
1067         }
1068       p++;
1069     }
1070   *q = 0;
1071
1072   *parent_path = g_strdup (*path);
1073   p = item_factory_find_separator_r (*parent_path);
1074   if (!p)
1075     {
1076       g_warning ("GtkItemFactory: invalid entry path `%s'", str);
1077       return FALSE;
1078     }
1079   *p = 0;
1080
1081   if (ifactory->translate_func)
1082     translation = ifactory->translate_func (str, ifactory->translate_data);
1083   else
1084     translation = str;
1085                               
1086   p = item_factory_find_separator_r (translation);
1087   if (p)
1088     p++;
1089   else
1090     p = translation;
1091
1092   *item = item_factory_unescape_label (p);
1093
1094   return TRUE;
1095 }
1096
1097 void
1098 gtk_item_factory_create_item (GtkItemFactory         *ifactory,
1099                               GtkItemFactoryEntry    *entry,
1100                               gpointer                callback_data,
1101                               guint                   callback_type)
1102 {
1103   GtkOptionMenu *option_menu = NULL;
1104   GtkWidget *parent;
1105   GtkWidget *widget;
1106   GtkWidget *image;
1107   GSList *radio_group;
1108   gchar *name;
1109   gchar *parent_path;
1110   gchar *path;
1111   gchar *accelerator;
1112   guint type_id;
1113   GtkType type;
1114   gchar *item_type_path;
1115   GtkStockItem stock_item;
1116       
1117   g_return_if_fail (ifactory != NULL);
1118   g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
1119   g_return_if_fail (entry != NULL);
1120   g_return_if_fail (entry->path != NULL);
1121   g_return_if_fail (entry->path[0] == '/');
1122   g_return_if_fail (callback_type >= 1 && callback_type <= 2);
1123
1124   if (!entry->item_type ||
1125       entry->item_type[0] == 0)
1126     {
1127       item_type_path = "<Item>";
1128       type_id = quark_type_item;
1129     }
1130   else
1131     {
1132       item_type_path = entry->item_type;
1133       type_id = gtk_object_data_try_key (item_type_path);
1134     }
1135
1136   radio_group = NULL;
1137   if (type_id == quark_type_item)
1138     type = GTK_TYPE_MENU_ITEM;
1139   else if (type_id == quark_type_title)
1140     type = GTK_TYPE_MENU_ITEM;
1141   else if (type_id == quark_type_radio_item)
1142     type = GTK_TYPE_RADIO_MENU_ITEM;
1143   else if (type_id == quark_type_check_item)
1144     type = GTK_TYPE_CHECK_MENU_ITEM;
1145   else if (type_id == quark_type_image_item)
1146     type = GTK_TYPE_IMAGE_MENU_ITEM;
1147   else if (type_id == quark_type_stock_item)
1148     type = GTK_TYPE_IMAGE_MENU_ITEM;
1149   else if (type_id == quark_type_tearoff_item)
1150     type = GTK_TYPE_TEAROFF_MENU_ITEM;
1151   else if (type_id == quark_type_toggle_item)
1152     type = GTK_TYPE_CHECK_MENU_ITEM;
1153   else if (type_id == quark_type_separator_item)
1154     type = GTK_TYPE_MENU_ITEM;
1155   else if (type_id == quark_type_branch)
1156     type = GTK_TYPE_MENU_ITEM;
1157   else if (type_id == quark_type_last_branch)
1158     type = GTK_TYPE_MENU_ITEM;
1159   else
1160     {
1161       GtkWidget *radio_link;
1162
1163       radio_link = gtk_item_factory_get_widget (ifactory, item_type_path);
1164       if (radio_link && GTK_IS_RADIO_MENU_ITEM (radio_link))
1165         {
1166           type = GTK_TYPE_RADIO_MENU_ITEM;
1167           radio_group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (radio_link));
1168         }
1169       else
1170         {
1171           g_warning ("GtkItemFactory: entry path `%s' has invalid type `%s'",
1172                      entry->path,
1173                      item_type_path);
1174           return;
1175         }
1176     }
1177
1178   if (!gtk_item_factory_parse_path (ifactory, entry->path, 
1179                                     &path, &parent_path, &name))
1180     return;
1181
1182   parent = gtk_item_factory_get_widget (ifactory, parent_path);
1183   if (!parent)
1184     {
1185       GtkItemFactoryEntry pentry;
1186       gchar *ppath, *p;
1187
1188       ppath = g_strdup (entry->path);
1189       p = item_factory_find_separator_r (ppath);
1190       g_return_if_fail (p != NULL);
1191       *p = 0;
1192       pentry.path = ppath;
1193       pentry.accelerator = NULL;
1194       pentry.callback = NULL;
1195       pentry.callback_action = 0;
1196       pentry.item_type = "<Branch>";
1197
1198       gtk_item_factory_create_item (ifactory, &pentry, NULL, 1);
1199       g_free (ppath);
1200
1201       parent = gtk_item_factory_get_widget (ifactory, parent_path);
1202       g_return_if_fail (parent != NULL);
1203     }
1204   g_free (parent_path);
1205
1206   if (GTK_IS_OPTION_MENU (parent))
1207     {
1208       option_menu = GTK_OPTION_MENU (parent);
1209       if (!option_menu->menu)
1210         gtk_option_menu_set_menu (option_menu, gtk_widget_new (GTK_TYPE_MENU, NULL));
1211       parent = option_menu->menu;
1212     }
1213                               
1214   g_return_if_fail (GTK_IS_CONTAINER (parent));
1215
1216   accelerator = entry->accelerator;
1217   
1218   widget = gtk_widget_new (type,
1219                            "GtkWidget::visible", TRUE,
1220                            "GtkWidget::sensitive", (type_id != quark_type_separator_item &&
1221                                                     type_id != quark_type_title),
1222                            "GtkWidget::parent", parent,
1223                            NULL);
1224   if (option_menu && !option_menu->menu_item)
1225     gtk_option_menu_set_history (option_menu, 0);
1226
1227   if (type == GTK_TYPE_RADIO_MENU_ITEM)
1228     gtk_radio_menu_item_set_group (GTK_RADIO_MENU_ITEM (widget), radio_group);
1229   if (GTK_IS_CHECK_MENU_ITEM (widget))
1230     gtk_check_menu_item_set_show_toggle (GTK_CHECK_MENU_ITEM (widget), TRUE);
1231   if (GTK_IS_IMAGE_MENU_ITEM (widget))
1232     {
1233       GdkPixbuf *pixbuf = NULL;
1234       image = NULL;
1235
1236       pixbuf = gdk_pixbuf_new_from_inline (entry->extra_data,
1237                                            FALSE,
1238                                            entry->extra_data2,
1239                                            NULL);
1240
1241       if (pixbuf)
1242         image = gtk_image_new_from_pixbuf (pixbuf);
1243
1244       if (image)
1245         gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (widget), image);
1246
1247       if (pixbuf)
1248         g_object_unref (G_OBJECT (pixbuf));
1249     }
1250   if (type_id == quark_type_stock_item)
1251     {
1252       image = gtk_image_new_from_stock (entry->extra_data, GTK_ICON_SIZE_MENU);
1253       if (image)
1254         gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (widget), image);
1255       
1256       if (gtk_stock_lookup (entry->extra_data, &stock_item))
1257         {
1258           if (!accelerator)
1259             accelerator = gtk_accelerator_name (stock_item.keyval, stock_item.modifier);
1260         }
1261     }
1262   
1263   
1264
1265   /* install underline accelerators for this item
1266    */
1267   if (type_id != quark_type_separator_item && 
1268       type_id != quark_type_tearoff_item &&
1269       *name)
1270     {
1271       GtkWidget *label;
1272       
1273       label = gtk_widget_new (GTK_TYPE_ACCEL_LABEL,
1274                               "GtkWidget::visible", TRUE,
1275                               "GtkWidget::parent", widget,
1276                               "GtkAccelLabel::accel_widget", widget,
1277                               "GtkMisc::xalign", 0.0,
1278                               NULL);
1279       
1280       gtk_label_set_text_with_mnemonic (GTK_LABEL (label), name);
1281     }
1282   
1283   g_free (name);
1284   
1285   if (type_id == quark_type_branch ||
1286       type_id == quark_type_last_branch)
1287     {
1288       if (entry->callback)
1289         g_warning ("gtk_item_factory_create_item(): Can't specify a callback on a branch: \"%s\"",
1290                    entry->path);
1291       
1292       if (type_id == quark_type_last_branch)
1293         gtk_menu_item_right_justify (GTK_MENU_ITEM (widget));
1294       
1295       parent = widget;
1296       widget = gtk_widget_new (GTK_TYPE_MENU,
1297                                NULL);
1298       
1299       gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), widget);
1300     }      
1301   
1302   gtk_item_factory_add_item (ifactory,
1303                              path, accelerator,
1304                              (type_id == quark_type_branch ||
1305                               type_id == quark_type_last_branch) ?
1306                               (GtkItemFactoryCallback) NULL : entry->callback,
1307                              entry->callback_action, callback_data,
1308                              callback_type,
1309                              item_type_path,
1310                              widget);
1311
1312   if (accelerator != entry->accelerator)
1313     g_free (accelerator);
1314   
1315   g_free (path);
1316 }
1317
1318 void
1319 gtk_item_factory_create_menu_entries (guint              n_entries,
1320                                       GtkMenuEntry      *entries)
1321 {
1322   static GPatternSpec *pspec_separator = NULL;
1323   static GPatternSpec *pspec_check = NULL;
1324   guint i;
1325
1326   if (!n_entries)
1327     return;
1328   g_return_if_fail (entries != NULL);
1329
1330   if (!pspec_separator)
1331     {
1332       pspec_separator = g_pattern_spec_new ("*<separator>*");
1333       pspec_check = g_pattern_spec_new ("*<check>*");
1334     }
1335
1336   for (i = 0; i < n_entries; i++)
1337     {
1338       GtkItemFactory *ifactory;
1339       GtkItemFactoryEntry entry;
1340       gchar *path;
1341       gchar *cpath;
1342
1343       path = entries[i].path;
1344       ifactory = gtk_item_factory_from_path (path);
1345       if (!ifactory)
1346         {
1347           g_warning ("gtk_item_factory_create_menu_entries(): "
1348                      "entry[%u] refers to unknown item factory: \"%s\"",
1349                      i, entries[i].path);
1350           continue;
1351         }
1352
1353       while (*path != '>')
1354         path++;
1355       path++;
1356       cpath = NULL;
1357
1358       entry.path = path;
1359       entry.accelerator = entries[i].accelerator;
1360       entry.callback = entries[i].callback;
1361       entry.callback_action = 0;
1362       if (g_pattern_match_string (pspec_separator, path))
1363         entry.item_type = "<Separator>";
1364       else if (!g_pattern_match_string (pspec_check, path))
1365         entry.item_type = NULL;
1366       else
1367         {
1368           gboolean in_brace = FALSE;
1369           gchar *c;
1370           
1371           cpath = g_new (gchar, strlen (path));
1372           c = cpath;
1373           while (*path != 0)
1374             {
1375               if (*path == '<')
1376                 in_brace = TRUE;
1377               else if (*path == '>')
1378                 in_brace = FALSE;
1379               else if (!in_brace)
1380                 *(c++) = *path;
1381               path++;
1382             }
1383           *c = 0;
1384           entry.item_type = "<ToggleItem>";
1385           entry.path = cpath;
1386         }
1387       
1388       gtk_item_factory_create_item (ifactory, &entry, entries[i].callback_data, 2);
1389       entries[i].widget = gtk_item_factory_get_widget (ifactory, entries[i].path);
1390       g_free (cpath);
1391     }
1392 }
1393
1394 void
1395 gtk_item_factories_path_delete (const gchar *ifactory_path,
1396                                 const gchar *path)
1397 {
1398   GtkItemFactoryClass *class;
1399   GtkItemFactoryItem *item;
1400
1401   g_return_if_fail (path != NULL);
1402
1403   class = gtk_type_class (GTK_TYPE_ITEM_FACTORY);
1404
1405   if (path[0] == '<')
1406     item = g_hash_table_lookup (class->item_ht, (gpointer) path);
1407   else
1408     {
1409       gchar *fpath;
1410
1411       g_return_if_fail (ifactory_path != NULL);
1412       
1413       fpath = g_strconcat (ifactory_path, path, NULL);
1414       item = g_hash_table_lookup (class->item_ht, fpath);
1415       g_free (fpath);
1416     }
1417   
1418   if (item)
1419     {
1420       GSList *widget_list;
1421       GSList *slist;
1422
1423       widget_list = NULL;
1424       for (slist = item->widgets; slist; slist = slist->next)
1425         {
1426           GtkWidget *widget;
1427
1428           widget = slist->data;
1429           widget_list = g_slist_prepend (widget_list, widget);
1430           gtk_widget_ref (widget);
1431         }
1432
1433       for (slist = widget_list; slist; slist = slist->next)
1434         {
1435           GtkWidget *widget;
1436
1437           widget = slist->data;
1438           gtk_widget_destroy (widget);
1439           gtk_widget_unref (widget);
1440         }
1441       g_slist_free (widget_list);
1442     }
1443 }
1444
1445 void
1446 gtk_item_factory_delete_item (GtkItemFactory         *ifactory,
1447                               const gchar            *path)
1448 {
1449   GtkItemFactoryClass *class;
1450   GtkWidget *widget;
1451
1452   g_return_if_fail (ifactory != NULL);
1453   g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
1454   g_return_if_fail (path != NULL);
1455
1456   class = GTK_ITEM_FACTORY_GET_CLASS (ifactory);
1457
1458   widget = gtk_item_factory_get_widget (ifactory, path);
1459
1460   if (widget)
1461     {
1462       if (GTK_IS_MENU (widget))
1463         widget = gtk_menu_get_attach_widget (GTK_MENU (widget));
1464
1465       gtk_widget_destroy (widget);
1466     }
1467 }
1468
1469 void
1470 gtk_item_factory_delete_entry (GtkItemFactory         *ifactory,
1471                                GtkItemFactoryEntry    *entry)
1472 {
1473   g_return_if_fail (ifactory != NULL);
1474   g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
1475   g_return_if_fail (entry != NULL);
1476
1477   gtk_item_factory_delete_item (ifactory, entry->path);
1478 }
1479
1480 void
1481 gtk_item_factory_delete_entries (GtkItemFactory         *ifactory,
1482                                  guint                   n_entries,
1483                                  GtkItemFactoryEntry    *entries)
1484 {
1485   guint i;
1486
1487   g_return_if_fail (ifactory != NULL);
1488   g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
1489   if (n_entries > 0)
1490     g_return_if_fail (entries != NULL);
1491
1492   for (i = 0; i < n_entries; i++)
1493     gtk_item_factory_delete_item (ifactory, (entries + i)->path);
1494 }
1495
1496 typedef struct
1497 {
1498   guint x;
1499   guint y;
1500 } MenuPos;
1501
1502 static void
1503 gtk_item_factory_menu_pos (GtkMenu  *menu,
1504                            gint     *x,
1505                            gint     *y,
1506                            gboolean *push_in,
1507                            gpointer  func_data)
1508 {
1509   MenuPos *mpos = func_data;
1510
1511   *x = mpos->x;
1512   *y = mpos->y;
1513 }
1514
1515 gpointer
1516 gtk_item_factory_popup_data_from_widget (GtkWidget     *widget)
1517 {
1518   GtkItemFactory *ifactory;
1519   
1520   g_return_val_if_fail (widget != NULL, NULL);
1521   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1522
1523   ifactory = gtk_item_factory_from_widget (widget);
1524   if (ifactory)
1525     return gtk_object_get_data_by_id (GTK_OBJECT (ifactory), quark_popup_data);
1526
1527   return NULL;
1528 }
1529
1530 gpointer
1531 gtk_item_factory_popup_data (GtkItemFactory *ifactory)
1532 {
1533   g_return_val_if_fail (ifactory != NULL, NULL);
1534   g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL);
1535
1536   return gtk_object_get_data_by_id (GTK_OBJECT (ifactory), quark_popup_data);
1537 }
1538
1539 static void
1540 ifactory_delete_popup_data (GtkObject      *object,
1541                             GtkItemFactory *ifactory)
1542 {
1543   gtk_signal_disconnect_by_func (object,
1544                                  GTK_SIGNAL_FUNC (ifactory_delete_popup_data),
1545                                  ifactory);
1546   gtk_object_remove_data_by_id (GTK_OBJECT (ifactory), quark_popup_data);
1547 }
1548
1549 void
1550 gtk_item_factory_popup (GtkItemFactory          *ifactory,
1551                         guint                    x,
1552                         guint                    y,
1553                         guint                    mouse_button,
1554                         guint32                  time)
1555 {
1556   gtk_item_factory_popup_with_data (ifactory, NULL, NULL, x, y, mouse_button, time);
1557 }
1558
1559 void
1560 gtk_item_factory_popup_with_data (GtkItemFactory        *ifactory,
1561                                   gpointer               popup_data,
1562                                   GtkDestroyNotify       destroy,
1563                                   guint                  x,
1564                                   guint                  y,
1565                                   guint                  mouse_button,
1566                                   guint32                time)
1567 {
1568   MenuPos *mpos;
1569
1570   g_return_if_fail (ifactory != NULL);
1571   g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
1572   g_return_if_fail (GTK_IS_MENU (ifactory->widget));
1573   
1574   mpos = gtk_object_get_data_by_id (GTK_OBJECT (ifactory->widget), quark_if_menu_pos);
1575   
1576   if (!mpos)
1577     {
1578       mpos = g_new0 (MenuPos, 1);
1579       gtk_object_set_data_by_id_full (GTK_OBJECT (ifactory->widget),
1580                                       quark_if_menu_pos,
1581                                       mpos,
1582                                       g_free);
1583     }
1584   
1585   mpos->x = x;
1586   mpos->y = y;
1587   
1588   if (popup_data != NULL)
1589     {
1590       gtk_object_set_data_by_id_full (GTK_OBJECT (ifactory),
1591                                       quark_popup_data,
1592                                       popup_data,
1593                                       destroy);
1594       gtk_signal_connect (GTK_OBJECT (ifactory->widget),
1595                           "selection-done",
1596                           GTK_SIGNAL_FUNC (ifactory_delete_popup_data),
1597                           ifactory);
1598     }
1599   
1600   gtk_menu_popup (GTK_MENU (ifactory->widget),
1601                   NULL, NULL,
1602                   gtk_item_factory_menu_pos, mpos,
1603                   mouse_button, time);
1604 }
1605
1606 static guint
1607 gtk_item_factory_parse_menu_path (GScanner            *scanner,
1608                                   GtkItemFactoryClass *class)
1609 {
1610   GtkItemFactoryItem *item;
1611   
1612   g_scanner_get_next_token (scanner);
1613   if (scanner->token != G_TOKEN_STRING)
1614     return G_TOKEN_STRING;
1615
1616   g_scanner_peek_next_token (scanner);
1617   if (scanner->next_token != G_TOKEN_STRING)
1618     {
1619       g_scanner_get_next_token (scanner);
1620       return G_TOKEN_STRING;
1621     }
1622
1623   item = g_hash_table_lookup (class->item_ht, scanner->value.v_string);
1624   if (!item)
1625     {
1626       item = g_chunk_new (GtkItemFactoryItem, ifactory_item_chunks);
1627
1628       item->path = g_strdup (scanner->value.v_string);
1629       item->accelerator_key = 0;
1630       item->accelerator_mods = 0;
1631       item->modified = TRUE;
1632       item->in_propagation = FALSE;
1633       item->dummy = NULL;
1634       item->widgets = NULL;
1635
1636       g_hash_table_insert (class->item_ht, item->path, item);
1637     }
1638   g_scanner_get_next_token (scanner);
1639   
1640   if (!item->in_propagation)
1641     {
1642       guint old_keyval;
1643       guint old_mods;
1644       
1645       old_keyval = item->accelerator_key;
1646       old_mods = item->accelerator_mods;
1647       gtk_accelerator_parse (scanner->value.v_string,
1648                              &item->accelerator_key,
1649                              &item->accelerator_mods);
1650       if (old_keyval != item->accelerator_key ||
1651           old_mods != item->accelerator_mods)
1652         {
1653           item->modified = TRUE;
1654           gtk_item_factory_propagate_accelerator (item, NULL);
1655         }
1656     }
1657   
1658   g_scanner_get_next_token (scanner);
1659   if (scanner->token != ')')
1660     return ')';
1661   else
1662     return G_TOKEN_NONE;
1663 }
1664
1665 static void
1666 gtk_item_factory_parse_statement (GScanner            *scanner,
1667                                   GtkItemFactoryClass *class)
1668 {
1669   guint expected_token;
1670   
1671   g_scanner_get_next_token (scanner);
1672   
1673   if (scanner->token == G_TOKEN_SYMBOL)
1674     {
1675       guint (*parser_func) (GScanner*, GtkItemFactoryClass*);
1676
1677       parser_func = scanner->value.v_symbol;
1678
1679       /* check whether this is a GtkItemFactory symbol.
1680        */
1681       if (parser_func == gtk_item_factory_parse_menu_path)
1682         expected_token = parser_func (scanner, class);
1683       else
1684         expected_token = G_TOKEN_SYMBOL;
1685     }
1686   else
1687     expected_token = G_TOKEN_SYMBOL;
1688
1689   /* skip rest of statement on errrors
1690    */
1691   if (expected_token != G_TOKEN_NONE)
1692     {
1693       register guint level;
1694
1695       level = 1;
1696       if (scanner->token == ')')
1697         level--;
1698       if (scanner->token == '(')
1699         level++;
1700       
1701       while (!g_scanner_eof (scanner) && level > 0)
1702         {
1703           g_scanner_get_next_token (scanner);
1704           
1705           if (scanner->token == '(')
1706             level++;
1707           else if (scanner->token == ')')
1708             level--;
1709         }
1710     }
1711 }
1712
1713 void
1714 gtk_item_factory_parse_rc_string (const gchar    *rc_string)
1715 {
1716   GScanner *scanner;
1717
1718   g_return_if_fail (rc_string != NULL);
1719
1720   if (!gtk_item_factory_class)
1721     gtk_type_class (GTK_TYPE_ITEM_FACTORY);
1722
1723   ifactory_scanner_config.cpair_comment_single = gtk_item_factory_class->cpair_comment_single;
1724   scanner = g_scanner_new (&ifactory_scanner_config);
1725
1726   g_scanner_input_text (scanner, rc_string, strlen (rc_string));
1727
1728   gtk_item_factory_parse_rc_scanner (scanner);
1729
1730   g_scanner_destroy (scanner);
1731 }
1732
1733 void
1734 gtk_item_factory_parse_rc_scanner (GScanner *scanner)
1735 {
1736   gpointer saved_symbol;
1737
1738   g_return_if_fail (scanner != NULL);
1739
1740   if (!gtk_item_factory_class)
1741     gtk_type_class (GTK_TYPE_ITEM_FACTORY);
1742
1743   saved_symbol = g_scanner_lookup_symbol (scanner, "menu-path");
1744   g_scanner_remove_symbol (scanner, "menu-path");
1745   g_scanner_add_symbol (scanner, "menu-path", gtk_item_factory_parse_menu_path);
1746
1747   g_scanner_peek_next_token (scanner);
1748
1749   while (scanner->next_token == '(')
1750     {
1751       g_scanner_get_next_token (scanner);
1752
1753       gtk_item_factory_parse_statement (scanner, gtk_item_factory_class);
1754
1755       g_scanner_peek_next_token (scanner);
1756     }
1757
1758   g_scanner_remove_symbol (scanner, "menu-path");
1759   g_scanner_add_symbol (scanner, "menu-path", saved_symbol);
1760 }
1761
1762 void
1763 gtk_item_factory_parse_rc (const gchar    *file_name)
1764 {
1765   gint fd;
1766   GScanner *scanner;
1767
1768   g_return_if_fail (file_name != NULL);
1769
1770   if (!S_ISREG (g_scanner_stat_mode (file_name)))
1771     return;
1772
1773   fd = open (file_name, O_RDONLY);
1774   if (fd < 0)
1775     return;
1776
1777   if (!gtk_item_factory_class)
1778     gtk_type_class (GTK_TYPE_ITEM_FACTORY);
1779
1780   ifactory_scanner_config.cpair_comment_single = gtk_item_factory_class->cpair_comment_single;
1781   scanner = g_scanner_new (&ifactory_scanner_config);
1782
1783   g_scanner_input_file (scanner, fd);
1784
1785   gtk_item_factory_parse_rc_scanner (scanner);
1786
1787   g_scanner_destroy (scanner);
1788
1789   close (fd);
1790 }
1791
1792 void
1793 gtk_item_factory_set_translate_func (GtkItemFactory      *ifactory,
1794                                      GtkTranslateFunc     func,
1795                                      gpointer             data,
1796                                      GtkDestroyNotify     notify)
1797 {
1798   g_return_if_fail (ifactory != NULL);
1799   
1800   if (ifactory->translate_notify)
1801     ifactory->translate_notify (ifactory->translate_data);
1802       
1803   ifactory->translate_func = func;
1804   ifactory->translate_data = data;
1805   ifactory->translate_notify = notify;
1806 }