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