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