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