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