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