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