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