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