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