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