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