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