]> Pileus Git - ~andy/gtk/blob - gtk/gtkuimanager.c
24267a6bfa301ec0a748d50f24880da59d35de27
[~andy/gtk] / gtk / gtkuimanager.c
1 /*
2  * GTK - The GIMP Toolkit
3  * Copyright (C) 1998, 1999 Red Hat, Inc.
4  * All rights reserved.
5  *
6  * This Library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This Library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
18  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /*
23  * Author: James Henstridge <james@daa.com.au>
24  *
25  * Modified by the GTK+ Team and others 2003.  See the AUTHORS
26  * file for a list of people on the GTK+ Team.  See the ChangeLog
27  * files for a list of changes.  These files are distributed with
28  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
29  */
30
31 #include "config.h"
32
33 #include <string.h>
34 #include "gtkactivatable.h"
35 #include "gtkbuildable.h"
36 #include "gtkintl.h"
37 #include "gtkmarshalers.h"
38 #include "gtkmenu.h"
39 #include "gtkmenubar.h"
40 #include "gtkmenutoolbutton.h"
41 #include "gtkseparatormenuitem.h"
42 #include "gtkseparatortoolitem.h"
43 #include "gtktearoffmenuitem.h"
44 #include "gtktoolbar.h"
45 #include "gtkuimanager.h"
46 #include "gtkwindow.h"
47 #include "gtkprivate.h"
48 #include "gtkalias.h"
49
50 #undef DEBUG_UI_MANAGER
51
52 typedef enum
53 {
54   NODE_TYPE_UNDECIDED,
55   NODE_TYPE_ROOT,
56   NODE_TYPE_MENUBAR,
57   NODE_TYPE_MENU,
58   NODE_TYPE_TOOLBAR,
59   NODE_TYPE_MENU_PLACEHOLDER,
60   NODE_TYPE_TOOLBAR_PLACEHOLDER,
61   NODE_TYPE_POPUP,
62   NODE_TYPE_MENUITEM,
63   NODE_TYPE_TOOLITEM,
64   NODE_TYPE_SEPARATOR,
65   NODE_TYPE_ACCELERATOR
66 } NodeType;
67
68 typedef struct _Node Node;
69
70 struct _Node {
71   NodeType type;
72
73   gchar *name;
74
75   GQuark action_name;
76   GtkAction *action;
77   GtkWidget *proxy;
78   GtkWidget *extra; /* second separator for placeholders */
79
80   GList *uifiles;
81
82   guint dirty : 1;
83   guint expand : 1;  /* used for separators */
84   guint popup_accels : 1;
85 };
86
87 #define GTK_UI_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_UI_MANAGER, GtkUIManagerPrivate))
88
89 struct _GtkUIManagerPrivate 
90 {
91   GtkAccelGroup *accel_group;
92
93   GNode *root_node;
94   GList *action_groups;
95
96   guint last_merge_id;
97
98   guint update_tag;  
99
100   gboolean add_tearoffs;
101 };
102
103 #define NODE_INFO(node) ((Node *)node->data)
104
105 typedef struct _NodeUIReference NodeUIReference;
106
107 struct _NodeUIReference 
108 {
109   guint merge_id;
110   GQuark action_quark;
111 };
112
113 static void        gtk_ui_manager_finalize        (GObject           *object);
114 static void        gtk_ui_manager_set_property    (GObject           *object,
115                                                    guint              prop_id,
116                                                    const GValue      *value,
117                                                    GParamSpec        *pspec);
118 static void        gtk_ui_manager_get_property    (GObject           *object,
119                                                    guint              prop_id,
120                                                    GValue            *value,
121                                                    GParamSpec        *pspec);
122 static GtkWidget * gtk_ui_manager_real_get_widget (GtkUIManager      *manager,
123                                                    const gchar       *path);
124 static GtkAction * gtk_ui_manager_real_get_action (GtkUIManager      *manager,
125                                                    const gchar       *path);
126 static void        queue_update                   (GtkUIManager      *self);
127 static void        dirty_all_nodes                (GtkUIManager      *self);
128 static void        mark_node_dirty                (GNode             *node);
129 static GNode     * get_child_node                 (GtkUIManager      *self,
130                                                    GNode             *parent,
131                                                    GNode             *sibling,
132                                                    const gchar       *childname,
133                                                    gint               childname_length,
134                                                    NodeType           node_type,
135                                                    gboolean           create,
136                                                    gboolean           top);
137 static GNode     * get_node                       (GtkUIManager      *self,
138                                                    const gchar       *path,
139                                                    NodeType           node_type,
140                                                    gboolean           create);
141 static gboolean    free_node                      (GNode             *node);
142 static void        node_prepend_ui_reference      (GNode             *node,
143                                                    guint              merge_id,
144                                                    GQuark             action_quark);
145 static void        node_remove_ui_reference       (GNode             *node,
146                                                    guint              merge_id);
147
148 /* GtkBuildable */
149 static void gtk_ui_manager_buildable_init      (GtkBuildableIface *iface);
150 static void gtk_ui_manager_buildable_add_child (GtkBuildable  *buildable,
151                                                 GtkBuilder    *builder,
152                                                 GObject       *child,
153                                                 const gchar   *type);
154 static GObject* gtk_ui_manager_buildable_construct_child (GtkBuildable *buildable,
155                                                           GtkBuilder   *builder,
156                                                           const gchar  *name);
157 static gboolean gtk_ui_manager_buildable_custom_tag_start (GtkBuildable  *buildable,
158                                                            GtkBuilder    *builder,
159                                                            GObject       *child,
160                                                            const gchar   *tagname,
161                                                            GMarkupParser *parser,
162                                                            gpointer      *data);
163 static void     gtk_ui_manager_buildable_custom_tag_end (GtkBuildable    *buildable,
164                                                          GtkBuilder      *builder,
165                                                          GObject         *child,
166                                                          const gchar     *tagname,
167                                                          gpointer        *data);
168
169
170
171 enum 
172 {
173   ADD_WIDGET,
174   ACTIONS_CHANGED,
175   CONNECT_PROXY,
176   DISCONNECT_PROXY,
177   PRE_ACTIVATE,
178   POST_ACTIVATE,
179   LAST_SIGNAL
180 };
181
182 enum
183 {
184   PROP_0,
185   PROP_ADD_TEAROFFS,
186   PROP_UI
187 };
188
189 static guint ui_manager_signals[LAST_SIGNAL] = { 0 };
190
191 G_DEFINE_TYPE_WITH_CODE (GtkUIManager, gtk_ui_manager, G_TYPE_OBJECT,
192                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
193                                                 gtk_ui_manager_buildable_init))
194
195 static void
196 gtk_ui_manager_class_init (GtkUIManagerClass *klass)
197 {
198   GObjectClass *gobject_class;
199
200   gobject_class = G_OBJECT_CLASS (klass);
201
202   gobject_class->finalize = gtk_ui_manager_finalize;
203   gobject_class->set_property = gtk_ui_manager_set_property;
204   gobject_class->get_property = gtk_ui_manager_get_property;
205   klass->get_widget = gtk_ui_manager_real_get_widget;
206   klass->get_action = gtk_ui_manager_real_get_action;
207
208   /**
209    * GtkUIManager:add-tearoffs:
210    *
211    * The "add-tearoffs" property controls whether generated menus 
212    * have tearoff menu items. 
213    *
214    * Note that this only affects regular menus. Generated popup 
215    * menus never have tearoff menu items.   
216    *
217    * Since: 2.4
218    */
219   g_object_class_install_property (gobject_class,
220                                    PROP_ADD_TEAROFFS,
221                                    g_param_spec_boolean ("add-tearoffs",
222                                                          P_("Add tearoffs to menus"),
223                                                          P_("Whether tearoff menu items should be added to menus"),
224                                                          FALSE,
225                                                          GTK_PARAM_READWRITE));
226
227   g_object_class_install_property (gobject_class,
228                                    PROP_UI,
229                                    g_param_spec_string ("ui",
230                                                         P_("Merged UI definition"),
231                                                         P_("An XML string describing the merged UI"),
232                                                         "<ui>\n</ui>\n",
233                                                         GTK_PARAM_READABLE));
234
235
236   /**
237    * GtkUIManager::add-widget:
238    * @merge: a #GtkUIManager
239    * @widget: the added widget
240    *
241    * The add_widget signal is emitted for each generated menubar and toolbar.
242    * It is not emitted for generated popup menus, which can be obtained by 
243    * gtk_ui_manager_get_widget().
244    *
245    * Since: 2.4
246    */
247   ui_manager_signals[ADD_WIDGET] =
248     g_signal_new (I_("add-widget"),
249                   G_OBJECT_CLASS_TYPE (klass),
250                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
251                   G_STRUCT_OFFSET (GtkUIManagerClass, add_widget),
252                   NULL, NULL,
253                   g_cclosure_marshal_VOID__OBJECT,
254                   G_TYPE_NONE, 1, 
255                   GTK_TYPE_WIDGET);
256
257   /**
258    * GtkUIManager::actions-changed:
259    * @merge: a #GtkUIManager
260    *
261    * The "actions-changed" signal is emitted whenever the set of actions
262    * changes.
263    *
264    * Since: 2.4
265    */
266   ui_manager_signals[ACTIONS_CHANGED] =
267     g_signal_new (I_("actions-changed"),
268                   G_OBJECT_CLASS_TYPE (klass),
269                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
270                   G_STRUCT_OFFSET (GtkUIManagerClass, actions_changed),  
271                   NULL, NULL,
272                   g_cclosure_marshal_VOID__VOID,
273                   G_TYPE_NONE, 0);
274   
275   /**
276    * GtkUIManager::connect-proxy:
277    * @uimanager: the ui manager
278    * @action: the action
279    * @proxy: the proxy
280    *
281    * The connect_proxy signal is emitted after connecting a proxy to 
282    * an action in the group. 
283    *
284    * This is intended for simple customizations for which a custom action
285    * class would be too clumsy, e.g. showing tooltips for menuitems in the
286    * statusbar.
287    *
288    * Since: 2.4
289    */
290   ui_manager_signals[CONNECT_PROXY] =
291     g_signal_new (I_("connect-proxy"),
292                   G_OBJECT_CLASS_TYPE (klass),
293                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
294                   G_STRUCT_OFFSET (GtkUIManagerClass, connect_proxy),
295                   NULL, NULL,
296                   _gtk_marshal_VOID__OBJECT_OBJECT,
297                   G_TYPE_NONE, 2, 
298                   GTK_TYPE_ACTION,
299                   GTK_TYPE_WIDGET);
300
301   /**
302    * GtkUIManager::disconnect-proxy:
303    * @uimanager: the ui manager
304    * @action: the action
305    * @proxy: the proxy
306    *
307    * The disconnect_proxy signal is emitted after disconnecting a proxy 
308    * from an action in the group. 
309    *
310    * Since: 2.4
311    */
312   ui_manager_signals[DISCONNECT_PROXY] =
313     g_signal_new (I_("disconnect-proxy"),
314                   G_OBJECT_CLASS_TYPE (klass),
315                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
316                   G_STRUCT_OFFSET (GtkUIManagerClass, disconnect_proxy),
317                   NULL, NULL,
318                   _gtk_marshal_VOID__OBJECT_OBJECT,
319                   G_TYPE_NONE, 2,
320                   GTK_TYPE_ACTION,
321                   GTK_TYPE_WIDGET);
322
323   /**
324    * GtkUIManager::pre-activate:
325    * @uimanager: the ui manager
326    * @action: the action
327    *
328    * The pre_activate signal is emitted just before the @action
329    * is activated.
330    *
331    * This is intended for applications to get notification
332    * just before any action is activated.
333    *
334    * Since: 2.4
335    */
336   ui_manager_signals[PRE_ACTIVATE] =
337     g_signal_new (I_("pre-activate"),
338                   G_OBJECT_CLASS_TYPE (klass),
339                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
340                   G_STRUCT_OFFSET (GtkUIManagerClass, pre_activate),
341                   NULL, NULL,
342                   _gtk_marshal_VOID__OBJECT,
343                   G_TYPE_NONE, 1,
344                   GTK_TYPE_ACTION);
345
346   /**
347    * GtkUIManager::post-activate:
348    * @uimanager: the ui manager
349    * @action: the action
350    *
351    * The post_activate signal is emitted just after the @action
352    * is activated.
353    *
354    * This is intended for applications to get notification
355    * just after any action is activated.
356    *
357    * Since: 2.4
358    */
359   ui_manager_signals[POST_ACTIVATE] =
360     g_signal_new (I_("post-activate"),
361                   G_OBJECT_CLASS_TYPE (klass),
362                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
363                   G_STRUCT_OFFSET (GtkUIManagerClass, post_activate),
364                   NULL, NULL,
365                   _gtk_marshal_VOID__OBJECT,
366                   G_TYPE_NONE, 1,
367                   GTK_TYPE_ACTION);
368
369   klass->add_widget = NULL;
370   klass->actions_changed = NULL;
371   klass->connect_proxy = NULL;
372   klass->disconnect_proxy = NULL;
373   klass->pre_activate = NULL;
374   klass->post_activate = NULL;
375
376   g_type_class_add_private (gobject_class, sizeof (GtkUIManagerPrivate));
377 }
378
379
380 static void
381 gtk_ui_manager_init (GtkUIManager *self)
382 {
383   guint merge_id;
384   GNode *node;
385
386   self->private_data = GTK_UI_MANAGER_GET_PRIVATE (self);
387
388   self->private_data->accel_group = gtk_accel_group_new ();
389
390   self->private_data->root_node = NULL;
391   self->private_data->action_groups = NULL;
392
393   self->private_data->last_merge_id = 0;
394   self->private_data->add_tearoffs = FALSE;
395
396   merge_id = gtk_ui_manager_new_merge_id (self);
397   node = get_child_node (self, NULL, NULL, "ui", 2,
398                          NODE_TYPE_ROOT, TRUE, FALSE);
399   node_prepend_ui_reference (node, merge_id, 0);
400 }
401
402 static void
403 gtk_ui_manager_finalize (GObject *object)
404 {
405   GtkUIManager *self = GTK_UI_MANAGER (object);
406   
407   if (self->private_data->update_tag != 0)
408     {
409       g_source_remove (self->private_data->update_tag);
410       self->private_data->update_tag = 0;
411     }
412   
413   g_node_traverse (self->private_data->root_node, 
414                    G_POST_ORDER, G_TRAVERSE_ALL, -1,
415                    (GNodeTraverseFunc)free_node, NULL);
416   g_node_destroy (self->private_data->root_node);
417   self->private_data->root_node = NULL;
418   
419   g_list_foreach (self->private_data->action_groups,
420                   (GFunc) g_object_unref, NULL);
421   g_list_free (self->private_data->action_groups);
422   self->private_data->action_groups = NULL;
423
424   g_object_unref (self->private_data->accel_group);
425   self->private_data->accel_group = NULL;
426
427   G_OBJECT_CLASS (gtk_ui_manager_parent_class)->finalize (object);
428 }
429
430 static void
431 gtk_ui_manager_buildable_init (GtkBuildableIface *iface)
432 {
433   iface->add_child = gtk_ui_manager_buildable_add_child;
434   iface->construct_child = gtk_ui_manager_buildable_construct_child;
435   iface->custom_tag_start = gtk_ui_manager_buildable_custom_tag_start;
436   iface->custom_tag_end = gtk_ui_manager_buildable_custom_tag_end;
437 }
438
439 static void
440 gtk_ui_manager_buildable_add_child (GtkBuildable  *buildable,
441                                     GtkBuilder    *builder,
442                                     GObject       *child,
443                                     const gchar   *type)
444 {
445   GtkUIManager *self = GTK_UI_MANAGER (buildable);
446   guint pos;
447
448   g_return_if_fail (GTK_IS_ACTION_GROUP (child));
449
450   pos = g_list_length (self->private_data->action_groups);
451
452   g_object_ref (child);
453   gtk_ui_manager_insert_action_group (self,
454                                       GTK_ACTION_GROUP (child),
455                                       pos);
456 }
457
458 static void
459 child_hierarchy_changed_cb (GtkWidget *widget,
460                             GtkWidget *unused,
461                             GtkUIManager *uimgr)
462 {
463   GtkWidget *toplevel;
464   GtkAccelGroup *group;
465   GSList *groups;
466
467   toplevel = gtk_widget_get_toplevel (widget);
468   if (!toplevel || !GTK_IS_WINDOW (toplevel))
469     return;
470   
471   group = gtk_ui_manager_get_accel_group (uimgr);
472   groups = gtk_accel_groups_from_object (G_OBJECT (toplevel));
473   if (g_slist_find (groups, group) == NULL)
474     gtk_window_add_accel_group (GTK_WINDOW (toplevel), group);
475
476   g_signal_handlers_disconnect_by_func (widget,
477                                         child_hierarchy_changed_cb,
478                                         uimgr);
479 }
480
481 static GObject *
482 gtk_ui_manager_buildable_construct_child (GtkBuildable *buildable,
483                                           GtkBuilder   *builder,
484                                           const gchar  *id)
485 {
486   GtkWidget *widget;
487   char *name;
488
489   name = g_strdup_printf ("ui/%s", id);
490   widget = gtk_ui_manager_get_widget (GTK_UI_MANAGER (buildable), name);
491   if (!widget)
492     {
493       g_error ("Unknown ui manager child: %s\n", name);
494       g_free (name);
495       return NULL;
496     }
497   g_free (name);
498
499   g_signal_connect (widget, "hierarchy-changed",
500                     G_CALLBACK (child_hierarchy_changed_cb),
501                     GTK_UI_MANAGER (buildable));
502   return g_object_ref (widget);
503 }
504
505 static void
506 gtk_ui_manager_set_property (GObject         *object,
507                              guint            prop_id,
508                              const GValue    *value,
509                              GParamSpec      *pspec)
510 {
511   GtkUIManager *self = GTK_UI_MANAGER (object);
512  
513   switch (prop_id)
514     {
515     case PROP_ADD_TEAROFFS:
516       gtk_ui_manager_set_add_tearoffs (self, g_value_get_boolean (value));
517       break;
518     default:
519       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
520       break;
521     }
522 }
523
524 static void
525 gtk_ui_manager_get_property (GObject         *object,
526                              guint            prop_id,
527                              GValue          *value,
528                              GParamSpec      *pspec)
529 {
530   GtkUIManager *self = GTK_UI_MANAGER (object);
531
532   switch (prop_id)
533     {
534     case PROP_ADD_TEAROFFS:
535       g_value_set_boolean (value, self->private_data->add_tearoffs);
536       break;
537     case PROP_UI:
538       g_value_take_string (value, gtk_ui_manager_get_ui (self));
539       break;
540     default:
541       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
542       break;
543     }
544 }
545
546 static GtkWidget *
547 gtk_ui_manager_real_get_widget (GtkUIManager *self,
548                                 const gchar  *path)
549 {
550   GNode *node;
551
552   /* ensure that there are no pending updates before we get the
553    * widget */
554   gtk_ui_manager_ensure_update (self);
555
556   node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
557
558   if (node == NULL)
559     return NULL;
560
561   return NODE_INFO (node)->proxy;
562 }
563
564 static GtkAction *
565 gtk_ui_manager_real_get_action (GtkUIManager *self,
566                                 const gchar  *path)
567 {
568   GNode *node;
569
570   /* ensure that there are no pending updates before we get
571    * the action */
572   gtk_ui_manager_ensure_update (self);
573
574   node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
575
576   if (node == NULL)
577     return NULL;
578
579   return NODE_INFO (node)->action;
580 }
581
582
583 /**
584  * gtk_ui_manager_new:
585  * 
586  * Creates a new ui manager object.
587  * 
588  * Return value: a new ui manager object.
589  *
590  * Since: 2.4
591  **/
592 GtkUIManager*
593 gtk_ui_manager_new (void)
594 {
595   return g_object_new (GTK_TYPE_UI_MANAGER, NULL);
596 }
597
598
599 /**
600  * gtk_ui_manager_get_add_tearoffs:
601  * @self: a #GtkUIManager
602  * 
603  * Returns whether menus generated by this #GtkUIManager
604  * will have tearoff menu items. 
605  * 
606  * Return value: whether tearoff menu items are added
607  *
608  * Since: 2.4
609  **/
610 gboolean 
611 gtk_ui_manager_get_add_tearoffs (GtkUIManager *self)
612 {
613   g_return_val_if_fail (GTK_IS_UI_MANAGER (self), FALSE);
614   
615   return self->private_data->add_tearoffs;
616 }
617
618
619 /**
620  * gtk_ui_manager_set_add_tearoffs:
621  * @self: a #GtkUIManager
622  * @add_tearoffs: whether tearoff menu items are added
623  * 
624  * Sets the "add_tearoffs" property, which controls whether menus 
625  * generated by this #GtkUIManager will have tearoff menu items. 
626  *
627  * Note that this only affects regular menus. Generated popup 
628  * menus never have tearoff menu items.
629  *
630  * Since: 2.4
631  **/
632 void 
633 gtk_ui_manager_set_add_tearoffs (GtkUIManager *self,
634                                  gboolean      add_tearoffs)
635 {
636   g_return_if_fail (GTK_IS_UI_MANAGER (self));
637
638   add_tearoffs = add_tearoffs != FALSE;
639
640   if (add_tearoffs != self->private_data->add_tearoffs)
641     {
642       self->private_data->add_tearoffs = add_tearoffs;
643       
644       dirty_all_nodes (self);
645
646       g_object_notify (G_OBJECT (self), "add-tearoffs");
647     }
648 }
649
650 static void
651 cb_proxy_connect_proxy (GtkActionGroup *group, 
652                         GtkAction      *action,
653                         GtkWidget      *proxy, 
654                         GtkUIManager *self)
655 {
656   g_signal_emit (self, ui_manager_signals[CONNECT_PROXY], 0, action, proxy);
657 }
658
659 static void
660 cb_proxy_disconnect_proxy (GtkActionGroup *group, 
661                            GtkAction      *action,
662                            GtkWidget      *proxy, 
663                            GtkUIManager *self)
664 {
665   g_signal_emit (self, ui_manager_signals[DISCONNECT_PROXY], 0, action, proxy);
666 }
667
668 static void
669 cb_proxy_pre_activate (GtkActionGroup *group, 
670                        GtkAction      *action,
671                        GtkUIManager   *self)
672 {
673   g_signal_emit (self, ui_manager_signals[PRE_ACTIVATE], 0, action);
674 }
675
676 static void
677 cb_proxy_post_activate (GtkActionGroup *group, 
678                         GtkAction      *action,
679                         GtkUIManager   *self)
680 {
681   g_signal_emit (self, ui_manager_signals[POST_ACTIVATE], 0, action);
682 }
683
684 /**
685  * gtk_ui_manager_insert_action_group:
686  * @self: a #GtkUIManager object
687  * @action_group: the action group to be inserted
688  * @pos: the position at which the group will be inserted.
689  * 
690  * Inserts an action group into the list of action groups associated 
691  * with @self. Actions in earlier groups hide actions with the same 
692  * name in later groups. 
693  *
694  * Since: 2.4
695  **/
696 void
697 gtk_ui_manager_insert_action_group (GtkUIManager   *self,
698                                     GtkActionGroup *action_group, 
699                                     gint            pos)
700 {
701 #ifdef G_ENABLE_DEBUG
702   GList *l;
703   const char *group_name;
704 #endif 
705
706   g_return_if_fail (GTK_IS_UI_MANAGER (self));
707   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
708   g_return_if_fail (g_list_find (self->private_data->action_groups, 
709                                  action_group) == NULL);
710
711 #ifdef G_ENABLE_DEBUG
712   group_name  = gtk_action_group_get_name (action_group);
713
714   for (l = self->private_data->action_groups; l; l = l->next) 
715     {
716       GtkActionGroup *group = l->data;
717
718       if (strcmp (gtk_action_group_get_name (group), group_name) == 0)
719         {
720           g_warning ("Inserting action group '%s' into UI manager which "
721                      "already has a group with this name\n", group_name);
722           break;
723         }
724     }
725 #endif /* G_ENABLE_DEBUG */
726
727   g_object_ref (action_group);
728   self->private_data->action_groups = 
729     g_list_insert (self->private_data->action_groups, action_group, pos);
730   g_object_connect (action_group,
731                     "object-signal::connect-proxy", G_CALLBACK (cb_proxy_connect_proxy), self,
732                     "object-signal::disconnect-proxy", G_CALLBACK (cb_proxy_disconnect_proxy), self,
733                     "object-signal::pre-activate", G_CALLBACK (cb_proxy_pre_activate), self,
734                     "object-signal::post-activate", G_CALLBACK (cb_proxy_post_activate), self,
735                     NULL);
736
737   /* dirty all nodes, as action bindings may change */
738   dirty_all_nodes (self);
739
740   g_signal_emit (self, ui_manager_signals[ACTIONS_CHANGED], 0);
741 }
742
743 /**
744  * gtk_ui_manager_remove_action_group:
745  * @self: a #GtkUIManager object
746  * @action_group: the action group to be removed
747  * 
748  * Removes an action group from the list of action groups associated 
749  * with @self.
750  *
751  * Since: 2.4
752  **/
753 void
754 gtk_ui_manager_remove_action_group (GtkUIManager   *self,
755                                     GtkActionGroup *action_group)
756 {
757   g_return_if_fail (GTK_IS_UI_MANAGER (self));
758   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
759   g_return_if_fail (g_list_find (self->private_data->action_groups, 
760                                  action_group) != NULL);
761
762   self->private_data->action_groups =
763     g_list_remove (self->private_data->action_groups, action_group);
764
765   g_object_disconnect (action_group,
766                        "any-signal::connect-proxy", G_CALLBACK (cb_proxy_connect_proxy), self,
767                        "any-signal::disconnect-proxy", G_CALLBACK (cb_proxy_disconnect_proxy), self,
768                        "any-signal::pre-activate", G_CALLBACK (cb_proxy_pre_activate), self,
769                        "any-signal::post-activate", G_CALLBACK (cb_proxy_post_activate), self, 
770                        NULL);
771   g_object_unref (action_group);
772
773   /* dirty all nodes, as action bindings may change */
774   dirty_all_nodes (self);
775
776   g_signal_emit (self, ui_manager_signals[ACTIONS_CHANGED], 0);
777 }
778
779 /**
780  * gtk_ui_manager_get_action_groups:
781  * @self: a #GtkUIManager object
782  * 
783  * Returns the list of action groups associated with @self.
784  *
785  * Return value: a #GList of action groups. The list is owned by GTK+ 
786  *   and should not be modified.
787  *
788  * Since: 2.4
789  **/
790 GList *
791 gtk_ui_manager_get_action_groups (GtkUIManager *self)
792 {
793   g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
794
795   return self->private_data->action_groups;
796 }
797
798 /**
799  * gtk_ui_manager_get_accel_group:
800  * @self: a #GtkUIManager object
801  * 
802  * Returns the #GtkAccelGroup associated with @self.
803  *
804  * Return value: the #GtkAccelGroup.
805  *
806  * Since: 2.4
807  **/
808 GtkAccelGroup *
809 gtk_ui_manager_get_accel_group (GtkUIManager *self)
810 {
811   g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
812
813   return self->private_data->accel_group;
814 }
815
816 /**
817  * gtk_ui_manager_get_widget:
818  * @self: a #GtkUIManager
819  * @path: a path
820  * 
821  * Looks up a widget by following a path. 
822  * The path consists of the names specified in the XML description of the UI. 
823  * separated by '/'. Elements which don't have a name or action attribute in 
824  * the XML (e.g. &lt;popup&gt;) can be addressed by their XML element name 
825  * (e.g. "popup"). The root element ("/ui") can be omitted in the path.
826  *
827  * Note that the widget found by following a path that ends in a &lt;menu&gt;
828  * element is the menuitem to which the menu is attached, not the menu itself.
829  *
830  * Also note that the widgets constructed by a ui manager are not tied to 
831  * the lifecycle of the ui manager. If you add the widgets returned by this 
832  * function to some container or explicitly ref them, they will survive the
833  * destruction of the ui manager.
834  * 
835  * Return value: the widget found by following the path, or %NULL if no widget
836  *   was found.
837  *
838  * Since: 2.4
839  **/
840 GtkWidget *
841 gtk_ui_manager_get_widget (GtkUIManager *self,
842                            const gchar  *path)
843 {
844   g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
845   g_return_val_if_fail (path != NULL, NULL);
846
847   return GTK_UI_MANAGER_GET_CLASS (self)->get_widget (self, path);
848 }
849
850 typedef struct {
851   GtkUIManagerItemType types;
852   GSList *list;
853 } ToplevelData;
854
855 static void
856 collect_toplevels (GNode   *node, 
857                    gpointer user_data)
858 {
859   ToplevelData *data = user_data;
860
861   if (NODE_INFO (node)->proxy)
862     {
863       switch (NODE_INFO (node)->type) 
864         {
865         case NODE_TYPE_MENUBAR:
866           if (data->types & GTK_UI_MANAGER_MENUBAR)
867         data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy);
868           break;
869         case NODE_TYPE_TOOLBAR:
870       if (data->types & GTK_UI_MANAGER_TOOLBAR)
871         data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy);
872       break;
873         case NODE_TYPE_POPUP:
874           if (data->types & GTK_UI_MANAGER_POPUP)
875             data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy);
876           break;
877         default: ;
878         }
879     }
880 }
881
882 /**
883  * gtk_ui_manager_get_toplevels:
884  * @self: a #GtkUIManager
885  * @types: specifies the types of toplevel widgets to include. Allowed
886  *   types are #GTK_UI_MANAGER_MENUBAR, #GTK_UI_MANAGER_TOOLBAR and
887  *   #GTK_UI_MANAGER_POPUP.
888  * 
889  * Obtains a list of all toplevel widgets of the requested types.
890  * 
891  * Return value: a newly-allocated #GSList of all toplevel widgets of the
892  * requested types.  Free the returned list with g_slist_free().
893  *
894  * Since: 2.4
895  **/
896 GSList *
897 gtk_ui_manager_get_toplevels (GtkUIManager         *self,
898                               GtkUIManagerItemType  types)
899 {
900   ToplevelData data;
901
902   g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
903   g_return_val_if_fail ((~(GTK_UI_MANAGER_MENUBAR | 
904                            GTK_UI_MANAGER_TOOLBAR |
905                            GTK_UI_MANAGER_POPUP) & types) == 0, NULL);
906   
907       
908   data.types = types;
909   data.list = NULL;
910
911   g_node_children_foreach (self->private_data->root_node, 
912                            G_TRAVERSE_ALL, 
913                            collect_toplevels, &data);
914
915   return data.list;
916 }
917
918
919 /**
920  * gtk_ui_manager_get_action:
921  * @self: a #GtkUIManager
922  * @path: a path
923  * 
924  * Looks up an action by following a path. See gtk_ui_manager_get_widget()
925  * for more information about paths.
926  * 
927  * Return value: the action whose proxy widget is found by following the path, 
928  *     or %NULL if no widget was found.
929  *
930  * Since: 2.4
931  **/
932 GtkAction *
933 gtk_ui_manager_get_action (GtkUIManager *self,
934                            const gchar  *path)
935 {
936   g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
937   g_return_val_if_fail (path != NULL, NULL);
938
939   return GTK_UI_MANAGER_GET_CLASS (self)->get_action (self, path);
940 }
941
942 static GNode *
943 get_child_node (GtkUIManager *self, 
944                 GNode        *parent,
945                 GNode        *sibling,
946                 const gchar  *childname, 
947                 gint          childname_length,
948                 NodeType      node_type,
949                 gboolean      create, 
950                 gboolean      top)
951 {
952   GNode *child = NULL;
953
954   if (parent)
955     {
956       if (childname)
957         {
958           for (child = parent->children; child != NULL; child = child->next)
959             {
960               if (NODE_INFO (child)->name &&
961                   strlen (NODE_INFO (child)->name) == childname_length &&
962                   !strncmp (NODE_INFO (child)->name, childname, childname_length))
963                 {
964                   /* if undecided about node type, set it */
965                   if (NODE_INFO (child)->type == NODE_TYPE_UNDECIDED)
966                     NODE_INFO (child)->type = node_type;
967                   
968                   /* warn about type mismatch */
969                   if (NODE_INFO (child)->type != NODE_TYPE_UNDECIDED &&
970                       node_type != NODE_TYPE_UNDECIDED &&
971                       NODE_INFO (child)->type != node_type)
972                     g_warning ("node type doesn't match %d (%s is type %d)",
973                                node_type, 
974                                NODE_INFO (child)->name,
975                                NODE_INFO (child)->type);
976                   
977                   return child;
978                 }
979             }
980         }
981       if (!child && create)
982         {
983           Node *mnode;
984           
985           mnode = g_slice_new0 (Node);
986           mnode->type = node_type;
987           mnode->name = g_strndup (childname, childname_length);
988
989           if (sibling)
990             {
991               if (top)
992                 child = g_node_insert_before (parent, sibling, 
993                                               g_node_new (mnode));
994               else
995                 child = g_node_insert_after (parent, sibling, 
996                                              g_node_new (mnode));
997             }
998           else
999             {
1000               if (top)
1001                 child = g_node_prepend_data (parent, mnode);
1002               else
1003                 child = g_node_append_data (parent, mnode);
1004             }
1005
1006           mark_node_dirty (child);
1007         }
1008     }
1009   else
1010     {
1011       /* handle root node */
1012       if (self->private_data->root_node)
1013         {
1014           child = self->private_data->root_node;
1015           if (strncmp (NODE_INFO (child)->name, childname, childname_length) != 0)
1016             g_warning ("root node name '%s' doesn't match '%s'",
1017                        childname, NODE_INFO (child)->name);
1018           if (NODE_INFO (child)->type != NODE_TYPE_ROOT)
1019             g_warning ("base element must be of type ROOT");
1020         }
1021       else if (create)
1022         {
1023           Node *mnode;
1024
1025           mnode = g_slice_new0 (Node);
1026           mnode->type = node_type;
1027           mnode->name = g_strndup (childname, childname_length);
1028           mnode->dirty = TRUE;
1029           
1030           child = self->private_data->root_node = g_node_new (mnode);
1031         }
1032     }
1033
1034   return child;
1035 }
1036
1037 static GNode *
1038 get_node (GtkUIManager *self, 
1039           const gchar  *path,
1040           NodeType      node_type, 
1041           gboolean      create)
1042 {
1043   const gchar *pos, *end;
1044   GNode *parent, *node;
1045   
1046   if (strncmp ("/ui", path, 3) == 0)
1047     path += 3;
1048   
1049   end = path + strlen (path);
1050   pos = path;
1051   parent = node = NULL;
1052   while (pos < end)
1053     {
1054       const gchar *slash;
1055       gsize length;
1056
1057       slash = strchr (pos, '/');
1058       if (slash)
1059         length = slash - pos;
1060       else
1061         length = strlen (pos);
1062
1063       node = get_child_node (self, parent, NULL, pos, length, NODE_TYPE_UNDECIDED,
1064                              create, FALSE);
1065       if (!node)
1066         return NULL;
1067
1068       pos += length + 1; /* move past the node name and the slash too */
1069       parent = node;
1070     }
1071
1072   if (node != NULL && NODE_INFO (node)->type == NODE_TYPE_UNDECIDED)
1073     NODE_INFO (node)->type = node_type;
1074
1075   return node;
1076 }
1077
1078 static void
1079 node_ui_reference_free (gpointer data)
1080 {
1081   g_slice_free (NodeUIReference, data);
1082 }
1083
1084 static gboolean
1085 free_node (GNode *node)
1086 {
1087   Node *info = NODE_INFO (node);
1088   
1089   g_list_foreach (info->uifiles, (GFunc) node_ui_reference_free, NULL);
1090   g_list_free (info->uifiles);
1091
1092   if (info->action)
1093     g_object_unref (info->action);
1094   if (info->proxy)
1095     g_object_unref (info->proxy);
1096   if (info->extra)
1097     g_object_unref (info->extra);
1098   g_free (info->name);
1099   g_slice_free (Node, info);
1100
1101   return FALSE;
1102 }
1103
1104 /**
1105  * gtk_ui_manager_new_merge_id:
1106  * @self: a #GtkUIManager
1107  * 
1108  * Returns an unused merge id, suitable for use with 
1109  * gtk_ui_manager_add_ui().
1110  * 
1111  * Return value: an unused merge id.
1112  *
1113  * Since: 2.4
1114  **/
1115 guint
1116 gtk_ui_manager_new_merge_id (GtkUIManager *self)
1117 {
1118   self->private_data->last_merge_id++;
1119
1120   return self->private_data->last_merge_id;
1121 }
1122
1123 static void
1124 node_prepend_ui_reference (GNode  *gnode,
1125                            guint   merge_id, 
1126                            GQuark  action_quark)
1127 {
1128   Node *node = NODE_INFO (gnode);
1129   NodeUIReference *reference = NULL;
1130
1131   if (node->uifiles && 
1132       ((NodeUIReference *)node->uifiles->data)->merge_id == merge_id)
1133     reference = node->uifiles->data;
1134   else
1135     {
1136       reference = g_slice_new (NodeUIReference);
1137       node->uifiles = g_list_prepend (node->uifiles, reference);
1138     }
1139
1140   reference->merge_id = merge_id;
1141   reference->action_quark = action_quark;
1142
1143   mark_node_dirty (gnode);
1144 }
1145
1146 static void
1147 node_remove_ui_reference (GNode  *gnode,
1148                           guint  merge_id)
1149 {
1150   Node *node = NODE_INFO (gnode);
1151   GList *p;
1152   
1153   for (p = node->uifiles; p != NULL; p = p->next)
1154     {
1155       NodeUIReference *reference = p->data;
1156       
1157       if (reference->merge_id == merge_id)
1158         {
1159           if (p == node->uifiles)
1160             mark_node_dirty (gnode);
1161           node->uifiles = g_list_delete_link (node->uifiles, p);
1162           g_slice_free (NodeUIReference, reference);
1163
1164           break;
1165         }
1166     }
1167 }
1168
1169 /* -------------------- The UI file parser -------------------- */
1170
1171 typedef enum
1172 {
1173   STATE_START,
1174   STATE_ROOT,
1175   STATE_MENU,
1176   STATE_TOOLBAR,
1177   STATE_MENUITEM,
1178   STATE_TOOLITEM,
1179   STATE_ACCELERATOR,
1180   STATE_END
1181 } ParseState;
1182
1183 typedef struct _ParseContext ParseContext;
1184 struct _ParseContext
1185 {
1186   ParseState state;
1187   ParseState prev_state;
1188
1189   GtkUIManager *self;
1190
1191   GNode *current;
1192
1193   guint merge_id;
1194 };
1195
1196 static void
1197 start_element_handler (GMarkupParseContext *context,
1198                        const gchar         *element_name,
1199                        const gchar        **attribute_names,
1200                        const gchar        **attribute_values,
1201                        gpointer             user_data,
1202                        GError             **error)
1203 {
1204   ParseContext *ctx = user_data;
1205   GtkUIManager *self = ctx->self;
1206
1207   gint i;
1208   const gchar *node_name;
1209   const gchar *action;
1210   GQuark action_quark;
1211   gboolean top;
1212   gboolean expand = FALSE;
1213   gboolean accelerators = FALSE;
1214
1215   gboolean raise_error = TRUE;
1216
1217   node_name = NULL;
1218   action = NULL;
1219   action_quark = 0;
1220   top = FALSE;
1221
1222   for (i = 0; attribute_names[i] != NULL; i++)
1223     {
1224       if (!strcmp (attribute_names[i], "name"))
1225         {
1226           node_name = attribute_values[i];
1227         }
1228       else if (!strcmp (attribute_names[i], "action"))
1229         {
1230           action = attribute_values[i];
1231           action_quark = g_quark_from_string (attribute_values[i]);
1232         }
1233       else if (!strcmp (attribute_names[i], "position"))
1234         {
1235           top = !strcmp (attribute_values[i], "top");
1236         }
1237       else if (!strcmp (attribute_names[i], "expand"))
1238         {
1239           expand = !strcmp (attribute_values[i], "true");
1240         }
1241       else if (!strcmp (attribute_names[i], "accelerators"))
1242         {
1243           accelerators = !strcmp (attribute_values[i], "true");
1244         }
1245       /*  else silently skip unknown attributes to be compatible with
1246        *  future additional attributes.
1247        */
1248     }
1249
1250   /* Work out a name for this node.  Either the name attribute, or
1251    * the action, or the element name */
1252   if (node_name == NULL) 
1253     {
1254       if (action != NULL)
1255         node_name = action;
1256       else 
1257         node_name = element_name;
1258     }
1259
1260   switch (element_name[0])
1261     {
1262     case 'a':
1263       if (ctx->state == STATE_ROOT && !strcmp (element_name, "accelerator"))
1264         {
1265           ctx->state = STATE_ACCELERATOR;
1266           ctx->current = get_child_node (self, ctx->current, NULL,
1267                                          node_name, strlen (node_name),
1268                                          NODE_TYPE_ACCELERATOR,
1269                                          TRUE, FALSE);
1270           if (NODE_INFO (ctx->current)->action_name == 0)
1271             NODE_INFO (ctx->current)->action_name = action_quark;
1272
1273           node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
1274
1275           raise_error = FALSE;
1276         }
1277       break;
1278     case 'u':
1279       if (ctx->state == STATE_START && !strcmp (element_name, "ui"))
1280         {
1281           ctx->state = STATE_ROOT;
1282           ctx->current = self->private_data->root_node;
1283           raise_error = FALSE;
1284
1285           node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
1286         }
1287       break;
1288     case 'm':
1289       if (ctx->state == STATE_ROOT && !strcmp (element_name, "menubar"))
1290         {
1291           ctx->state = STATE_MENU;
1292           ctx->current = get_child_node (self, ctx->current, NULL,
1293                                          node_name, strlen (node_name),
1294                                          NODE_TYPE_MENUBAR,
1295                                          TRUE, FALSE);
1296           if (NODE_INFO (ctx->current)->action_name == 0)
1297             NODE_INFO (ctx->current)->action_name = action_quark;
1298
1299           node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
1300           mark_node_dirty (ctx->current);
1301
1302           raise_error = FALSE;
1303         }
1304       else if (ctx->state == STATE_MENU && !strcmp (element_name, "menu"))
1305         {
1306           ctx->current = get_child_node (self, ctx->current, NULL,
1307                                          node_name, strlen (node_name),
1308                                          NODE_TYPE_MENU,
1309                                          TRUE, top);
1310           if (NODE_INFO (ctx->current)->action_name == 0)
1311             NODE_INFO (ctx->current)->action_name = action_quark;
1312
1313           node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
1314           
1315           raise_error = FALSE;
1316         }
1317       else if (ctx->state == STATE_TOOLITEM && !strcmp (element_name, "menu"))
1318         {
1319           ctx->state = STATE_MENU;
1320           
1321           ctx->current = get_child_node (self, g_node_last_child (ctx->current), NULL,
1322                                          node_name, strlen (node_name),
1323                                          NODE_TYPE_MENU,
1324                                          TRUE, top);
1325           if (NODE_INFO (ctx->current)->action_name == 0)
1326             NODE_INFO (ctx->current)->action_name = action_quark;
1327
1328           node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
1329           
1330           raise_error = FALSE;
1331         }
1332       else if (ctx->state == STATE_MENU && !strcmp (element_name, "menuitem"))
1333         {
1334           GNode *node;
1335
1336           ctx->state = STATE_MENUITEM;
1337           node = get_child_node (self, ctx->current, NULL,
1338                                  node_name, strlen (node_name),
1339                                  NODE_TYPE_MENUITEM,
1340                                  TRUE, top);
1341           if (NODE_INFO (node)->action_name == 0)
1342             NODE_INFO (node)->action_name = action_quark;
1343           
1344           node_prepend_ui_reference (node, ctx->merge_id, action_quark);
1345           
1346           raise_error = FALSE;
1347         }
1348       break;
1349     case 'p':
1350       if (ctx->state == STATE_ROOT && !strcmp (element_name, "popup"))
1351         {
1352           ctx->state = STATE_MENU;
1353           ctx->current = get_child_node (self, ctx->current, NULL,
1354                                          node_name, strlen (node_name),
1355                                          NODE_TYPE_POPUP,
1356                                          TRUE, FALSE);
1357
1358           NODE_INFO (ctx->current)->popup_accels = accelerators;
1359
1360           if (NODE_INFO (ctx->current)->action_name == 0)
1361             NODE_INFO (ctx->current)->action_name = action_quark;
1362           
1363           node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
1364           
1365           raise_error = FALSE;
1366         }
1367       else if ((ctx->state == STATE_MENU || ctx->state == STATE_TOOLBAR) &&
1368                !strcmp (element_name, "placeholder"))
1369         {
1370           if (ctx->state == STATE_TOOLBAR)
1371             ctx->current = get_child_node (self, ctx->current, NULL,
1372                                            node_name, strlen (node_name),
1373                                            NODE_TYPE_TOOLBAR_PLACEHOLDER,
1374                                            TRUE, top);
1375           else
1376             ctx->current = get_child_node (self, ctx->current, NULL,
1377                                            node_name, strlen (node_name),
1378                                            NODE_TYPE_MENU_PLACEHOLDER,
1379                                            TRUE, top);
1380           
1381           node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
1382           
1383           raise_error = FALSE;
1384         }
1385       break;
1386     case 's':
1387       if ((ctx->state == STATE_MENU || ctx->state == STATE_TOOLBAR) &&
1388           !strcmp (element_name, "separator"))
1389         {
1390           GNode *node;
1391           gint length;
1392
1393           if (ctx->state == STATE_TOOLBAR)
1394             ctx->state = STATE_TOOLITEM;
1395           else
1396             ctx->state = STATE_MENUITEM;
1397           if (!strcmp (node_name, "separator"))
1398             {
1399               node_name = NULL;
1400               length = 0;
1401             }
1402           else
1403             length = strlen (node_name);
1404           node = get_child_node (self, ctx->current, NULL,
1405                                  node_name, length,
1406                                  NODE_TYPE_SEPARATOR,
1407                                  TRUE, top);
1408
1409           NODE_INFO (node)->expand = expand;
1410
1411           if (NODE_INFO (node)->action_name == 0)
1412             NODE_INFO (node)->action_name = action_quark;
1413
1414           node_prepend_ui_reference (node, ctx->merge_id, action_quark);
1415           
1416           raise_error = FALSE;
1417         }
1418       break;
1419     case 't':
1420       if (ctx->state == STATE_ROOT && !strcmp (element_name, "toolbar"))
1421         {
1422           ctx->state = STATE_TOOLBAR;
1423           ctx->current = get_child_node (self, ctx->current, NULL,
1424                                          node_name, strlen (node_name),
1425                                          NODE_TYPE_TOOLBAR,
1426                                          TRUE, FALSE);
1427           if (NODE_INFO (ctx->current)->action_name == 0)
1428             NODE_INFO (ctx->current)->action_name = action_quark;
1429           
1430           node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
1431           
1432           raise_error = FALSE;
1433         }
1434       else if (ctx->state == STATE_TOOLBAR && !strcmp (element_name, "toolitem"))
1435         {
1436           GNode *node;
1437
1438           ctx->state = STATE_TOOLITEM;
1439           node = get_child_node (self, ctx->current, NULL,
1440                                 node_name, strlen (node_name),
1441                                  NODE_TYPE_TOOLITEM,
1442                                  TRUE, top);
1443           if (NODE_INFO (node)->action_name == 0)
1444             NODE_INFO (node)->action_name = action_quark;
1445           
1446           node_prepend_ui_reference (node, ctx->merge_id, action_quark);
1447
1448           raise_error = FALSE;
1449         }
1450       break;
1451     default:
1452       break;
1453     }
1454   if (raise_error)
1455     {
1456       gint line_number, char_number;
1457  
1458       g_markup_parse_context_get_position (context,
1459                                            &line_number, &char_number);
1460       g_set_error (error,
1461                    G_MARKUP_ERROR,
1462                    G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1463                    _("Unexpected start tag '%s' on line %d char %d"),
1464                    element_name,
1465                    line_number, char_number);
1466     }
1467 }
1468
1469 static void
1470 end_element_handler (GMarkupParseContext *context,
1471                      const gchar         *element_name,
1472                      gpointer             user_data,
1473                      GError             **error)
1474 {
1475   ParseContext *ctx = user_data;
1476
1477   switch (ctx->state)
1478     {
1479     case STATE_START:
1480     case STATE_END:
1481       /* no need to GError here, GMarkup already catches this */
1482       break;
1483     case STATE_ROOT:
1484       ctx->current = NULL;
1485       ctx->state = STATE_END;
1486       break;
1487     case STATE_MENU:
1488     case STATE_TOOLBAR:
1489     case STATE_ACCELERATOR:
1490       ctx->current = ctx->current->parent;
1491       if (NODE_INFO (ctx->current)->type == NODE_TYPE_ROOT) 
1492         ctx->state = STATE_ROOT;
1493       else if (NODE_INFO (ctx->current)->type == NODE_TYPE_TOOLITEM)
1494         {
1495           ctx->current = ctx->current->parent;
1496           ctx->state = STATE_TOOLITEM;
1497         }
1498       /* else, stay in same state */
1499       break;
1500     case STATE_MENUITEM:
1501       ctx->state = STATE_MENU;
1502       break;
1503     case STATE_TOOLITEM:
1504       ctx->state = STATE_TOOLBAR;
1505       break;
1506     }
1507 }
1508
1509 static void
1510 cleanup (GMarkupParseContext *context,
1511          GError              *error,
1512          gpointer             user_data)
1513 {
1514   ParseContext *ctx = user_data;
1515
1516   ctx->current = NULL;
1517   /* should also walk through the tree and get rid of nodes related to
1518    * this UI file's tag */
1519
1520   gtk_ui_manager_remove_ui (ctx->self, ctx->merge_id);
1521 }
1522
1523 static gboolean
1524 xml_isspace (char c)
1525 {
1526   return c == ' ' || c == '\t' || c == '\n' || c == '\r';
1527 }
1528
1529 static void 
1530 text_handler (GMarkupParseContext *context,
1531               const gchar         *text,
1532               gsize                text_len,  
1533               gpointer             user_data,
1534               GError             **error)
1535 {
1536   const gchar *p;
1537   const gchar *end;
1538
1539   p = text;
1540   end = text + text_len;
1541   while (p != end && xml_isspace (*p))
1542     ++p;
1543   
1544   if (p != end)
1545     {
1546       gint line_number, char_number;
1547       
1548       g_markup_parse_context_get_position (context,
1549                                            &line_number, &char_number);
1550       g_set_error (error,
1551                    G_MARKUP_ERROR,
1552                    G_MARKUP_ERROR_INVALID_CONTENT,
1553                    _("Unexpected character data on line %d char %d"),
1554                    line_number, char_number);
1555     }
1556 }
1557
1558
1559 static const GMarkupParser ui_parser = {
1560   start_element_handler,
1561   end_element_handler,
1562   text_handler,
1563   NULL,
1564   cleanup
1565 };
1566
1567 static guint
1568 add_ui_from_string (GtkUIManager *self,
1569                     const gchar  *buffer, 
1570                     gssize        length,
1571                     gboolean      needs_root,
1572                     GError      **error)
1573 {
1574   ParseContext ctx = { 0 };
1575   GMarkupParseContext *context;
1576
1577   ctx.state = STATE_START;
1578   ctx.self = self;
1579   ctx.current = NULL;
1580   ctx.merge_id = gtk_ui_manager_new_merge_id (self);
1581
1582   context = g_markup_parse_context_new (&ui_parser, 0, &ctx, NULL);
1583
1584   if (needs_root)
1585     if (!g_markup_parse_context_parse (context, "<ui>", -1, error))
1586       goto out;
1587
1588   if (!g_markup_parse_context_parse (context, buffer, length, error))
1589     goto out;
1590
1591   if (needs_root)
1592     if (!g_markup_parse_context_parse (context, "</ui>", -1, error))
1593       goto out;
1594
1595   if (!g_markup_parse_context_end_parse (context, error))
1596     goto out;
1597
1598   g_markup_parse_context_free (context);
1599
1600   queue_update (self);
1601
1602   g_object_notify (G_OBJECT (self), "ui");
1603
1604   return ctx.merge_id;
1605
1606  out:
1607
1608   g_markup_parse_context_free (context);
1609
1610   return 0;
1611 }
1612
1613 /**
1614  * gtk_ui_manager_add_ui_from_string:
1615  * @self: a #GtkUIManager object
1616  * @buffer: the string to parse
1617  * @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
1618  * @error: return location for an error
1619  * 
1620  * Parses a string containing a <link linkend="XML-UI">UI definition</link> and 
1621  * merges it with the current contents of @self. An enclosing &lt;ui&gt; 
1622  * element is added if it is missing.
1623  * 
1624  * Return value: The merge id for the merged UI. The merge id can be used
1625  *   to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred,
1626  *   the return value is 0.
1627  *
1628  * Since: 2.4
1629  **/
1630 guint
1631 gtk_ui_manager_add_ui_from_string (GtkUIManager *self,
1632                                    const gchar  *buffer, 
1633                                    gssize        length,
1634                                    GError      **error)
1635 {
1636   gboolean needs_root = TRUE;
1637   const gchar *p;
1638   const gchar *end;
1639
1640   g_return_val_if_fail (GTK_IS_UI_MANAGER (self), 0);
1641   g_return_val_if_fail (buffer != NULL, 0);
1642
1643   if (length < 0)
1644     length = strlen (buffer);
1645
1646   p = buffer;
1647   end = buffer + length;
1648   while (p != end && xml_isspace (*p))
1649     ++p;
1650
1651   if (end - p >= 4 && strncmp (p, "<ui>", 4) == 0)
1652     needs_root = FALSE;
1653   
1654   return add_ui_from_string (self, buffer, length, needs_root, error);
1655 }
1656
1657 /**
1658  * gtk_ui_manager_add_ui_from_file:
1659  * @self: a #GtkUIManager object
1660  * @filename: the name of the file to parse 
1661  * @error: return location for an error
1662  * 
1663  * Parses a file containing a <link linkend="XML-UI">UI definition</link> and 
1664  * merges it with the current contents of @self. 
1665  * 
1666  * Return value: The merge id for the merged UI. The merge id can be used
1667  *   to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred,
1668  *   the return value is 0.
1669  *
1670  * Since: 2.4
1671  **/
1672 guint
1673 gtk_ui_manager_add_ui_from_file (GtkUIManager *self,
1674                                  const gchar  *filename,
1675                                  GError      **error)
1676 {
1677   gchar *buffer;
1678   gsize length;
1679   guint res;
1680
1681   g_return_val_if_fail (GTK_IS_UI_MANAGER (self), 0);
1682
1683   if (!g_file_get_contents (filename, &buffer, &length, error))
1684     return 0;
1685
1686   res = add_ui_from_string (self, buffer, length, FALSE, error);
1687   g_free (buffer);
1688
1689   return res;
1690 }
1691
1692 /**
1693  * gtk_ui_manager_add_ui:
1694  * @self: a #GtkUIManager
1695  * @merge_id: the merge id for the merged UI, see gtk_ui_manager_new_merge_id()
1696  * @path: a path
1697  * @name: the name for the added UI element
1698  * @action: the name of the action to be proxied, or %NULL to add a separator
1699  * @type: the type of UI element to add.
1700  * @top: if %TRUE, the UI element is added before its siblings, otherwise it 
1701  *   is added after its siblings.
1702  * 
1703  * Adds a UI element to the current contents of @self. 
1704  *
1705  * If @type is %GTK_UI_MANAGER_AUTO, GTK+ inserts a menuitem, toolitem or 
1706  * separator if such an element can be inserted at the place determined by 
1707  * @path. Otherwise @type must indicate an element that can be inserted at 
1708  * the place determined by @path.
1709  *
1710  * If @path points to a menuitem or toolitem, the new element will be inserted
1711  * before or after this item, depending on @top.
1712  * 
1713  * Since: 2.4
1714  **/
1715 void
1716 gtk_ui_manager_add_ui (GtkUIManager        *self,
1717                        guint                merge_id,
1718                        const gchar         *path,
1719                        const gchar         *name,
1720                        const gchar         *action,
1721                        GtkUIManagerItemType type,
1722                        gboolean             top)
1723 {
1724   GNode *node;
1725   GNode *sibling;
1726   GNode *child;
1727   NodeType node_type;
1728   GQuark action_quark = 0;
1729
1730   g_return_if_fail (GTK_IS_UI_MANAGER (self));  
1731   g_return_if_fail (merge_id > 0);
1732   g_return_if_fail (name != NULL || type == GTK_UI_MANAGER_SEPARATOR);
1733
1734   node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
1735   sibling = NULL;
1736
1737   if (node == NULL)
1738     return;
1739
1740   node_type = NODE_TYPE_UNDECIDED;
1741
1742  reswitch:
1743   switch (NODE_INFO (node)->type) 
1744     {
1745     case NODE_TYPE_SEPARATOR:
1746     case NODE_TYPE_MENUITEM:
1747     case NODE_TYPE_TOOLITEM:
1748       sibling = node;
1749       node = node->parent;
1750       goto reswitch;
1751     case NODE_TYPE_MENUBAR:
1752     case NODE_TYPE_MENU:
1753     case NODE_TYPE_POPUP:
1754     case NODE_TYPE_MENU_PLACEHOLDER:
1755       switch (type) 
1756         {
1757         case GTK_UI_MANAGER_AUTO:
1758           if (action != NULL)
1759               node_type = NODE_TYPE_MENUITEM;
1760           else
1761               node_type = NODE_TYPE_SEPARATOR;
1762           break;
1763         case GTK_UI_MANAGER_MENU:
1764           node_type = NODE_TYPE_MENU;
1765           break;
1766         case GTK_UI_MANAGER_MENUITEM:
1767           node_type = NODE_TYPE_MENUITEM;
1768           break;
1769         case GTK_UI_MANAGER_SEPARATOR:
1770           node_type = NODE_TYPE_SEPARATOR;
1771           break;
1772         case GTK_UI_MANAGER_PLACEHOLDER:
1773           node_type = NODE_TYPE_MENU_PLACEHOLDER;
1774           break;
1775         default: ;
1776           /* do nothing */
1777         }
1778       break;
1779     case NODE_TYPE_TOOLBAR:
1780     case NODE_TYPE_TOOLBAR_PLACEHOLDER:
1781       switch (type) 
1782         {
1783         case GTK_UI_MANAGER_AUTO:
1784           if (action != NULL)
1785               node_type = NODE_TYPE_TOOLITEM;
1786           else
1787               node_type = NODE_TYPE_SEPARATOR;
1788           break;
1789         case GTK_UI_MANAGER_TOOLITEM:
1790           node_type = NODE_TYPE_TOOLITEM;
1791           break;
1792         case GTK_UI_MANAGER_SEPARATOR:
1793           node_type = NODE_TYPE_SEPARATOR;
1794           break;
1795         case GTK_UI_MANAGER_PLACEHOLDER:
1796           node_type = NODE_TYPE_TOOLBAR_PLACEHOLDER;
1797           break;
1798         default: ;
1799           /* do nothing */
1800         }
1801       break;
1802     case NODE_TYPE_ROOT:
1803       switch (type) 
1804         {
1805         case GTK_UI_MANAGER_MENUBAR:
1806           node_type = NODE_TYPE_MENUBAR;
1807           break;
1808         case GTK_UI_MANAGER_TOOLBAR:
1809           node_type = NODE_TYPE_TOOLBAR;
1810           break;
1811         case GTK_UI_MANAGER_POPUP:
1812         case GTK_UI_MANAGER_POPUP_WITH_ACCELS:
1813           node_type = NODE_TYPE_POPUP;
1814           break;
1815         case GTK_UI_MANAGER_ACCELERATOR:
1816           node_type = NODE_TYPE_ACCELERATOR;
1817           break;
1818         default: ;
1819           /* do nothing */
1820         }
1821       break;
1822     default: ;
1823       /* do nothing */
1824     }
1825
1826   if (node_type == NODE_TYPE_UNDECIDED)
1827     {
1828       g_warning ("item type %d not suitable for adding at '%s'", 
1829                  type, path);
1830       return;
1831     }
1832    
1833   child = get_child_node (self, node, sibling,
1834                           name, name ? strlen (name) : 0,
1835                           node_type, TRUE, top);
1836
1837   if (type == GTK_UI_MANAGER_POPUP_WITH_ACCELS)
1838     NODE_INFO (child)->popup_accels = TRUE;
1839
1840   if (action != NULL)
1841     action_quark = g_quark_from_string (action);
1842
1843   node_prepend_ui_reference (child, merge_id, action_quark);
1844
1845   if (NODE_INFO (child)->action_name == 0)
1846     NODE_INFO (child)->action_name = action_quark;
1847
1848   queue_update (self);
1849
1850   g_object_notify (G_OBJECT (self), "ui");      
1851 }
1852
1853 static gboolean
1854 remove_ui (GNode   *node, 
1855            gpointer user_data)
1856 {
1857   guint merge_id = GPOINTER_TO_UINT (user_data);
1858
1859   node_remove_ui_reference (node, merge_id);
1860
1861   return FALSE; /* continue */
1862 }
1863
1864 /**
1865  * gtk_ui_manager_remove_ui:
1866  * @self: a #GtkUIManager object
1867  * @merge_id: a merge id as returned by gtk_ui_manager_add_ui_from_string()
1868  * 
1869  * Unmerges the part of @self<!-- -->s content identified by @merge_id.
1870  *
1871  * Since: 2.4
1872  **/
1873 void
1874 gtk_ui_manager_remove_ui (GtkUIManager *self, 
1875                           guint         merge_id)
1876 {
1877   g_return_if_fail (GTK_IS_UI_MANAGER (self));
1878
1879   g_node_traverse (self->private_data->root_node, 
1880                    G_POST_ORDER, G_TRAVERSE_ALL, -1,
1881                    remove_ui, GUINT_TO_POINTER (merge_id));
1882
1883   queue_update (self);
1884
1885   g_object_notify (G_OBJECT (self), "ui");      
1886 }
1887
1888 /* -------------------- Updates -------------------- */
1889
1890
1891 static GtkAction *
1892 get_action_by_name (GtkUIManager *merge, 
1893                     const gchar  *action_name)
1894 {
1895   GList *tmp;
1896
1897   if (!action_name)
1898     return NULL;
1899   
1900   /* lookup name */
1901   for (tmp = merge->private_data->action_groups; tmp != NULL; tmp = tmp->next)
1902     {
1903       GtkActionGroup *action_group = tmp->data;
1904       GtkAction *action;
1905       
1906       action = gtk_action_group_get_action (action_group, action_name);
1907
1908       if (action)
1909         return action;
1910     }
1911
1912   return NULL;
1913 }
1914
1915 static gboolean
1916 find_menu_position (GNode      *node, 
1917                     GtkWidget **menushell_p, 
1918                     gint       *pos_p)
1919 {
1920   GtkWidget *menushell;
1921   gint pos = 0;
1922
1923   g_return_val_if_fail (node != NULL, FALSE);
1924   g_return_val_if_fail (NODE_INFO (node)->type == NODE_TYPE_MENU ||
1925                         NODE_INFO (node)->type == NODE_TYPE_POPUP ||
1926                         NODE_INFO (node)->type == NODE_TYPE_MENU_PLACEHOLDER ||
1927                         NODE_INFO (node)->type == NODE_TYPE_MENUITEM ||
1928                         NODE_INFO (node)->type == NODE_TYPE_SEPARATOR,
1929                         FALSE);
1930
1931   /* first sibling -- look at parent */
1932   if (node->prev == NULL)
1933     {
1934       GNode *parent;
1935       GList *siblings;
1936
1937       parent = node->parent;
1938       switch (NODE_INFO (parent)->type)
1939         {
1940         case NODE_TYPE_MENUBAR:
1941         case NODE_TYPE_POPUP:
1942           menushell = NODE_INFO (parent)->proxy;
1943           pos = 0;
1944           break;
1945         case NODE_TYPE_MENU:
1946           menushell = NODE_INFO (parent)->proxy;
1947           if (GTK_IS_MENU_ITEM (menushell))
1948             menushell = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menushell));
1949           siblings = gtk_container_get_children (GTK_CONTAINER (menushell));
1950           if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
1951             pos = 1;
1952           else
1953             pos = 0;
1954           g_list_free (siblings);
1955           break;
1956         case NODE_TYPE_MENU_PLACEHOLDER:
1957           menushell = gtk_widget_get_parent (NODE_INFO (parent)->proxy);
1958           g_return_val_if_fail (GTK_IS_MENU_SHELL (menushell), FALSE);
1959           pos = g_list_index (GTK_MENU_SHELL (menushell)->children,
1960                               NODE_INFO (parent)->proxy) + 1;
1961           break;
1962         default:
1963           g_warning ("%s: bad parent node type %d", G_STRLOC,
1964                      NODE_INFO (parent)->type);
1965           return FALSE;
1966         }
1967     }
1968   else
1969     {
1970       GtkWidget *prev_child;
1971       GNode *sibling;
1972
1973       sibling = node->prev;
1974       if (NODE_INFO (sibling)->type == NODE_TYPE_MENU_PLACEHOLDER)
1975         prev_child = NODE_INFO (sibling)->extra; /* second Separator */
1976       else
1977         prev_child = NODE_INFO (sibling)->proxy;
1978
1979       if (!GTK_IS_WIDGET (prev_child))
1980         return FALSE;
1981
1982       menushell = gtk_widget_get_parent (prev_child);
1983       if (!GTK_IS_MENU_SHELL (menushell))
1984         return FALSE;
1985
1986       pos = g_list_index (GTK_MENU_SHELL (menushell)->children, prev_child) + 1;
1987     }
1988
1989   if (menushell_p)
1990     *menushell_p = menushell;
1991   if (pos_p)
1992     *pos_p = pos;
1993
1994   return TRUE;
1995 }
1996
1997 static gboolean
1998 find_toolbar_position (GNode      *node, 
1999                        GtkWidget **toolbar_p, 
2000                        gint       *pos_p)
2001 {
2002   GtkWidget *toolbar;
2003   gint pos;
2004
2005   g_return_val_if_fail (node != NULL, FALSE);
2006   g_return_val_if_fail (NODE_INFO (node)->type == NODE_TYPE_TOOLBAR ||
2007                         NODE_INFO (node)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER ||
2008                         NODE_INFO (node)->type == NODE_TYPE_TOOLITEM ||
2009                         NODE_INFO (node)->type == NODE_TYPE_SEPARATOR,
2010                         FALSE);
2011   
2012   /* first sibling -- look at parent */
2013   if (node->prev == NULL)
2014     {
2015       GNode *parent;
2016
2017       parent = node->parent;
2018       switch (NODE_INFO (parent)->type)
2019         {
2020         case NODE_TYPE_TOOLBAR:
2021           toolbar = NODE_INFO (parent)->proxy;
2022           pos = 0;
2023           break;
2024         case NODE_TYPE_TOOLBAR_PLACEHOLDER:
2025           toolbar = gtk_widget_get_parent (NODE_INFO (parent)->proxy);
2026           g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), FALSE);
2027           pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar),
2028                                             GTK_TOOL_ITEM (NODE_INFO (parent)->proxy)) + 1;
2029           break;
2030         default:
2031           g_warning ("%s: bad parent node type %d", G_STRLOC,
2032                      NODE_INFO (parent)->type);
2033           return FALSE;
2034         }
2035     }
2036   else
2037     {
2038       GtkWidget *prev_child;
2039       GNode *sibling;
2040
2041       sibling = node->prev;
2042       if (NODE_INFO (sibling)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER)
2043         prev_child = NODE_INFO (sibling)->extra; /* second Separator */
2044       else
2045         prev_child = NODE_INFO (sibling)->proxy;
2046
2047       if (!GTK_IS_WIDGET (prev_child))
2048         return FALSE;
2049
2050       toolbar = gtk_widget_get_parent (prev_child);
2051       if (!GTK_IS_TOOLBAR (toolbar))
2052         return FALSE;
2053
2054       pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar),
2055                                         GTK_TOOL_ITEM (prev_child)) + 1;
2056     }
2057   
2058   if (toolbar_p)
2059     *toolbar_p = toolbar;
2060   if (pos_p)
2061     *pos_p = pos;
2062
2063   return TRUE;
2064 }
2065
2066 /**
2067  * _gtk_menu_is_empty:
2068  * @menu: a #GtkMenu or %NULL
2069  * 
2070  * Determines whether @menu is empty. A menu is considered empty if it
2071  * the only visible children are tearoff menu items or "filler" menu 
2072  * items which were inserted to mark the menu as empty.
2073  * 
2074  * This function is used by #GtkAction.
2075  *
2076  * Return value: whether @menu is empty.
2077  **/
2078 gboolean
2079 _gtk_menu_is_empty (GtkWidget *menu)
2080 {
2081   GList *children, *cur;
2082   gboolean result = TRUE;
2083
2084   g_return_val_if_fail (menu == NULL || GTK_IS_MENU (menu), TRUE);
2085
2086   if (!menu)
2087     return FALSE;
2088
2089   children = gtk_container_get_children (GTK_CONTAINER (menu));
2090
2091   cur = children;
2092   while (cur) 
2093     {
2094       if (GTK_WIDGET_VISIBLE (cur->data))
2095         {
2096           if (!GTK_IS_TEAROFF_MENU_ITEM (cur->data) &&
2097               !g_object_get_data (cur->data, "gtk-empty-menu-item"))
2098             {
2099               result = FALSE;
2100               break;
2101             }
2102         }
2103       cur = cur->next;
2104     }
2105   g_list_free (children);
2106
2107   return result;
2108 }
2109
2110 enum {
2111   SEPARATOR_MODE_SMART,
2112   SEPARATOR_MODE_VISIBLE,
2113   SEPARATOR_MODE_HIDDEN
2114 };
2115
2116 static void
2117 update_smart_separators (GtkWidget *proxy)
2118 {
2119   GtkWidget *parent = NULL;
2120   
2121   if (GTK_IS_MENU (proxy) || GTK_IS_TOOLBAR (proxy))
2122     parent = proxy;
2123   else if (GTK_IS_MENU_ITEM (proxy) || GTK_IS_TOOL_ITEM (proxy))
2124     parent = gtk_widget_get_parent (proxy);
2125
2126   if (parent) 
2127     {
2128       gboolean visible;
2129       gboolean empty;
2130       GList *children, *cur, *last;
2131       GtkWidget *filler;
2132
2133       children = gtk_container_get_children (GTK_CONTAINER (parent));
2134       
2135       visible = FALSE;
2136       last = NULL;
2137       empty = TRUE;
2138       filler = NULL;
2139
2140       cur = children;
2141       while (cur) 
2142         {
2143           if (g_object_get_data (cur->data, "gtk-empty-menu-item"))
2144             {
2145               filler = cur->data;
2146             }
2147           else if (GTK_IS_SEPARATOR_MENU_ITEM (cur->data) ||
2148                    GTK_IS_SEPARATOR_TOOL_ITEM (cur->data))
2149             {
2150               gint mode = 
2151                 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cur->data), 
2152                                                     "gtk-separator-mode"));
2153               switch (mode) 
2154                 {
2155                 case SEPARATOR_MODE_VISIBLE:
2156                   gtk_widget_show (GTK_WIDGET (cur->data));
2157                   last = NULL;
2158                   visible = FALSE;
2159                   break;
2160                 case SEPARATOR_MODE_HIDDEN:
2161                   gtk_widget_hide (GTK_WIDGET (cur->data));
2162                   break;
2163                 case SEPARATOR_MODE_SMART:
2164                   if (visible)
2165                     {
2166                       gtk_widget_show (GTK_WIDGET (cur->data));
2167                       last = cur;
2168                       visible = FALSE;
2169                     }
2170                   else 
2171                     gtk_widget_hide (GTK_WIDGET (cur->data));
2172                   break;
2173                 }
2174             }
2175           else if (GTK_WIDGET_VISIBLE (cur->data))
2176             {
2177               last = NULL;
2178               if (GTK_IS_TEAROFF_MENU_ITEM (cur->data) || cur->data == filler)
2179                 visible = FALSE;
2180               else 
2181                 {
2182                   visible = TRUE;
2183                   empty = FALSE;
2184                 }
2185             }
2186           
2187           cur = cur->next;
2188         }
2189
2190       if (last)
2191         gtk_widget_hide (GTK_WIDGET (last->data));
2192
2193       if (GTK_IS_MENU (parent)) 
2194         {
2195           GtkWidget *item;
2196
2197           item = gtk_menu_get_attach_widget (GTK_MENU (parent));
2198           if (GTK_IS_MENU_ITEM (item))
2199             _gtk_action_sync_menu_visible (NULL, item, empty);
2200           if (GTK_IS_WIDGET (filler))
2201             {
2202               if (empty)
2203                 gtk_widget_show (filler);
2204               else
2205                 gtk_widget_hide (filler);
2206             }
2207         }
2208
2209       g_list_free (children);
2210     }
2211 }
2212
2213 static void
2214 update_node (GtkUIManager *self, 
2215              GNode        *node,
2216              gboolean      in_popup,
2217              gboolean      popup_accels)
2218 {
2219   Node *info;
2220   GNode *child;
2221   GtkAction *action;
2222   const gchar *action_name;
2223   NodeUIReference *ref;
2224   
2225 #ifdef DEBUG_UI_MANAGER
2226   GList *tmp;
2227 #endif
2228
2229   g_return_if_fail (node != NULL);
2230   g_return_if_fail (NODE_INFO (node) != NULL);
2231
2232   info = NODE_INFO (node);
2233   
2234   if (!info->dirty)
2235     return;
2236
2237   if (info->type == NODE_TYPE_POPUP)
2238     {
2239       in_popup = TRUE;
2240       popup_accels = info->popup_accels;
2241     }
2242
2243 #ifdef DEBUG_UI_MANAGER
2244   g_print ("update_node name=%s dirty=%d popup %d (", 
2245            info->name, info->dirty, in_popup);
2246   for (tmp = info->uifiles; tmp != NULL; tmp = tmp->next)
2247     {
2248       NodeUIReference *ref = tmp->data;
2249       g_print("%s:%u", g_quark_to_string (ref->action_quark), ref->merge_id);
2250       if (tmp->next)
2251         g_print (", ");
2252     }
2253   g_print (")\n");
2254 #endif
2255
2256   if (info->uifiles == NULL) {
2257     /* We may need to remove this node.
2258      * This must be done in post order
2259      */
2260     goto recurse_children;
2261   }
2262   
2263   ref = info->uifiles->data;
2264   action_name = g_quark_to_string (ref->action_quark);
2265   action = get_action_by_name (self, action_name);
2266   
2267   info->dirty = FALSE;
2268   
2269   /* Check if the node doesn't have an action and must have an action */
2270   if (action == NULL &&
2271       info->type != NODE_TYPE_ROOT &&
2272       info->type != NODE_TYPE_MENUBAR &&
2273       info->type != NODE_TYPE_TOOLBAR &&
2274       info->type != NODE_TYPE_POPUP &&
2275       info->type != NODE_TYPE_SEPARATOR &&
2276       info->type != NODE_TYPE_MENU_PLACEHOLDER &&
2277       info->type != NODE_TYPE_TOOLBAR_PLACEHOLDER)
2278     {
2279       g_warning ("%s: missing action %s", info->name, action_name);
2280       
2281       return;
2282     }
2283   
2284   if (action)
2285     gtk_action_set_accel_group (action, self->private_data->accel_group);
2286   
2287   /* If the widget already has a proxy and the action hasn't changed, then
2288    * we only have to update the tearoff menu items.
2289    */
2290   if (info->proxy != NULL && action == info->action)
2291     {
2292       if (info->type == NODE_TYPE_MENU) 
2293         {
2294           GtkWidget *menu;
2295           GList *siblings;
2296           
2297           if (GTK_IS_MENU (info->proxy))
2298             menu = info->proxy;
2299           else
2300             menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
2301           siblings = gtk_container_get_children (GTK_CONTAINER (menu));
2302           if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
2303             {
2304               if (self->private_data->add_tearoffs && !in_popup)
2305                 gtk_widget_show (GTK_WIDGET (siblings->data));
2306               else
2307                 gtk_widget_hide (GTK_WIDGET (siblings->data));
2308             }
2309           g_list_free (siblings);
2310         }
2311       
2312       goto recurse_children;
2313     }
2314   
2315   switch (info->type)
2316     {
2317     case NODE_TYPE_MENUBAR:
2318       if (info->proxy == NULL)
2319         {
2320           info->proxy = gtk_menu_bar_new ();
2321           g_object_ref_sink (info->proxy);
2322           gtk_widget_set_name (info->proxy, info->name);
2323           gtk_widget_show (info->proxy);
2324           g_signal_emit (self, ui_manager_signals[ADD_WIDGET], 0, info->proxy);
2325         }
2326       break;
2327     case NODE_TYPE_POPUP:
2328       if (info->proxy == NULL) 
2329         {
2330           info->proxy = gtk_menu_new ();
2331           g_object_ref_sink (info->proxy);
2332         }
2333       gtk_widget_set_name (info->proxy, info->name);
2334       break;
2335     case NODE_TYPE_MENU:
2336       {
2337         GtkWidget *prev_submenu = NULL;
2338         GtkWidget *menu = NULL;
2339         GList *siblings;
2340
2341         /* remove the proxy if it is of the wrong type ... */
2342         if (info->proxy &&  
2343             G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->menu_item_type)
2344           {
2345             if (GTK_IS_MENU_ITEM (info->proxy))
2346               {
2347                 prev_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
2348                 if (prev_submenu)
2349                   {
2350                     g_object_ref (prev_submenu);
2351                     gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
2352                 }
2353               }
2354
2355             gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), NULL);
2356             gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2357                                   info->proxy);
2358             g_object_unref (info->proxy);
2359             info->proxy = NULL;
2360           }
2361
2362         /* create proxy if needed ... */
2363         if (info->proxy == NULL)
2364           {
2365             /* ... if the action already provides a menu, then use
2366              * that menu instead of creating an empty one
2367              */
2368             if ((NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM ||
2369                  NODE_INFO (node->parent)->type == NODE_TYPE_MENUITEM) &&
2370                 GTK_ACTION_GET_CLASS (action)->create_menu)
2371               {
2372                 menu = gtk_action_create_menu (action);
2373               }
2374
2375             if (!menu)
2376               {
2377                 GtkWidget *tearoff;
2378                 GtkWidget *filler;
2379             
2380                 menu = gtk_menu_new ();
2381                 gtk_widget_set_name (menu, info->name);
2382                 tearoff = gtk_tearoff_menu_item_new ();
2383                 gtk_widget_set_no_show_all (tearoff, TRUE);
2384                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
2385                 filler = gtk_menu_item_new_with_label (_("Empty"));
2386                 g_object_set_data (G_OBJECT (filler),
2387                                    I_("gtk-empty-menu-item"),
2388                                    GINT_TO_POINTER (TRUE));
2389                 gtk_widget_set_sensitive (filler, FALSE);
2390                 gtk_widget_set_no_show_all (filler, TRUE);
2391                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), filler);
2392               }
2393             
2394             if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM)
2395               {
2396                 info->proxy = menu;
2397                 g_object_ref_sink (info->proxy);
2398                 gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (NODE_INFO (node->parent)->proxy),
2399                                                menu);
2400               }
2401             else
2402               {
2403                 GtkWidget *menushell;
2404                 gint pos;
2405                 
2406                 if (find_menu_position (node, &menushell, &pos))
2407                   {
2408                      info->proxy = gtk_action_create_menu_item (action);
2409                      g_object_ref_sink (info->proxy);
2410                      g_signal_connect (info->proxy, "notify::visible",
2411                                        G_CALLBACK (update_smart_separators), NULL);
2412                      gtk_widget_set_name (info->proxy, info->name);
2413                 
2414                      gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), menu);
2415                      gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos);
2416                  }
2417               }
2418           }
2419         else
2420           gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), action);
2421         
2422         if (prev_submenu)
2423           {
2424             gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy),
2425                                        prev_submenu);
2426             g_object_unref (prev_submenu);
2427           }
2428         
2429         if (GTK_IS_MENU (info->proxy))
2430           menu = info->proxy;
2431         else
2432           menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
2433
2434         siblings = gtk_container_get_children (GTK_CONTAINER (menu));
2435         if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
2436           {
2437             if (self->private_data->add_tearoffs && !in_popup)
2438               gtk_widget_show (GTK_WIDGET (siblings->data));
2439             else
2440               gtk_widget_hide (GTK_WIDGET (siblings->data));
2441           }
2442         g_list_free (siblings);
2443       }
2444       break;
2445     case NODE_TYPE_UNDECIDED:
2446       g_warning ("found undecided node!");
2447       break;
2448     case NODE_TYPE_ROOT:
2449       break;
2450     case NODE_TYPE_TOOLBAR:
2451       if (info->proxy == NULL)
2452         {
2453           info->proxy = gtk_toolbar_new ();
2454           g_object_ref_sink (info->proxy);
2455           gtk_widget_set_name (info->proxy, info->name);
2456           gtk_widget_show (info->proxy);
2457           g_signal_emit (self, ui_manager_signals[ADD_WIDGET], 0, info->proxy);
2458         }
2459       break;
2460     case NODE_TYPE_MENU_PLACEHOLDER:
2461       /* create menu items for placeholders if necessary ... */
2462       if (!GTK_IS_SEPARATOR_MENU_ITEM (info->proxy) ||
2463           !GTK_IS_SEPARATOR_MENU_ITEM (info->extra))
2464         {
2465           if (info->proxy)
2466             {
2467               gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2468                                     info->proxy);
2469               g_object_unref (info->proxy);
2470               info->proxy = NULL;
2471             }
2472           if (info->extra)
2473             {
2474               gtk_container_remove (GTK_CONTAINER (info->extra->parent),
2475                                     info->extra);
2476               g_object_unref (info->extra);
2477               info->extra = NULL;
2478             }
2479         }
2480       if (info->proxy == NULL)
2481         {
2482           GtkWidget *menushell;
2483           gint pos;
2484           
2485           if (find_menu_position (node, &menushell, &pos))
2486             {
2487               info->proxy = gtk_separator_menu_item_new ();
2488               g_object_ref_sink (info->proxy);
2489               g_object_set_data (G_OBJECT (info->proxy),
2490                                  I_("gtk-separator-mode"),
2491                                  GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
2492               gtk_widget_set_no_show_all (info->proxy, TRUE);
2493               gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
2494                                      NODE_INFO (node)->proxy, pos);
2495           
2496               info->extra = gtk_separator_menu_item_new ();
2497               g_object_ref_sink (info->extra);
2498               g_object_set_data (G_OBJECT (info->extra),
2499                                  I_("gtk-separator-mode"),
2500                                  GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
2501               gtk_widget_set_no_show_all (info->extra, TRUE);
2502               gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
2503                                      NODE_INFO (node)->extra, pos + 1);
2504             }
2505         }
2506       break;
2507     case NODE_TYPE_TOOLBAR_PLACEHOLDER:
2508       /* create toolbar items for placeholders if necessary ... */
2509       if (!GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy) ||
2510           !GTK_IS_SEPARATOR_TOOL_ITEM (info->extra))
2511         {
2512           if (info->proxy)
2513             {
2514               gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2515                                     info->proxy);
2516               g_object_unref (info->proxy);
2517               info->proxy = NULL;
2518             }
2519           if (info->extra)
2520             {
2521               gtk_container_remove (GTK_CONTAINER (info->extra->parent),
2522                                     info->extra);
2523               g_object_unref (info->extra);
2524               info->extra = NULL;
2525             }
2526         }
2527       if (info->proxy == NULL)
2528         {
2529           GtkWidget *toolbar;
2530           gint pos;
2531           GtkToolItem *item;    
2532           
2533           if (find_toolbar_position (node, &toolbar, &pos))
2534             {
2535               item = gtk_separator_tool_item_new ();
2536               gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
2537               info->proxy = GTK_WIDGET (item);
2538               g_object_ref_sink (info->proxy);
2539               g_object_set_data (G_OBJECT (info->proxy),
2540                                  I_("gtk-separator-mode"),
2541                                  GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
2542               gtk_widget_set_no_show_all (info->proxy, TRUE);
2543           
2544               item = gtk_separator_tool_item_new ();
2545               gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos+1);
2546               info->extra = GTK_WIDGET (item);
2547               g_object_ref_sink (info->extra);
2548               g_object_set_data (G_OBJECT (info->extra),
2549                                  I_("gtk-separator-mode"),
2550                                  GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
2551               gtk_widget_set_no_show_all (info->extra, TRUE);
2552             }
2553         }
2554       break;
2555     case NODE_TYPE_MENUITEM:
2556       /* remove the proxy if it is of the wrong type ... */
2557       if (info->proxy &&  
2558           G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->menu_item_type)
2559         {
2560           g_signal_handlers_disconnect_by_func (info->proxy,
2561                                                 G_CALLBACK (update_smart_separators),
2562                                                 NULL);  
2563           gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), NULL);
2564           gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2565                                 info->proxy);
2566           g_object_unref (info->proxy);
2567           info->proxy = NULL;
2568         }
2569       /* create proxy if needed ... */
2570       if (info->proxy == NULL)
2571         {
2572           GtkWidget *menushell;
2573           gint pos;
2574           
2575           if (find_menu_position (node, &menushell, &pos))
2576             {
2577               info->proxy = gtk_action_create_menu_item (action);
2578               g_object_ref_sink (info->proxy);
2579               gtk_widget_set_name (info->proxy, info->name);
2580           
2581               gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
2582                                      info->proxy, pos);
2583            }
2584         }
2585       else
2586         {
2587           g_signal_handlers_disconnect_by_func (info->proxy,
2588                                                 G_CALLBACK (update_smart_separators),
2589                                                 NULL);
2590           gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
2591           gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), action);
2592         }
2593
2594       if (info->proxy)
2595         {
2596           g_signal_connect (info->proxy, "notify::visible",
2597                             G_CALLBACK (update_smart_separators), NULL);
2598           if (in_popup && !popup_accels)
2599             {
2600               /* don't show accels in popups */
2601               GtkWidget *label = GTK_BIN (info->proxy)->child;
2602               g_object_set (label, "accel-closure", NULL, NULL);
2603             }
2604         }
2605       
2606       break;
2607     case NODE_TYPE_TOOLITEM:
2608       /* remove the proxy if it is of the wrong type ... */
2609       if (info->proxy && 
2610           G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->toolbar_item_type)
2611         {
2612           g_signal_handlers_disconnect_by_func (info->proxy,
2613                                                 G_CALLBACK (update_smart_separators),
2614                                                 NULL);
2615           gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), NULL);
2616           gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2617                                 info->proxy);
2618           g_object_unref (info->proxy);
2619           info->proxy = NULL;
2620         }
2621       /* create proxy if needed ... */
2622       if (info->proxy == NULL)
2623         {
2624           GtkWidget *toolbar;
2625           gint pos;
2626           
2627           if (find_toolbar_position (node, &toolbar, &pos))
2628             {
2629               info->proxy = gtk_action_create_tool_item (action);
2630               g_object_ref_sink (info->proxy);
2631               gtk_widget_set_name (info->proxy, info->name);
2632               
2633               gtk_toolbar_insert (GTK_TOOLBAR (toolbar),
2634                                   GTK_TOOL_ITEM (info->proxy), pos);
2635             }
2636         }
2637       else
2638         {
2639           g_signal_handlers_disconnect_by_func (info->proxy,
2640                                                 G_CALLBACK (update_smart_separators),
2641                                                 NULL);
2642           gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), action);
2643         }
2644
2645       if (info->proxy)
2646         {
2647           g_signal_connect (info->proxy, "notify::visible",
2648                             G_CALLBACK (update_smart_separators), NULL);
2649         }
2650       break;
2651     case NODE_TYPE_SEPARATOR:
2652       if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR ||
2653           NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER)
2654         {
2655           GtkWidget *toolbar;
2656           gint pos;
2657           gint separator_mode;
2658           GtkToolItem *item;
2659
2660           if (GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy))
2661             {
2662               gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2663                                     info->proxy);
2664               g_object_unref (info->proxy);
2665               info->proxy = NULL;
2666             }
2667           
2668           if (find_toolbar_position (node, &toolbar, &pos))
2669             {
2670               item  = gtk_separator_tool_item_new ();
2671               gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
2672               info->proxy = GTK_WIDGET (item);
2673               g_object_ref_sink (info->proxy);
2674               gtk_widget_set_no_show_all (info->proxy, TRUE);
2675               if (info->expand)
2676                 {
2677                   gtk_tool_item_set_expand (GTK_TOOL_ITEM (item), TRUE);
2678                   gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (item), FALSE);
2679                   separator_mode = SEPARATOR_MODE_VISIBLE;
2680                 }
2681               else
2682                 separator_mode = SEPARATOR_MODE_SMART;
2683           
2684               g_object_set_data (G_OBJECT (info->proxy),
2685                                  I_("gtk-separator-mode"),
2686                                  GINT_TO_POINTER (separator_mode));
2687               gtk_widget_show (info->proxy);
2688             }
2689         }
2690       else
2691         {
2692           GtkWidget *menushell;
2693           gint pos;
2694           
2695           if (GTK_IS_SEPARATOR_MENU_ITEM (info->proxy))
2696             {
2697               gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2698                                     info->proxy);
2699               g_object_unref (info->proxy);
2700               info->proxy = NULL;
2701             }
2702           
2703           if (find_menu_position (node, &menushell, &pos))
2704             {
2705               info->proxy = gtk_separator_menu_item_new ();
2706               g_object_ref_sink (info->proxy);
2707               gtk_widget_set_no_show_all (info->proxy, TRUE);
2708               g_object_set_data (G_OBJECT (info->proxy),
2709                                  I_("gtk-separator-mode"),
2710                                  GINT_TO_POINTER (SEPARATOR_MODE_SMART));
2711               gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
2712                                      info->proxy, pos);
2713               gtk_widget_show (info->proxy);
2714             }
2715         }
2716       break;
2717     case NODE_TYPE_ACCELERATOR:
2718       gtk_action_connect_accelerator (action);
2719       break;
2720     }
2721   
2722   if (action)
2723     g_object_ref (action);
2724   if (info->action)
2725     g_object_unref (info->action);
2726   info->action = action;
2727
2728  recurse_children:
2729   /* process children */
2730   child = node->children;
2731   while (child)
2732     {
2733       GNode *current;
2734       
2735       current = child;
2736       child = current->next;
2737       update_node (self, current, in_popup, popup_accels);
2738     }
2739   
2740   if (info->proxy) 
2741     {
2742       if (info->type == NODE_TYPE_MENU && GTK_IS_MENU_ITEM (info->proxy)) 
2743         update_smart_separators (gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy)));
2744       else if (info->type == NODE_TYPE_MENU || 
2745                info->type == NODE_TYPE_TOOLBAR || 
2746                info->type == NODE_TYPE_POPUP) 
2747         update_smart_separators (info->proxy);
2748     }
2749   
2750   /* handle cleanup of dead nodes */
2751   if (node->children == NULL && info->uifiles == NULL)
2752     {
2753       if (info->proxy)
2754         gtk_widget_destroy (info->proxy);
2755       if (info->extra)
2756         gtk_widget_destroy (info->extra);
2757       if (info->type == NODE_TYPE_ACCELERATOR && info->action != NULL)
2758         gtk_action_disconnect_accelerator (info->action);
2759       free_node (node);
2760       g_node_destroy (node);
2761     }
2762 }
2763
2764 static gboolean
2765 do_updates (GtkUIManager *self)
2766 {
2767   /* this function needs to check through the tree for dirty nodes.
2768    * For such nodes, it needs to do the following:
2769    *
2770    * 1) check if they are referenced by any loaded UI files anymore.
2771    *    In which case, the proxy widget should be destroyed, unless
2772    *    there are any subnodes.
2773    *
2774    * 2) lookup the action for this node again.  If it is different to
2775    *    the current one (or if no previous action has been looked up),
2776    *    the proxy is reconnected to the new action (or a new proxy widget
2777    *    is created and added to the parent container).
2778    */
2779   update_node (self, self->private_data->root_node, FALSE, FALSE);
2780
2781   self->private_data->update_tag = 0;
2782
2783   return FALSE;
2784 }
2785
2786 static gboolean
2787 do_updates_idle (GtkUIManager *self)
2788 {
2789   do_updates (self);
2790
2791   return FALSE;
2792 }
2793
2794 static void
2795 queue_update (GtkUIManager *self)
2796 {
2797   if (self->private_data->update_tag != 0)
2798     return;
2799
2800   self->private_data->update_tag = gdk_threads_add_idle (
2801                                                (GSourceFunc)do_updates_idle, 
2802                                                self);
2803 }
2804
2805
2806 /**
2807  * gtk_ui_manager_ensure_update:
2808  * @self: a #GtkUIManager
2809  * 
2810  * Makes sure that all pending updates to the UI have been completed.
2811  *
2812  * This may occasionally be necessary, since #GtkUIManager updates the 
2813  * UI in an idle function. A typical example where this function is
2814  * useful is to enforce that the menubar and toolbar have been added to 
2815  * the main window before showing it:
2816  * |[
2817  * gtk_container_add (GTK_CONTAINER (window), vbox); 
2818  * g_signal_connect (merge, "add-widget", 
2819  *                   G_CALLBACK (add_widget), vbox);
2820  * gtk_ui_manager_add_ui_from_file (merge, "my-menus");
2821  * gtk_ui_manager_add_ui_from_file (merge, "my-toolbars");
2822  * gtk_ui_manager_ensure_update (merge);  
2823  * gtk_widget_show (window);
2824  * ]|
2825  *
2826  * Since: 2.4
2827  **/
2828 void
2829 gtk_ui_manager_ensure_update (GtkUIManager *self)
2830 {
2831   if (self->private_data->update_tag != 0)
2832     {
2833       g_source_remove (self->private_data->update_tag);
2834       do_updates (self);
2835     }
2836 }
2837
2838 static gboolean
2839 dirty_traverse_func (GNode   *node,
2840                      gpointer data)
2841 {
2842   NODE_INFO (node)->dirty = TRUE;
2843   return FALSE;
2844 }
2845
2846 static void
2847 dirty_all_nodes (GtkUIManager *self)
2848 {
2849   g_node_traverse (self->private_data->root_node,
2850                    G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2851                    dirty_traverse_func, NULL);
2852   queue_update (self);
2853 }
2854
2855 static void
2856 mark_node_dirty (GNode *node)
2857 {
2858   GNode *p;
2859
2860   /* FIXME could optimize this */
2861   for (p = node; p; p = p->parent)
2862     NODE_INFO (p)->dirty = TRUE;  
2863 }
2864
2865 static const gchar *
2866 open_tag_format (NodeType type)
2867 {
2868   switch (type)
2869     {
2870     case NODE_TYPE_UNDECIDED: return "%*s<UNDECIDED"; 
2871     case NODE_TYPE_ROOT: return "%*s<ui"; 
2872     case NODE_TYPE_MENUBAR: return "%*s<menubar";
2873     case NODE_TYPE_MENU: return "%*s<menu";
2874     case NODE_TYPE_TOOLBAR: return "%*s<toolbar";
2875     case NODE_TYPE_MENU_PLACEHOLDER:
2876     case NODE_TYPE_TOOLBAR_PLACEHOLDER: return "%*s<placeholder";
2877     case NODE_TYPE_POPUP: return "%*s<popup";
2878     case NODE_TYPE_MENUITEM: return "%*s<menuitem";
2879     case NODE_TYPE_TOOLITEM: return "%*s<toolitem";
2880     case NODE_TYPE_SEPARATOR: return "%*s<separator";
2881     case NODE_TYPE_ACCELERATOR: return "%*s<accelerator";
2882     default: return NULL;
2883     }
2884 }
2885
2886 static const gchar *
2887 close_tag_format (NodeType type)
2888 {
2889   switch (type)
2890     {
2891     case NODE_TYPE_UNDECIDED: return "%*s</UNDECIDED>\n";
2892     case NODE_TYPE_ROOT: return "%*s</ui>\n";
2893     case NODE_TYPE_MENUBAR: return "%*s</menubar>\n";
2894     case NODE_TYPE_MENU: return "%*s</menu>\n";
2895     case NODE_TYPE_TOOLBAR: return "%*s</toolbar>\n";
2896     case NODE_TYPE_MENU_PLACEHOLDER:
2897     case NODE_TYPE_TOOLBAR_PLACEHOLDER: return "%*s</placeholder>\n";
2898     case NODE_TYPE_POPUP: return "%*s</popup>\n";
2899     default: return NULL;
2900     }
2901 }
2902
2903 static void
2904 print_node (GtkUIManager *self,
2905             GNode        *node,
2906             gint          indent_level,
2907             GString      *buffer)
2908 {
2909   Node  *mnode;
2910   GNode *child;
2911   const gchar *open_fmt;
2912   const gchar *close_fmt;
2913
2914   mnode = node->data;
2915
2916   open_fmt = open_tag_format (mnode->type);
2917   close_fmt = close_tag_format (mnode->type);
2918
2919   g_string_append_printf (buffer, open_fmt, indent_level, "");
2920
2921   if (mnode->type != NODE_TYPE_ROOT)
2922     {
2923       if (mnode->name)
2924         g_string_append_printf (buffer, " name=\"%s\"", mnode->name);
2925       
2926       if (mnode->action_name)
2927         g_string_append_printf (buffer, " action=\"%s\"",
2928                                 g_quark_to_string (mnode->action_name));
2929     }
2930
2931   g_string_append (buffer, close_fmt ? ">\n" : "/>\n");
2932
2933   for (child = node->children; child != NULL; child = child->next)
2934     print_node (self, child, indent_level + 2, buffer);
2935
2936   if (close_fmt)
2937     g_string_append_printf (buffer, close_fmt, indent_level, "");
2938 }
2939
2940 static gboolean
2941 gtk_ui_manager_buildable_custom_tag_start (GtkBuildable  *buildable,
2942                                            GtkBuilder    *builder,
2943                                            GObject       *child,
2944                                            const gchar   *tagname,
2945                                            GMarkupParser *parser,
2946                                            gpointer      *data)
2947 {
2948   if (child)
2949     return FALSE;
2950
2951   if (strcmp (tagname, "ui") == 0)
2952     {
2953       ParseContext *ctx;
2954
2955       ctx = g_new0 (ParseContext, 1);
2956       ctx->state = STATE_START;
2957       ctx->self = GTK_UI_MANAGER (buildable);
2958       ctx->current = NULL;
2959       ctx->merge_id = gtk_ui_manager_new_merge_id (GTK_UI_MANAGER (buildable));
2960
2961       *data = ctx;
2962       *parser = ui_parser;
2963
2964       return TRUE;
2965     }
2966
2967   return FALSE;
2968
2969 }
2970
2971 static void
2972 gtk_ui_manager_buildable_custom_tag_end (GtkBuildable *buildable,
2973                                          GtkBuilder   *builder,
2974                                          GObject      *child,
2975                                          const gchar  *tagname,
2976                                          gpointer     *data)
2977 {
2978   queue_update (GTK_UI_MANAGER (buildable));
2979   g_object_notify (G_OBJECT (buildable), "ui");
2980   g_free (data);
2981 }
2982
2983 /**
2984  * gtk_ui_manager_get_ui:
2985  * @self: a #GtkUIManager
2986  * 
2987  * Creates a <link linkend="XML-UI">UI definition</link> of the merged UI.
2988  * 
2989  * Return value: A newly allocated string containing an XML representation of 
2990  * the merged UI.
2991  *
2992  * Since: 2.4
2993  **/
2994 gchar *
2995 gtk_ui_manager_get_ui (GtkUIManager *self)
2996 {
2997   GString *buffer;
2998
2999   buffer = g_string_new (NULL);
3000
3001   gtk_ui_manager_ensure_update (self); 
3002  
3003   print_node (self, self->private_data->root_node, 0, buffer);  
3004
3005   return g_string_free (buffer, FALSE);
3006 }
3007
3008 #if defined (G_OS_WIN32) && !defined (_WIN64)
3009
3010 #undef gtk_ui_manager_add_ui_from_file
3011
3012 guint
3013 gtk_ui_manager_add_ui_from_file (GtkUIManager *self,
3014                                  const gchar  *filename,
3015                                  GError      **error)
3016 {
3017   gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error);
3018   guint retval;
3019
3020   if (utf8_filename == NULL)
3021     return 0;
3022   
3023   retval = gtk_ui_manager_add_ui_from_file_utf8 (self, utf8_filename, error);
3024
3025   g_free (utf8_filename);
3026
3027   return retval;
3028 }
3029
3030 #endif
3031
3032 #define __GTK_UI_MANAGER_C__
3033 #include "gtkaliasdef.c"