]> Pileus Git - ~andy/gtk/blob - gtk/gtkuimanager.c
More of the same
[~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 void
1790 find_menu_position (GNode      *node, 
1791                     GtkWidget **menushell_p, 
1792                     gint       *pos_p)
1793 {
1794   GtkWidget *menushell;
1795   gint pos;
1796
1797   g_return_if_fail (node != NULL);
1798   g_return_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
1804   /* first sibling -- look at parent */
1805   if (node->prev == NULL)
1806     {
1807       GNode *parent;
1808       GList *siblings;
1809
1810       parent = node->parent;
1811       switch (NODE_INFO (parent)->type)
1812         {
1813         case NODE_TYPE_MENUBAR:
1814         case NODE_TYPE_POPUP:
1815           menushell = NODE_INFO (parent)->proxy;
1816           pos = 0;
1817           break;
1818         case NODE_TYPE_MENU:
1819           menushell = NODE_INFO (parent)->proxy;
1820           if (GTK_IS_MENU_ITEM (menushell))
1821             menushell = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menushell));
1822           siblings = gtk_container_get_children (GTK_CONTAINER (menushell));
1823           if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
1824             pos = 1;
1825           else
1826             pos = 0;
1827           g_list_free (siblings);
1828           break;
1829         case NODE_TYPE_MENU_PLACEHOLDER:
1830           menushell = gtk_widget_get_parent (NODE_INFO (parent)->proxy);
1831           g_return_if_fail (GTK_IS_MENU_SHELL (menushell));
1832           pos = g_list_index (GTK_MENU_SHELL (menushell)->children,
1833                               NODE_INFO (parent)->proxy) + 1;
1834           break;
1835         default:
1836           g_warning("%s: bad parent node type %d", G_STRLOC,
1837                     NODE_INFO (parent)->type);
1838           return;
1839         }
1840     }
1841   else
1842     {
1843       GtkWidget *prev_child;
1844       GNode *sibling;
1845
1846       sibling = node->prev;
1847       if (NODE_INFO (sibling)->type == NODE_TYPE_MENU_PLACEHOLDER)
1848         prev_child = NODE_INFO (sibling)->extra; /* second Separator */
1849       else
1850         prev_child = NODE_INFO (sibling)->proxy;
1851
1852       g_return_if_fail (GTK_IS_WIDGET (prev_child));
1853       menushell = gtk_widget_get_parent (prev_child);
1854       g_return_if_fail (GTK_IS_MENU_SHELL (menushell));
1855
1856       pos = g_list_index (GTK_MENU_SHELL (menushell)->children, prev_child) + 1;
1857     }
1858
1859   if (menushell_p)
1860     *menushell_p = menushell;
1861   if (pos_p)
1862     *pos_p = pos;
1863 }
1864
1865 static void
1866 find_toolbar_position (GNode      *node, 
1867                        GtkWidget **toolbar_p, 
1868                        gint       *pos_p)
1869 {
1870   GtkWidget *toolbar;
1871   gint pos;
1872
1873   g_return_if_fail (node != NULL);
1874   g_return_if_fail (NODE_INFO (node)->type == NODE_TYPE_TOOLBAR ||
1875                     NODE_INFO (node)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER ||
1876                     NODE_INFO (node)->type == NODE_TYPE_TOOLITEM ||
1877                     NODE_INFO (node)->type == NODE_TYPE_SEPARATOR);
1878   
1879   /* first sibling -- look at parent */
1880   if (node->prev == NULL)
1881     {
1882       GNode *parent;
1883
1884       parent = node->parent;
1885       switch (NODE_INFO (parent)->type)
1886         {
1887         case NODE_TYPE_TOOLBAR:
1888           toolbar = NODE_INFO (parent)->proxy;
1889           pos = 0;
1890           break;
1891         case NODE_TYPE_TOOLBAR_PLACEHOLDER:
1892           toolbar = gtk_widget_get_parent (NODE_INFO (parent)->proxy);
1893           g_return_if_fail (GTK_IS_TOOLBAR (toolbar));
1894           pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar),
1895                                             GTK_TOOL_ITEM (NODE_INFO (parent)->proxy)) + 1;
1896           break;
1897         default:
1898           g_warning ("%s: bad parent node type %d", G_STRLOC,
1899                      NODE_INFO (parent)->type);
1900           return;
1901         }
1902     }
1903   else
1904     {
1905       GtkWidget *prev_child;
1906       GNode *sibling;
1907
1908       sibling = node->prev;
1909       if (NODE_INFO (sibling)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER)
1910         prev_child = NODE_INFO (sibling)->extra; /* second Separator */
1911       else
1912         prev_child = NODE_INFO (sibling)->proxy;
1913
1914       g_return_if_fail (GTK_IS_WIDGET (prev_child));
1915       toolbar = gtk_widget_get_parent (prev_child);
1916       g_return_if_fail (GTK_IS_TOOLBAR (toolbar));
1917
1918       pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar),
1919                                         GTK_TOOL_ITEM (prev_child)) + 1;
1920     }
1921   
1922   if (toolbar_p)
1923     *toolbar_p = toolbar;
1924   if (pos_p)
1925     *pos_p = pos;
1926 }
1927
1928 /**
1929  * _gtk_menu_is_empty:
1930  * @menu: a #GtkMenu or %NULL
1931  * 
1932  * Determines whether @menu is empty. A menu is considered empty if it
1933  * the only visible children are tearoff menu items or "filler" menu 
1934  * items which were inserted to mark the menu as empty.
1935  * 
1936  * This function is used by #GtkAction.
1937  *
1938  * Return value: whether @menu is empty.
1939  **/
1940 gboolean
1941 _gtk_menu_is_empty (GtkWidget *menu)
1942 {
1943   GList *children, *cur;
1944
1945   g_return_val_if_fail (menu == NULL || GTK_IS_MENU (menu), TRUE);
1946
1947   if (!menu)
1948     return FALSE;
1949
1950   children = gtk_container_get_children (GTK_CONTAINER (menu));
1951
1952   cur = children;
1953   while (cur) 
1954     {
1955       if (GTK_WIDGET_VISIBLE (cur->data))
1956         {
1957           if (!GTK_IS_TEAROFF_MENU_ITEM (cur->data) &&
1958               !g_object_get_data (cur->data, "gtk-empty-menu-item"))
1959             return FALSE;
1960         }
1961       cur = cur->next;
1962     }
1963   g_list_free (children);
1964
1965   return TRUE;
1966 }
1967
1968 enum {
1969   SEPARATOR_MODE_SMART,
1970   SEPARATOR_MODE_VISIBLE,
1971   SEPARATOR_MODE_HIDDEN
1972 };
1973
1974 static void
1975 update_smart_separators (GtkWidget *proxy)
1976 {
1977   GtkWidget *parent = NULL;
1978   
1979   if (GTK_IS_MENU (proxy) || GTK_IS_TOOLBAR (proxy))
1980     parent = proxy;
1981   else if (GTK_IS_MENU_ITEM (proxy) || GTK_IS_TOOL_ITEM (proxy))
1982     parent = gtk_widget_get_parent (proxy);
1983
1984   if (parent) 
1985     {
1986       gboolean visible;
1987       gboolean empty;
1988       GList *children, *cur, *last;
1989       GtkWidget *filler;
1990
1991       children = gtk_container_get_children (GTK_CONTAINER (parent));
1992       
1993       visible = FALSE;
1994       last = NULL;
1995       empty = TRUE;
1996       filler = NULL;
1997
1998       cur = children;
1999       while (cur) 
2000         {
2001           if (g_object_get_data (cur->data, "gtk-empty-menu-item"))
2002             {
2003               filler = cur->data;
2004             }
2005           else if (GTK_IS_SEPARATOR_MENU_ITEM (cur->data) ||
2006                    GTK_IS_SEPARATOR_TOOL_ITEM (cur->data))
2007             {
2008               gint mode = 
2009                 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cur->data), 
2010                                                     "gtk-separator-mode"));
2011               switch (mode) 
2012                 {
2013                 case SEPARATOR_MODE_VISIBLE:
2014                   gtk_widget_show (GTK_WIDGET (cur->data));
2015                   last = NULL;
2016                   visible = FALSE;
2017                   break;
2018                 case SEPARATOR_MODE_HIDDEN:
2019                   gtk_widget_hide (GTK_WIDGET (cur->data));
2020                   break;
2021                 case SEPARATOR_MODE_SMART:
2022                   if (visible)
2023                     {
2024                       gtk_widget_show (GTK_WIDGET (cur->data));
2025                       last = cur;
2026                       visible = FALSE;
2027                     }
2028                   else 
2029                     gtk_widget_hide (GTK_WIDGET (cur->data));
2030                   break;
2031                 }
2032             }
2033           else if (GTK_WIDGET_VISIBLE (cur->data))
2034             {
2035               last = NULL;
2036               if (GTK_IS_TEAROFF_MENU_ITEM (cur->data) || cur->data == filler)
2037                 visible = FALSE;
2038               else 
2039                 {
2040                   visible = TRUE;
2041                   empty = FALSE;
2042                 }
2043             }
2044           
2045           cur = cur->next;
2046         }
2047
2048       if (last)
2049         gtk_widget_hide (GTK_WIDGET (last->data));
2050
2051       if (GTK_IS_MENU (parent)) 
2052         {
2053           GtkWidget *item;
2054
2055           item = gtk_menu_get_attach_widget (GTK_MENU (parent));
2056           if (GTK_IS_MENU_ITEM (item))
2057             _gtk_action_sync_menu_visible (NULL, item, empty);
2058           if (GTK_IS_WIDGET (filler))
2059             {
2060               if (empty)
2061                 gtk_widget_show (filler);
2062               else
2063                 gtk_widget_hide (filler);
2064             }
2065         }
2066
2067       g_list_free (children);
2068     }
2069 }
2070
2071 static void
2072 update_node (GtkUIManager *self, 
2073              GNode        *node,
2074              gboolean      in_popup)
2075 {
2076   Node *info;
2077   GNode *child;
2078   GtkAction *action;
2079   const gchar *action_name;
2080   NodeUIReference *ref;
2081   
2082 #ifdef DEBUG_UI_MANAGER
2083   GList *tmp;
2084 #endif
2085
2086   g_return_if_fail (node != NULL);
2087   g_return_if_fail (NODE_INFO (node) != NULL);
2088
2089   info = NODE_INFO (node);
2090   
2091   if (!info->dirty)
2092     return;
2093
2094   in_popup = in_popup || (info->type == NODE_TYPE_POPUP);
2095
2096 #ifdef DEBUG_UI_MANAGER
2097   g_print ("update_node name=%s dirty=%d popup %d (", 
2098            info->name, info->dirty, in_popup);
2099   for (tmp = info->uifiles; tmp != NULL; tmp = tmp->next)
2100     {
2101       NodeUIReference *ref = tmp->data;
2102       g_print("%s:%u", g_quark_to_string (ref->action_quark), ref->merge_id);
2103       if (tmp->next)
2104         g_print (", ");
2105     }
2106   g_print (")\n");
2107 #endif
2108
2109   if (info->uifiles == NULL) {
2110     /* We may need to remove this node.
2111      * This must be done in post order
2112      */
2113     goto recurse_children;
2114   }
2115   
2116   ref = info->uifiles->data;
2117   action_name = g_quark_to_string (ref->action_quark);
2118   action = get_action_by_name (self, action_name);
2119   
2120   info->dirty = FALSE;
2121   
2122   /* Check if the node doesn't have an action and must have an action */
2123   if (action == NULL &&
2124       info->type != NODE_TYPE_ROOT &&
2125       info->type != NODE_TYPE_MENUBAR &&
2126       info->type != NODE_TYPE_TOOLBAR &&
2127       info->type != NODE_TYPE_POPUP &&
2128       info->type != NODE_TYPE_SEPARATOR &&
2129       info->type != NODE_TYPE_MENU_PLACEHOLDER &&
2130       info->type != NODE_TYPE_TOOLBAR_PLACEHOLDER)
2131     {
2132       g_warning ("%s: missing action", info->name);
2133       
2134       goto recurse_children;
2135     }
2136   
2137   if (action)
2138     gtk_action_set_accel_group (action, self->private_data->accel_group);
2139   
2140   /* If the widget already has a proxy and the action hasn't changed, then
2141    * we only have to update the tearoff menu items.
2142    */
2143   if (info->proxy != NULL && action == info->action)
2144     {
2145       if (info->type == NODE_TYPE_MENU) 
2146         {
2147           GtkWidget *menu;
2148           GList *siblings;
2149           
2150           if (GTK_IS_MENU (info->proxy))
2151             menu = info->proxy;
2152           else
2153             menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
2154           siblings = gtk_container_get_children (GTK_CONTAINER (menu));
2155           if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
2156             {
2157               if (self->private_data->add_tearoffs && !in_popup)
2158                 gtk_widget_show (GTK_WIDGET (siblings->data));
2159               else
2160                 gtk_widget_hide (GTK_WIDGET (siblings->data));
2161             }
2162           g_list_free (siblings);
2163         }
2164       
2165       goto recurse_children;
2166     }
2167   
2168   switch (info->type)
2169     {
2170     case NODE_TYPE_MENUBAR:
2171       if (info->proxy == NULL)
2172         {
2173           info->proxy = gtk_menu_bar_new ();
2174           g_object_ref_sink (info->proxy);
2175           gtk_widget_set_name (info->proxy, info->name);
2176           gtk_widget_show (info->proxy);
2177           g_signal_emit (self, ui_manager_signals[ADD_WIDGET], 0, info->proxy);
2178         }
2179       break;
2180     case NODE_TYPE_POPUP:
2181       if (info->proxy == NULL) 
2182         {
2183           info->proxy = gtk_menu_new ();
2184           g_object_ref_sink (info->proxy);
2185         }
2186       gtk_widget_set_name (info->proxy, info->name);
2187       break;
2188     case NODE_TYPE_MENU:
2189       {
2190         GtkWidget *prev_submenu = NULL;
2191         GtkWidget *menu;
2192         GList *siblings;
2193         /* remove the proxy if it is of the wrong type ... */
2194         if (info->proxy &&  
2195             G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->menu_item_type)
2196           {
2197             if (GTK_IS_MENU_ITEM (info->proxy))
2198               {
2199                 prev_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
2200                 if (prev_submenu)
2201                   {
2202                     g_object_ref (prev_submenu);
2203                     gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
2204                 }
2205               }
2206
2207             gtk_action_disconnect_proxy (info->action, info->proxy);
2208             gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2209                                   info->proxy);
2210             g_object_unref (info->proxy);
2211             info->proxy = NULL;
2212           }
2213         /* create proxy if needed ... */
2214         if (info->proxy == NULL)
2215           {
2216             GtkWidget *tearoff;
2217             GtkWidget *filler;
2218             
2219             menu = gtk_menu_new ();
2220             gtk_widget_set_name (menu, info->name);
2221             tearoff = gtk_tearoff_menu_item_new ();
2222             gtk_widget_set_no_show_all (tearoff, TRUE);
2223             gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
2224             filler = gtk_menu_item_new_with_label (_("Empty"));
2225             g_object_set_data (G_OBJECT (filler),
2226                                I_("gtk-empty-menu-item"),
2227                                GINT_TO_POINTER (TRUE));
2228             gtk_widget_set_sensitive (filler, FALSE);
2229             gtk_widget_set_no_show_all (filler, TRUE);
2230             gtk_menu_shell_append (GTK_MENU_SHELL (menu), filler);
2231             
2232             if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM)
2233               {
2234                 info->proxy = menu;
2235                 g_object_ref_sink (info->proxy);
2236                 gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (NODE_INFO (node->parent)->proxy),
2237                                                menu);
2238               }
2239             else
2240               {
2241                 GtkWidget *menushell;
2242                 gint pos;
2243                 
2244                 find_menu_position (node, &menushell, &pos);
2245                 info->proxy = gtk_action_create_menu_item (action);
2246                 g_object_ref_sink (info->proxy);
2247                 g_signal_connect (info->proxy, "notify::visible",
2248                                   G_CALLBACK (update_smart_separators), NULL);
2249                 gtk_widget_set_name (info->proxy, info->name);
2250                 
2251                 gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), menu);
2252                 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos);
2253               }
2254           }
2255         else
2256           gtk_action_connect_proxy (action, info->proxy);
2257         
2258         if (prev_submenu)
2259           {
2260             gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy),
2261                                        prev_submenu);
2262             g_object_unref (prev_submenu);
2263           }
2264         
2265         if (GTK_IS_MENU (info->proxy))
2266           menu = info->proxy;
2267         else
2268           menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
2269         siblings = gtk_container_get_children (GTK_CONTAINER (menu));
2270         if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
2271           {
2272             if (self->private_data->add_tearoffs && !in_popup)
2273               gtk_widget_show (GTK_WIDGET (siblings->data));
2274             else
2275               gtk_widget_hide (GTK_WIDGET (siblings->data));
2276           }
2277         g_list_free (siblings);
2278       }
2279       break;
2280     case NODE_TYPE_UNDECIDED:
2281       g_warning ("found undecided node!");
2282       break;
2283     case NODE_TYPE_ROOT:
2284       break;
2285     case NODE_TYPE_TOOLBAR:
2286       if (info->proxy == NULL)
2287         {
2288           info->proxy = gtk_toolbar_new ();
2289           g_object_ref_sink (info->proxy);
2290           gtk_widget_set_name (info->proxy, info->name);
2291           gtk_widget_show (info->proxy);
2292           g_signal_emit (self, ui_manager_signals[ADD_WIDGET], 0, info->proxy);
2293         }
2294       break;
2295     case NODE_TYPE_MENU_PLACEHOLDER:
2296       /* create menu items for placeholders if necessary ... */
2297       if (!GTK_IS_SEPARATOR_MENU_ITEM (info->proxy) ||
2298           !GTK_IS_SEPARATOR_MENU_ITEM (info->extra))
2299         {
2300           if (info->proxy)
2301             {
2302               gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2303                                     info->proxy);
2304               g_object_unref (info->proxy);
2305               info->proxy = NULL;
2306             }
2307           if (info->extra)
2308             {
2309               gtk_container_remove (GTK_CONTAINER (info->extra->parent),
2310                                     info->extra);
2311               g_object_unref (info->extra);
2312               info->extra = NULL;
2313             }
2314         }
2315       if (info->proxy == NULL)
2316         {
2317           GtkWidget *menushell;
2318           gint pos;
2319           
2320           find_menu_position (node, &menushell, &pos);
2321           info->proxy = gtk_separator_menu_item_new ();
2322           g_object_ref_sink (info->proxy);
2323           g_object_set_data (G_OBJECT (info->proxy),
2324                              I_("gtk-separator-mode"),
2325                              GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
2326           gtk_widget_set_no_show_all (info->proxy, TRUE);
2327           gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
2328                                  NODE_INFO (node)->proxy, pos);
2329           
2330           info->extra = gtk_separator_menu_item_new ();
2331           g_object_ref_sink (info->extra);
2332           g_object_set_data (G_OBJECT (info->extra),
2333                              I_("gtk-separator-mode"),
2334                              GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
2335           gtk_widget_set_no_show_all (info->extra, TRUE);
2336           gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
2337                                  NODE_INFO (node)->extra, pos+1);
2338         }
2339       break;
2340     case NODE_TYPE_TOOLBAR_PLACEHOLDER:
2341       /* create toolbar items for placeholders if necessary ... */
2342       if (!GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy) ||
2343           !GTK_IS_SEPARATOR_TOOL_ITEM (info->extra))
2344         {
2345           if (info->proxy)
2346             {
2347               gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2348                                     info->proxy);
2349               g_object_unref (info->proxy);
2350               info->proxy = NULL;
2351             }
2352           if (info->extra)
2353             {
2354               gtk_container_remove (GTK_CONTAINER (info->extra->parent),
2355                                     info->extra);
2356               g_object_unref (info->extra);
2357               info->extra = NULL;
2358             }
2359         }
2360       if (info->proxy == NULL)
2361         {
2362           GtkWidget *toolbar;
2363           gint pos;
2364           GtkToolItem *item;    
2365           
2366           find_toolbar_position (node, &toolbar, &pos);
2367           item = gtk_separator_tool_item_new ();
2368           gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
2369           info->proxy = GTK_WIDGET (item);
2370           g_object_ref_sink (info->proxy);
2371           g_object_set_data (G_OBJECT (info->proxy),
2372                              I_("gtk-separator-mode"),
2373                              GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
2374           gtk_widget_set_no_show_all (info->proxy, TRUE);
2375           
2376           item = gtk_separator_tool_item_new ();
2377           gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos+1);
2378           info->extra = GTK_WIDGET (item);
2379           g_object_ref_sink (info->extra);
2380           g_object_set_data (G_OBJECT (info->extra),
2381                              I_("gtk-separator-mode"),
2382                              GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
2383           gtk_widget_set_no_show_all (info->extra, TRUE);
2384         }
2385       break;
2386     case NODE_TYPE_MENUITEM:
2387       /* remove the proxy if it is of the wrong type ... */
2388       if (info->proxy &&  
2389           G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->menu_item_type)
2390         {
2391           g_signal_handlers_disconnect_by_func (info->proxy,
2392                                                 G_CALLBACK (update_smart_separators),
2393                                                 NULL);  
2394           gtk_action_disconnect_proxy (info->action, info->proxy);
2395           gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2396                                 info->proxy);
2397           g_object_unref (info->proxy);
2398           info->proxy = NULL;
2399         }
2400       /* create proxy if needed ... */
2401       if (info->proxy == NULL)
2402         {
2403           GtkWidget *menushell;
2404           gint pos;
2405           
2406           find_menu_position (node, &menushell, &pos);
2407           info->proxy = gtk_action_create_menu_item (action);
2408           g_object_ref_sink (info->proxy);
2409           gtk_widget_set_name (info->proxy, info->name);
2410           
2411           gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
2412                                  info->proxy, pos);
2413         }
2414       else
2415         {
2416           g_signal_handlers_disconnect_by_func (info->proxy,
2417                                                 G_CALLBACK (update_smart_separators),
2418                                                 NULL);
2419           gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
2420           gtk_action_connect_proxy (action, info->proxy);
2421         }
2422       g_signal_connect (info->proxy, "notify::visible",
2423                         G_CALLBACK (update_smart_separators), NULL);
2424       if (in_popup) 
2425         {
2426           /* don't show accels in popups */
2427           GtkWidget *label = GTK_BIN (info->proxy)->child;
2428           g_object_set (label,
2429                         "accel_closure", NULL,
2430                         NULL);
2431         }
2432       
2433       break;
2434     case NODE_TYPE_TOOLITEM:
2435       /* remove the proxy if it is of the wrong type ... */
2436       if (info->proxy && 
2437           G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->toolbar_item_type)
2438         {
2439           g_signal_handlers_disconnect_by_func (info->proxy,
2440                                                 G_CALLBACK (update_smart_separators),
2441                                                 NULL);
2442           gtk_action_disconnect_proxy (info->action, info->proxy);
2443           gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2444                                 info->proxy);
2445           g_object_unref (info->proxy);
2446           info->proxy = NULL;
2447         }
2448       /* create proxy if needed ... */
2449       if (info->proxy == NULL)
2450         {
2451           GtkWidget *toolbar;
2452           gint pos;
2453           
2454           find_toolbar_position (node, &toolbar, &pos);
2455           info->proxy = gtk_action_create_tool_item (action);
2456           g_object_ref_sink (info->proxy);
2457           gtk_widget_set_name (info->proxy, info->name);
2458           
2459           gtk_toolbar_insert (GTK_TOOLBAR (toolbar),
2460                               GTK_TOOL_ITEM (info->proxy), pos);
2461         }
2462       else
2463         {
2464           g_signal_handlers_disconnect_by_func (info->proxy,
2465                                                 G_CALLBACK (update_smart_separators),
2466                                                 NULL);
2467           gtk_action_connect_proxy (action, info->proxy);
2468         }
2469
2470       /* FIXME: we must re-set the tooltip, since tooltips on toolitems can't be 
2471        * set before the toolitem is added to the toolbar.
2472        */
2473       {
2474         gchar *tooltip;
2475
2476         g_object_get (G_OBJECT (action), "tooltip", &tooltip, NULL);
2477         g_object_set (G_OBJECT (action), "tooltip", tooltip, NULL);
2478         g_free (tooltip);
2479       }
2480
2481       g_signal_connect (info->proxy, "notify::visible",
2482                         G_CALLBACK (update_smart_separators), NULL);
2483       break;
2484     case NODE_TYPE_SEPARATOR:
2485       if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR ||
2486           NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER)
2487         {
2488           GtkWidget *toolbar;
2489           gint pos;
2490           gint separator_mode;
2491           GtkToolItem *item;
2492
2493           if (GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy))
2494             {
2495               gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2496                                     info->proxy);
2497               g_object_unref (info->proxy);
2498               info->proxy = NULL;
2499             }
2500           
2501           find_toolbar_position (node, &toolbar, &pos);
2502           item  = gtk_separator_tool_item_new ();
2503           gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
2504           info->proxy = GTK_WIDGET (item);
2505           g_object_ref_sink (info->proxy);
2506           gtk_widget_set_no_show_all (info->proxy, TRUE);
2507           if (info->expand)
2508             {
2509               gtk_tool_item_set_expand (GTK_TOOL_ITEM (item), TRUE);
2510               gtk_separator_tool_item_set_draw
2511                 (GTK_SEPARATOR_TOOL_ITEM (item), FALSE);
2512               separator_mode = SEPARATOR_MODE_VISIBLE;
2513             }
2514           else
2515             separator_mode = SEPARATOR_MODE_SMART;
2516           
2517           g_object_set_data (G_OBJECT (info->proxy),
2518                              I_("gtk-separator-mode"),
2519                              GINT_TO_POINTER (separator_mode));
2520           gtk_widget_show (info->proxy);
2521         }
2522       else
2523         {
2524           GtkWidget *menushell;
2525           gint pos;
2526           
2527           if (GTK_IS_SEPARATOR_MENU_ITEM (info->proxy))
2528             {
2529               gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2530                                     info->proxy);
2531               g_object_unref (info->proxy);
2532               info->proxy = NULL;
2533             }
2534           
2535           find_menu_position (node, &menushell, &pos);
2536           info->proxy = gtk_separator_menu_item_new ();
2537           g_object_ref_sink (info->proxy);
2538           gtk_widget_set_no_show_all (info->proxy, TRUE);
2539           g_object_set_data (G_OBJECT (info->proxy),
2540                              I_("gtk-separator-mode"),
2541                              GINT_TO_POINTER (SEPARATOR_MODE_SMART));
2542           gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
2543                                  info->proxy, pos);
2544           gtk_widget_show (info->proxy);
2545         }
2546       break;
2547     case NODE_TYPE_ACCELERATOR:
2548       gtk_action_connect_accelerator (action);
2549       break;
2550     }
2551   
2552   if (action)
2553     g_object_ref (action);
2554   if (info->action)
2555     g_object_unref (info->action);
2556   info->action = action;
2557
2558  recurse_children:
2559   /* process children */
2560   child = node->children;
2561   while (child)
2562     {
2563       GNode *current;
2564       
2565       current = child;
2566       child = current->next;
2567       update_node (self, current, in_popup);
2568     }
2569   
2570   if (info->proxy) 
2571     {
2572       if (info->type == NODE_TYPE_MENU && GTK_IS_MENU_ITEM (info->proxy)) 
2573         update_smart_separators (gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy)));
2574       else if (info->type == NODE_TYPE_MENU || 
2575                info->type == NODE_TYPE_TOOLBAR || 
2576                info->type == NODE_TYPE_POPUP) 
2577         update_smart_separators (info->proxy);
2578     }
2579   
2580   /* handle cleanup of dead nodes */
2581   if (node->children == NULL && info->uifiles == NULL)
2582     {
2583       if (info->proxy)
2584         gtk_widget_destroy (info->proxy);
2585       if (info->extra)
2586         gtk_widget_destroy (info->extra);
2587       if (info->type == NODE_TYPE_ACCELERATOR)
2588         gtk_action_disconnect_accelerator (info->action);
2589       free_node (node);
2590       g_node_destroy (node);
2591     }
2592 }
2593
2594 static gboolean
2595 do_updates (GtkUIManager *self)
2596 {
2597   /* this function needs to check through the tree for dirty nodes.
2598    * For such nodes, it needs to do the following:
2599    *
2600    * 1) check if they are referenced by any loaded UI files anymore.
2601    *    In which case, the proxy widget should be destroyed, unless
2602    *    there are any subnodes.
2603    *
2604    * 2) lookup the action for this node again.  If it is different to
2605    *    the current one (or if no previous action has been looked up),
2606    *    the proxy is reconnected to the new action (or a new proxy widget
2607    *    is created and added to the parent container).
2608    */
2609   update_node (self, self->private_data->root_node, FALSE);
2610
2611   self->private_data->update_tag = 0;
2612
2613   return FALSE;
2614 }
2615
2616 static gboolean
2617 do_updates_idle (GtkUIManager *self)
2618 {
2619   GDK_THREADS_ENTER ();
2620   do_updates (self);
2621   GDK_THREADS_LEAVE ();
2622
2623   return FALSE;
2624 }
2625
2626 static void
2627 queue_update (GtkUIManager *self)
2628 {
2629   if (self->private_data->update_tag != 0)
2630     return;
2631
2632   self->private_data->update_tag = g_idle_add ((GSourceFunc)do_updates_idle, 
2633                                                self);
2634 }
2635
2636
2637 /**
2638  * gtk_ui_manager_ensure_update:
2639  * @self: a #GtkUIManager
2640  * 
2641  * Makes sure that all pending updates to the UI have been completed.
2642  *
2643  * This may occasionally be necessary, since #GtkUIManager updates the 
2644  * UI in an idle function. A typical example where this function is
2645  * useful is to enforce that the menubar and toolbar have been added to 
2646  * the main window before showing it:
2647  * <informalexample>
2648  * <programlisting>
2649  * gtk_container_add (GTK_CONTAINER (window), vbox); 
2650  * g_signal_connect (merge, "add_widget", 
2651  *                   G_CALLBACK (add_widget), vbox);
2652  * gtk_ui_manager_add_ui_from_file (merge, "my-menus");
2653  * gtk_ui_manager_add_ui_from_file (merge, "my-toolbars");
2654  * gtk_ui_manager_ensure_update (merge);  
2655  * gtk_widget_show (window);
2656  * </programlisting>
2657  * </informalexample>
2658  *
2659  * Since: 2.4
2660  **/
2661 void
2662 gtk_ui_manager_ensure_update (GtkUIManager *self)
2663 {
2664   if (self->private_data->update_tag != 0)
2665     {
2666       g_source_remove (self->private_data->update_tag);
2667       do_updates (self);
2668     }
2669 }
2670
2671 static gboolean
2672 dirty_traverse_func (GNode   *node,
2673                      gpointer data)
2674 {
2675   NODE_INFO (node)->dirty = TRUE;
2676   return FALSE;
2677 }
2678
2679 static void
2680 dirty_all_nodes (GtkUIManager *self)
2681 {
2682   g_node_traverse (self->private_data->root_node,
2683                    G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2684                    dirty_traverse_func, NULL);
2685   queue_update (self);
2686 }
2687
2688 static void
2689 mark_node_dirty (GNode *node)
2690 {
2691   GNode *p;
2692
2693   /* FIXME could optimize this */
2694   for (p = node; p; p = p->parent)
2695     NODE_INFO (p)->dirty = TRUE;  
2696 }
2697
2698 static const gchar *
2699 open_tag_format (NodeType type)
2700 {
2701   switch (type)
2702     {
2703     case NODE_TYPE_UNDECIDED: return "%*s<UNDECIDED"; 
2704     case NODE_TYPE_ROOT: return "%*s<ui"; 
2705     case NODE_TYPE_MENUBAR: return "%*s<menubar";
2706     case NODE_TYPE_MENU: return "%*s<menu";
2707     case NODE_TYPE_TOOLBAR: return "%*s<toolbar";
2708     case NODE_TYPE_MENU_PLACEHOLDER:
2709     case NODE_TYPE_TOOLBAR_PLACEHOLDER: return "%*s<placeholder";
2710     case NODE_TYPE_POPUP: return "%*s<popup";
2711     case NODE_TYPE_MENUITEM: return "%*s<menuitem";
2712     case NODE_TYPE_TOOLITEM: return "%*s<toolitem";
2713     case NODE_TYPE_SEPARATOR: return "%*s<separator";
2714     case NODE_TYPE_ACCELERATOR: return "%*s<accelerator";
2715     default: return NULL;
2716     }
2717 }
2718
2719 static const gchar *
2720 close_tag_format (NodeType type)
2721 {
2722   switch (type)
2723     {
2724     case NODE_TYPE_UNDECIDED: return "%*s</UNDECIDED>\n";
2725     case NODE_TYPE_ROOT: return "%*s</ui>\n";
2726     case NODE_TYPE_MENUBAR: return "%*s</menubar>\n";
2727     case NODE_TYPE_MENU: return "%*s</menu>\n";
2728     case NODE_TYPE_TOOLBAR: return "%*s</toolbar>\n";
2729     case NODE_TYPE_MENU_PLACEHOLDER:
2730     case NODE_TYPE_TOOLBAR_PLACEHOLDER: return "%*s</placeholder>\n";
2731     case NODE_TYPE_POPUP: return "%*s</popup>\n";
2732     default: return NULL;
2733     }
2734 }
2735
2736 static void
2737 print_node (GtkUIManager *self,
2738             GNode        *node,
2739             gint          indent_level,
2740             GString      *buffer)
2741 {
2742   Node  *mnode;
2743   GNode *child;
2744   const gchar *open_fmt;
2745   const gchar *close_fmt;
2746
2747   mnode = node->data;
2748
2749   open_fmt = open_tag_format (mnode->type);
2750   close_fmt = close_tag_format (mnode->type);
2751
2752   g_string_append_printf (buffer, open_fmt, indent_level, "");
2753
2754   if (mnode->type != NODE_TYPE_ROOT)
2755     {
2756       if (mnode->name)
2757         g_string_append_printf (buffer, " name=\"%s\"", mnode->name);
2758       
2759       if (mnode->action_name)
2760         g_string_append_printf (buffer, " action=\"%s\"",
2761                                 g_quark_to_string (mnode->action_name));
2762     }
2763
2764   g_string_append (buffer, close_fmt ? ">\n" : "/>\n");
2765
2766   for (child = node->children; child != NULL; child = child->next)
2767     print_node (self, child, indent_level + 2, buffer);
2768
2769   if (close_fmt)
2770     g_string_append_printf (buffer, close_fmt, indent_level, "");
2771 }
2772
2773 /**
2774  * gtk_ui_manager_get_ui:
2775  * @self: a #GtkUIManager
2776  * 
2777  * Creates a <link linkend="XML-UI">UI definition</link> of the merged UI.
2778  * 
2779  * Return value: A newly allocated string containing an XML representation of 
2780  * the merged UI.
2781  *
2782  * Since: 2.4
2783  **/
2784 gchar *
2785 gtk_ui_manager_get_ui (GtkUIManager *self)
2786 {
2787   GString *buffer;
2788
2789   buffer = g_string_new (NULL);
2790
2791   gtk_ui_manager_ensure_update (self); 
2792  
2793   print_node (self, self->private_data->root_node, 0, buffer);  
2794
2795   return g_string_free (buffer, FALSE);
2796 }
2797
2798 #ifdef G_OS_WIN32
2799
2800 #undef gtk_ui_manager_add_ui_from_file
2801
2802 guint
2803 gtk_ui_manager_add_ui_from_file (GtkUIManager *self,
2804                                  const gchar  *filename,
2805                                  GError      **error)
2806 {
2807   gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error);
2808   guint retval;
2809
2810   if (utf8_filename == NULL)
2811     return 0;
2812   
2813   retval = gtk_ui_manager_add_ui_from_file_utf8 (self, utf8_filename, error);
2814
2815   g_free (utf8_filename);
2816
2817   return retval;
2818 }
2819
2820 #endif
2821
2822 #define __GTK_UI_MANAGER_C__
2823 #include "gtkaliasdef.c"