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