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