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