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