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