2 * GTK - The GIMP Toolkit
3 * Copyright (C) 1998, 1999 Red Hat, Inc.
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.
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.
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.
23 * Author: James Henstridge <james@daa.com.au>
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/.
34 #include "gtkuimanager.h"
35 #include "gtktoolbar.h"
36 #include "gtkseparatortoolitem.h"
37 #include "gtkmenushell.h"
39 #include "gtkmenubar.h"
40 #include "gtkseparatormenuitem.h"
41 #include "gtktearoffmenuitem.h"
44 #undef DEBUG_UI_MANAGER
53 NODE_TYPE_MENU_PLACEHOLDER,
54 NODE_TYPE_TOOLBAR_PLACEHOLDER,
62 typedef struct _Node Node;
72 GtkWidget *extra; /*GtkMenu for submenus, second separator for placeholders*/
79 #define GTK_UI_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_UI_MANAGER, GtkUIManagerPrivate))
81 struct _GtkUIManagerPrivate
83 GtkAccelGroup *accel_group;
92 gboolean add_tearoffs;
95 #define NODE_INFO(node) ((Node *)node->data)
97 typedef struct _NodeUIReference NodeUIReference;
99 struct _NodeUIReference
105 static void gtk_ui_manager_class_init (GtkUIManagerClass *class);
106 static void gtk_ui_manager_init (GtkUIManager *self);
107 static void gtk_ui_manager_finalize (GObject *object);
108 static void gtk_ui_manager_set_property (GObject *object,
112 static void gtk_ui_manager_get_property (GObject *object,
116 static void queue_update (GtkUIManager *self);
117 static void dirty_all_nodes (GtkUIManager *self);
118 static GNode * get_child_node (GtkUIManager *self,
120 const gchar *childname,
121 gint childname_length,
125 static GNode * get_node (GtkUIManager *self,
129 static gboolean free_node (GNode *node);
130 static void node_prepend_ui_reference (Node *node,
132 GQuark action_quark);
133 static void node_remove_ui_reference (Node *node,
151 static guint merge_signals[LAST_SIGNAL] = { 0 };
153 static GMemChunk *merge_node_chunk = NULL;
156 gtk_ui_manager_get_type (void)
158 static GtkType type = 0;
162 static const GTypeInfo type_info =
164 sizeof (GtkUIManagerClass),
165 (GBaseInitFunc) NULL,
166 (GBaseFinalizeFunc) NULL,
167 (GClassInitFunc) gtk_ui_manager_class_init,
168 (GClassFinalizeFunc) NULL,
171 sizeof (GtkUIManager),
173 (GInstanceInitFunc) gtk_ui_manager_init,
176 type = g_type_register_static (G_TYPE_OBJECT,
184 gtk_ui_manager_class_init (GtkUIManagerClass *klass)
186 GObjectClass *gobject_class;
188 gobject_class = G_OBJECT_CLASS (klass);
190 if (!merge_node_chunk)
191 merge_node_chunk = g_mem_chunk_create (Node, 64,
194 gobject_class->finalize = gtk_ui_manager_finalize;
195 gobject_class->set_property = gtk_ui_manager_set_property;
196 gobject_class->get_property = gtk_ui_manager_get_property;
199 * GtkUIManager:add-tearoffs:
201 * The "add-tearoffs" property controls whether generated menus
202 * have tearoff menu items.
204 * Note that this only affects regular menus. Generated popup
205 * menus never have tearoff menu items.
209 g_object_class_install_property (gobject_class,
211 g_param_spec_boolean ("add_tearoffs",
212 _("Add tearoffs to menus"),
213 _("Whether tearoff menu items should be added to menus"),
215 G_PARAM_READABLE | G_PARAM_WRITABLE));
218 * GtkUIManager::add-widget:
219 * @merge: a #GtkUIManager
220 * @widget: the added widget
222 * The add_widget signal is emitted for each generated menubar and toolbar.
223 * It is not emitted for generated popup menus, which can be obtained by
224 * gtk_ui_manager_get_widget().
228 merge_signals[ADD_WIDGET] =
229 g_signal_new ("add_widget",
230 G_OBJECT_CLASS_TYPE (klass),
231 G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
232 G_STRUCT_OFFSET (GtkUIManagerClass, add_widget), NULL, NULL,
233 g_cclosure_marshal_VOID__OBJECT,
238 * GtkUIManager::changed:
239 * @merge: a #GtkUIManager
241 * The "changed" signal is emitted whenever the merged UI changes.
245 merge_signals[CHANGED] =
246 g_signal_new ("changed",
247 G_OBJECT_CLASS_TYPE (klass),
248 G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
249 G_STRUCT_OFFSET (GtkUIManagerClass, changed), NULL, NULL,
250 g_cclosure_marshal_VOID__VOID,
253 g_type_class_add_private (gobject_class, sizeof (GtkUIManagerPrivate));
258 gtk_ui_manager_init (GtkUIManager *self)
263 self->private_data = GTK_UI_MANAGER_GET_PRIVATE (self);
265 self->private_data->accel_group = gtk_accel_group_new ();
267 self->private_data->root_node = NULL;
268 self->private_data->action_groups = NULL;
270 self->private_data->last_merge_id = 0;
271 self->private_data->add_tearoffs = FALSE;
273 merge_id = gtk_ui_manager_new_merge_id (self);
274 node = get_child_node (self, NULL, "ui", 2,
275 NODE_TYPE_ROOT, TRUE, FALSE);
276 node_prepend_ui_reference (NODE_INFO (node), merge_id, 0);
280 gtk_ui_manager_finalize (GObject *object)
282 GtkUIManager *self = GTK_UI_MANAGER (object);
284 if (self->private_data->update_tag != 0)
286 g_source_remove (self->private_data->update_tag);
287 self->private_data->update_tag = 0;
290 g_node_traverse (self->private_data->root_node,
291 G_POST_ORDER, G_TRAVERSE_ALL, -1,
292 (GNodeTraverseFunc)free_node, 0);
293 g_node_destroy (self->private_data->root_node);
294 self->private_data->root_node = NULL;
296 g_list_foreach (self->private_data->action_groups,
297 (GFunc) g_object_unref, NULL);
298 g_list_free (self->private_data->action_groups);
299 self->private_data->action_groups = NULL;
301 g_object_unref (self->private_data->accel_group);
302 self->private_data->accel_group = NULL;
306 gtk_ui_manager_set_property (GObject *object,
311 GtkUIManager *self = GTK_UI_MANAGER (object);
315 case PROP_ADD_TEAROFFS:
316 gtk_ui_manager_set_add_tearoffs (self, g_value_get_boolean (value));
319 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
325 gtk_ui_manager_get_property (GObject *object,
330 GtkUIManager *self = GTK_UI_MANAGER (object);
334 case PROP_ADD_TEAROFFS:
335 g_value_set_boolean (value, self->private_data->add_tearoffs);
338 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
345 * gtk_ui_manager_new:
347 * Creates a new ui manager object.
349 * Return value: a new ui manager object.
354 gtk_ui_manager_new (void)
356 return g_object_new (GTK_TYPE_UI_MANAGER, NULL);
361 * gtk_ui_manager_get_add_tearoffs:
362 * @self: a #GtkUIManager
364 * Returns whether menus generated by this #GtkUIManager
365 * will have tearoff menu items.
367 * Return value: whether tearoff menu items are added
372 gtk_ui_manager_get_add_tearoffs (GtkUIManager *self)
374 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), FALSE);
376 return self->private_data->add_tearoffs;
381 * gtk_ui_manager_set_add_tearoffs:
382 * @self: a #GtkUIManager
383 * @add_tearoffs: whether tearoff menu items are added
385 * Sets the "add_tearoffs" property, which controls whether menus
386 * generated by this #GtkUIManager will have tearoff menu items.
388 * Note that this only affects regular menus. Generated popup
389 * menus never have tearoff menu items.
394 gtk_ui_manager_set_add_tearoffs (GtkUIManager *self,
395 gboolean add_tearoffs)
397 g_return_if_fail (GTK_IS_UI_MANAGER (self));
399 add_tearoffs = add_tearoffs != FALSE;
401 if (add_tearoffs != self->private_data->add_tearoffs)
403 self->private_data->add_tearoffs = add_tearoffs;
405 dirty_all_nodes (self);
407 g_object_notify (G_OBJECT (self), "add_tearoffs");
412 * gtk_ui_manager_insert_action_group:
413 * @self: a #GtkUIManager object
414 * @action_group: the action group to be inserted
415 * @pos: the position at which the group will be inserted.
417 * Inserts an action group into the list of action groups associated
423 gtk_ui_manager_insert_action_group (GtkUIManager *self,
424 GtkActionGroup *action_group,
427 g_return_if_fail (GTK_IS_UI_MANAGER (self));
428 g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
429 g_return_if_fail (g_list_find (self->private_data->action_groups,
430 action_group) == NULL);
432 g_object_ref (action_group);
433 self->private_data->action_groups =
434 g_list_insert (self->private_data->action_groups, action_group, pos);
436 /* dirty all nodes, as action bindings may change */
437 dirty_all_nodes (self);
441 * gtk_ui_manager_remove_action_group:
442 * @self: a #GtkUIManager object
443 * @action_group: the action group to be removed
445 * Removes an action group from the list of action groups associated
451 gtk_ui_manager_remove_action_group (GtkUIManager *self,
452 GtkActionGroup *action_group)
454 g_return_if_fail (GTK_IS_UI_MANAGER (self));
455 g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
456 g_return_if_fail (g_list_find (self->private_data->action_groups,
457 action_group) != NULL);
459 self->private_data->action_groups =
460 g_list_remove (self->private_data->action_groups, action_group);
461 g_object_unref (action_group);
463 /* dirty all nodes, as action bindings may change */
464 dirty_all_nodes (self);
468 * gtk_ui_manager_get_action_groups:
469 * @self: a #GtkUIManager object
471 * Returns the list of action groups associated with @self.
473 * Return value: a #GList of action groups. The list is owned by GTK+
474 * and should not be modified.
479 gtk_ui_manager_get_action_groups (GtkUIManager *self)
481 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
483 return self->private_data->action_groups;
487 * gtk_ui_manager_get_accel_group:
488 * @self: a #GtkUIManager object
490 * Returns the #GtkAccelGroup associated with @self.
492 * Return value: the #GtkAccelGroup.
497 gtk_ui_manager_get_accel_group (GtkUIManager *self)
499 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
501 return self->private_data->accel_group;
505 * gtk_ui_manager_get_widget:
506 * @self: a #GtkUIManager
509 * Looks up a widget by following a path.
510 * The path consists of the names specified in the XML description of the UI.
511 * separated by '/'. Elements which don't have a name or action attribute in
512 * the XML (e.g. <popup>) can be addressed by their XML element name
513 * (e.g. "popup"). The root element (<ui>) can be omitted in the path.
515 * Return value: the widget found by following the path, or %NULL if no widget
521 gtk_ui_manager_get_widget (GtkUIManager *self,
526 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
527 g_return_val_if_fail (path != NULL, NULL);
529 /* ensure that there are no pending updates before we get the
531 gtk_ui_manager_ensure_update (self);
533 node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
538 return NODE_INFO (node)->proxy;
542 * gtk_ui_manager_get_action:
543 * @self: a #GtkUIManager
546 * Looks up an action by following a path. See gtk_ui_manager_get_widget()
547 * for more information about paths.
549 * Return value: the action whose proxy widget is found by following the path,
550 * or %NULL if no widget was found.
555 gtk_ui_manager_get_action (GtkUIManager *self,
560 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
561 g_return_val_if_fail (path != NULL, NULL);
563 /* ensure that there are no pending updates before we get
565 gtk_ui_manager_ensure_update (self);
567 node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
572 return NODE_INFO (node)->action;
576 get_child_node (GtkUIManager *self,
578 const gchar *childname,
579 gint childname_length,
586 g_return_val_if_fail (parent == NULL ||
587 (NODE_INFO (parent)->type != NODE_TYPE_MENUITEM &&
588 NODE_INFO (parent)->type != NODE_TYPE_TOOLITEM),
595 for (child = parent->children; child != NULL; child = child->next)
597 if (strlen (NODE_INFO (child)->name) == childname_length &&
598 !strncmp (NODE_INFO (child)->name, childname, childname_length))
600 /* if undecided about node type, set it */
601 if (NODE_INFO (child)->type == NODE_TYPE_UNDECIDED)
602 NODE_INFO (child)->type = node_type;
604 /* warn about type mismatch */
605 if (NODE_INFO (child)->type != NODE_TYPE_UNDECIDED &&
606 node_type != NODE_TYPE_UNDECIDED &&
607 NODE_INFO (child)->type != node_type)
608 g_warning ("node type doesn't match %d (%s is type %d)",
610 NODE_INFO (child)->name,
611 NODE_INFO (child)->type);
617 if (!child && create)
621 mnode = g_chunk_new0 (Node, merge_node_chunk);
622 mnode->type = node_type;
623 mnode->name = g_strndup (childname, childname_length);
627 child = g_node_prepend_data (parent, mnode);
629 child = g_node_append_data (parent, mnode);
634 /* handle root node */
635 if (self->private_data->root_node)
637 child = self->private_data->root_node;
638 if (strncmp (NODE_INFO (child)->name, childname, childname_length) != 0)
639 g_warning ("root node name '%s' doesn't match '%s'",
640 childname, NODE_INFO (child)->name);
641 if (NODE_INFO (child)->type != NODE_TYPE_ROOT)
642 g_warning ("base element must be of type ROOT");
648 mnode = g_chunk_new0 (Node, merge_node_chunk);
649 mnode->type = node_type;
650 mnode->name = g_strndup (childname, childname_length);
653 child = self->private_data->root_node = g_node_new (mnode);
661 get_node (GtkUIManager *self,
666 const gchar *pos, *end;
667 GNode *parent, *node;
669 end = path + strlen (path);
671 parent = node = NULL;
677 slash = strchr (pos, '/');
679 length = slash - pos;
681 length = strlen (pos);
683 node = get_child_node (self, parent, pos, length, NODE_TYPE_UNDECIDED,
688 pos += length + 1; /* move past the node name and the slash too */
692 if (node != NULL && NODE_INFO (node)->type == NODE_TYPE_UNDECIDED)
693 NODE_INFO (node)->type = node_type;
699 free_node (GNode *node)
701 Node *info = NODE_INFO (node);
703 g_list_foreach (info->uifiles, (GFunc) g_free, NULL);
704 g_list_free (info->uifiles);
707 g_object_unref (info->action);
709 g_chunk_free (info, merge_node_chunk);
715 * gtk_ui_manager_new_merge_id:
716 * @self: a #GtkUIManager
718 * Returns an unused merge id, suitable for use with
719 * gtk_ui_manager_add_ui().
721 * Return value: an unused merge id.
726 gtk_ui_manager_new_merge_id (GtkUIManager *self)
728 self->private_data->last_merge_id++;
730 return self->private_data->last_merge_id;
734 node_prepend_ui_reference (Node *node,
738 NodeUIReference *reference;
740 reference = g_new (NodeUIReference, 1);
741 reference->action_quark = action_quark;
742 reference->merge_id = merge_id;
744 /* Prepend the reference */
745 node->uifiles = g_list_prepend (node->uifiles, reference);
751 node_remove_ui_reference (Node *node,
756 for (p = node->uifiles; p != NULL; p = p->next)
758 NodeUIReference *reference = p->data;
760 if (reference->merge_id == merge_id)
762 node->uifiles = g_list_remove_link (node->uifiles, p);
771 /* -------------------- The UI file parser -------------------- */
784 typedef struct _ParseContext ParseContext;
788 ParseState prev_state;
798 start_element_handler (GMarkupParseContext *context,
799 const gchar *element_name,
800 const gchar **attribute_names,
801 const gchar **attribute_values,
805 ParseContext *ctx = user_data;
806 GtkUIManager *self = ctx->self;
809 const gchar *node_name;
814 gboolean raise_error = TRUE;
820 for (i = 0; attribute_names[i] != NULL; i++)
822 if (!strcmp (attribute_names[i], "name"))
824 node_name = attribute_values[i];
826 else if (!strcmp (attribute_names[i], "action"))
828 action = attribute_values[i];
829 action_quark = g_quark_from_string (attribute_values[i]);
831 else if (!strcmp (attribute_names[i], "position"))
833 top = !strcmp (attribute_values[i], "top");
837 gint line_number, char_number;
839 g_markup_parse_context_get_position (context,
840 &line_number, &char_number);
843 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
844 _("Unknown attribute '%s' on line %d char %d"),
846 line_number, char_number);
851 /* Work out a name for this node. Either the name attribute, or
852 * the action, or the element name */
853 if (node_name == NULL)
858 node_name = element_name;
861 switch (element_name[0])
864 if (ctx->state == STATE_START && !strcmp (element_name, "ui"))
866 ctx->state = STATE_ROOT;
867 ctx->current = self->private_data->root_node;
870 node_prepend_ui_reference (NODE_INFO (ctx->current),
871 ctx->merge_id, action_quark);
875 if (ctx->state == STATE_ROOT && !strcmp (element_name, "menubar"))
877 ctx->state = STATE_MENU;
878 ctx->current = get_child_node (self, ctx->current,
879 node_name, strlen (node_name),
882 if (NODE_INFO (ctx->current)->action_name == 0)
883 NODE_INFO (ctx->current)->action_name = action_quark;
885 node_prepend_ui_reference (NODE_INFO (ctx->current),
886 ctx->merge_id, action_quark);
887 NODE_INFO (ctx->current)->dirty = TRUE;
891 else if (ctx->state == STATE_MENU && !strcmp (element_name, "menu"))
893 ctx->current = get_child_node (self, ctx->current,
894 node_name, strlen (node_name),
897 if (NODE_INFO (ctx->current)->action_name == 0)
898 NODE_INFO (ctx->current)->action_name = action_quark;
900 node_prepend_ui_reference (NODE_INFO (ctx->current),
901 ctx->merge_id, action_quark);
902 NODE_INFO (ctx->current)->dirty = TRUE;
906 else if (ctx->state == STATE_MENU && !strcmp (element_name, "menuitem"))
910 ctx->state = STATE_MENUITEM;
911 node = get_child_node (self, ctx->current,
912 node_name, strlen (node_name),
915 if (NODE_INFO (node)->action_name == 0)
916 NODE_INFO (node)->action_name = action_quark;
918 node_prepend_ui_reference (NODE_INFO (node),
919 ctx->merge_id, action_quark);
920 NODE_INFO (node)->dirty = TRUE;
926 if (ctx->state == STATE_ROOT && !strcmp (element_name, "popup"))
928 ctx->state = STATE_MENU;
929 ctx->current = get_child_node (self, ctx->current,
930 node_name, strlen (node_name),
933 if (NODE_INFO (ctx->current)->action_name == 0)
934 NODE_INFO (ctx->current)->action_name = action_quark;
936 node_prepend_ui_reference (NODE_INFO (ctx->current),
937 ctx->merge_id, action_quark);
938 NODE_INFO (ctx->current)->dirty = TRUE;
942 else if ((ctx->state == STATE_MENU || ctx->state == STATE_TOOLBAR) &&
943 !strcmp (element_name, "placeholder"))
945 if (ctx->state == STATE_TOOLBAR)
946 ctx->current = get_child_node (self, ctx->current,
947 node_name, strlen (node_name),
948 NODE_TYPE_TOOLBAR_PLACEHOLDER,
951 ctx->current = get_child_node (self, ctx->current,
952 node_name, strlen (node_name),
953 NODE_TYPE_MENU_PLACEHOLDER,
956 node_prepend_ui_reference (NODE_INFO (ctx->current),
957 ctx->merge_id, action_quark);
958 NODE_INFO (ctx->current)->dirty = TRUE;
964 if ((ctx->state == STATE_MENU || ctx->state == STATE_TOOLBAR) &&
965 !strcmp (element_name, "separator"))
969 if (ctx->state == STATE_TOOLBAR)
970 ctx->state = STATE_TOOLITEM;
972 ctx->state = STATE_MENUITEM;
973 node = get_child_node (self, ctx->current,
974 node_name, strlen (node_name),
977 if (NODE_INFO (node)->action_name == 0)
978 NODE_INFO (node)->action_name = action_quark;
980 node_prepend_ui_reference (NODE_INFO (node),
981 ctx->merge_id, action_quark);
982 NODE_INFO (node)->dirty = TRUE;
988 if (ctx->state == STATE_ROOT && !strcmp (element_name, "toolbar"))
990 ctx->state = STATE_TOOLBAR;
991 ctx->current = get_child_node (self, ctx->current,
992 node_name, strlen (node_name),
995 if (NODE_INFO (ctx->current)->action_name == 0)
996 NODE_INFO (ctx->current)->action_name = action_quark;
998 node_prepend_ui_reference (NODE_INFO (ctx->current),
999 ctx->merge_id, action_quark);
1000 NODE_INFO (ctx->current)->dirty = TRUE;
1002 raise_error = FALSE;
1004 else if (ctx->state == STATE_TOOLBAR && !strcmp (element_name, "toolitem"))
1008 ctx->state = STATE_TOOLITEM;
1009 node = get_child_node (self, ctx->current,
1010 node_name, strlen (node_name),
1013 if (NODE_INFO (node)->action_name == 0)
1014 NODE_INFO (node)->action_name = action_quark;
1016 node_prepend_ui_reference (NODE_INFO (node),
1017 ctx->merge_id, action_quark);
1018 NODE_INFO (node)->dirty = TRUE;
1020 raise_error = FALSE;
1028 gint line_number, char_number;
1030 g_markup_parse_context_get_position (context,
1031 &line_number, &char_number);
1034 G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1035 _("Unexpected start tag '%s' on line %d char %d"),
1037 line_number, char_number);
1042 end_element_handler (GMarkupParseContext *context,
1043 const gchar *element_name,
1047 ParseContext *ctx = user_data;
1053 /* no need to GError here, GMarkup already catches this */
1056 ctx->current = NULL;
1057 ctx->state = STATE_END;
1061 ctx->current = ctx->current->parent;
1062 if (NODE_INFO (ctx->current)->type == NODE_TYPE_ROOT)
1063 ctx->state = STATE_ROOT;
1064 /* else, stay in same state */
1066 case STATE_MENUITEM:
1067 ctx->state = STATE_MENU;
1069 case STATE_TOOLITEM:
1070 ctx->state = STATE_TOOLBAR;
1076 cleanup (GMarkupParseContext *context,
1080 ParseContext *ctx = user_data;
1082 ctx->current = NULL;
1083 /* should also walk through the tree and get rid of nodes related to
1084 * this UI file's tag */
1086 gtk_ui_manager_remove_ui (ctx->self, ctx->merge_id);
1089 static GMarkupParser ui_parser = {
1090 start_element_handler,
1091 end_element_handler,
1099 xml_isspace (char c)
1101 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
1105 add_ui_from_string (GtkUIManager *self,
1106 const gchar *buffer,
1108 gboolean needs_root,
1111 ParseContext ctx = { 0 };
1112 GMarkupParseContext *context;
1114 ctx.state = STATE_START;
1117 ctx.merge_id = gtk_ui_manager_new_merge_id (self);
1119 context = g_markup_parse_context_new (&ui_parser, 0, &ctx, NULL);
1122 if (!g_markup_parse_context_parse (context, "<ui>", -1, error))
1125 if (!g_markup_parse_context_parse (context, buffer, length, error))
1129 if (!g_markup_parse_context_parse (context, "</ui>", -1, error))
1132 if (!g_markup_parse_context_end_parse (context, error))
1135 g_markup_parse_context_free (context);
1137 queue_update (self);
1139 g_signal_emit (self, merge_signals[CHANGED], 0);
1141 return ctx.merge_id;
1145 g_markup_parse_context_free (context);
1151 * gtk_ui_manager_add_ui_from_string:
1152 * @self: a #GtkUIManager object
1153 * @buffer: the string to parse
1154 * @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
1155 * @error: return location for an error
1157 * Parses a string containing a <link linkend="XML-UI">UI description</link> and
1158 * merges it with the current contents of @self. An enclosing <ui> </ui>
1159 * element is added if it is missing.
1161 * Return value: The merge id for the merged UI. The merge id can be used
1162 * to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred,
1163 * the return value is 0.
1168 gtk_ui_manager_add_ui_from_string (GtkUIManager *self,
1169 const gchar *buffer,
1173 gboolean needs_root = TRUE;
1177 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), FALSE);
1178 g_return_val_if_fail (buffer != NULL, FALSE);
1181 length = strlen (buffer);
1184 end = buffer + length;
1185 while (p != end && xml_isspace (*p))
1188 if (end - p >= 4 && strncmp (p, "<ui>", 4) == 0)
1191 return add_ui_from_string (self, buffer, length, needs_root, error);
1195 * gtk_ui_manager_add_ui_from_file:
1196 * @self: a #GtkUIManager object
1197 * @filename: the name of the file to parse
1198 * @error: return location for an error
1200 * Parses a file containing a <link linkend="XML-UI">UI description</link> and
1201 * merges it with the current contents of @self.
1203 * Return value: The merge id for the merged UI. The merge id can be used
1204 * to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred,
1205 * the return value is 0.
1210 gtk_ui_manager_add_ui_from_file (GtkUIManager *self,
1211 const gchar *filename,
1218 if (!g_file_get_contents (filename, &buffer, &length, error))
1221 res = add_ui_from_string (self, buffer, length, FALSE, error);
1228 * gtk_ui_manager_add_ui:
1229 * @self: a #GtkUIManager
1230 * @merge_id: the merge id for the merged UI, see gtk_ui_manager_new_merge_id()
1232 * @name: the name for the added UI element
1233 * @action: the name of the action to be proxied, or %NULL to add a separator
1234 * @type: the type of UI element to add.
1235 * @top: if %TRUE, the UI element is added before its siblings, otherwise it
1236 * is added after its siblings.
1238 * Adds a UI element to the current contents of @self.
1240 * If @type is %GTK_UI_MANAGER_AUTO, GTK+ inserts a menuitem, toolitem or
1241 * separator if such an element can be inserted at the place determined by
1242 * @path. Otherwise @type must indicate an element that can be inserted at
1243 * the place determined by @path.
1248 gtk_ui_manager_add_ui (GtkUIManager *self,
1252 const gchar *action,
1253 GtkUIManagerItemType type,
1259 GQuark action_quark = 0;
1261 g_return_if_fail (GTK_IS_UI_MANAGER (self));
1262 g_return_if_fail (merge_id > 0);
1263 g_return_if_fail (name != NULL);
1265 node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
1270 node_type = NODE_TYPE_UNDECIDED;
1272 switch (NODE_INFO (node)->type)
1274 case NODE_TYPE_MENUBAR:
1275 case NODE_TYPE_MENU:
1276 case NODE_TYPE_POPUP:
1277 case NODE_TYPE_MENU_PLACEHOLDER:
1280 case GTK_UI_MANAGER_AUTO:
1282 node_type = NODE_TYPE_MENUITEM;
1284 node_type = NODE_TYPE_SEPARATOR;
1286 case GTK_UI_MANAGER_MENU:
1287 node_type = NODE_TYPE_MENU;
1289 case GTK_UI_MANAGER_MENUITEM:
1290 node_type = NODE_TYPE_MENUITEM;
1292 case GTK_UI_MANAGER_SEPARATOR:
1293 node_type = NODE_TYPE_SEPARATOR;
1295 case GTK_UI_MANAGER_PLACEHOLDER:
1296 node_type = NODE_TYPE_MENU_PLACEHOLDER;
1302 case NODE_TYPE_TOOLBAR:
1303 case NODE_TYPE_TOOLBAR_PLACEHOLDER:
1306 case GTK_UI_MANAGER_AUTO:
1308 node_type = NODE_TYPE_TOOLITEM;
1310 node_type = NODE_TYPE_SEPARATOR;
1312 case GTK_UI_MANAGER_TOOLITEM:
1313 node_type = NODE_TYPE_TOOLITEM;
1315 case GTK_UI_MANAGER_SEPARATOR:
1316 node_type = NODE_TYPE_SEPARATOR;
1318 case GTK_UI_MANAGER_PLACEHOLDER:
1319 node_type = NODE_TYPE_MENU_PLACEHOLDER;
1325 case NODE_TYPE_ROOT:
1328 case GTK_UI_MANAGER_MENUBAR:
1329 node_type = NODE_TYPE_MENUBAR;
1331 case GTK_UI_MANAGER_TOOLBAR:
1332 node_type = NODE_TYPE_TOOLBAR;
1334 case GTK_UI_MANAGER_POPUP:
1335 node_type = NODE_TYPE_POPUP;
1345 if (node_type == NODE_TYPE_UNDECIDED)
1348 child = get_child_node (self, node,
1349 name, strlen (name),
1350 node_type, TRUE, top);
1353 action_quark = g_quark_from_string (action);
1355 node_prepend_ui_reference (NODE_INFO (child),
1356 merge_id, action_quark);
1358 if (NODE_INFO (node)->action_name == 0)
1359 NODE_INFO (child)->action_name = action_quark;
1361 NODE_INFO (child)->dirty = TRUE;
1363 queue_update (self);
1365 g_signal_emit (self, merge_signals[CHANGED], 0);
1369 remove_ui (GNode *node,
1372 guint merge_id = GPOINTER_TO_UINT (user_data);
1374 node_remove_ui_reference (NODE_INFO (node), merge_id);
1376 return FALSE; /* continue */
1380 * gtk_ui_manager_remove_ui:
1381 * @self: a #GtkUIManager object
1382 * @merge_id: a merge id as returned by gtk_ui_manager_add_ui_from_string()
1384 * Unmerges the part of @self<!-- -->s content identified by @merge_id.
1389 gtk_ui_manager_remove_ui (GtkUIManager *self,
1392 g_node_traverse (self->private_data->root_node,
1393 G_POST_ORDER, G_TRAVERSE_ALL, -1,
1394 remove_ui, GUINT_TO_POINTER (merge_id));
1396 queue_update (self);
1398 g_signal_emit (self, merge_signals[CHANGED], 0);
1401 /* -------------------- Updates -------------------- */
1405 get_action_by_name (GtkUIManager *merge,
1406 const char *action_name)
1414 for (tmp = merge->private_data->action_groups; tmp != NULL; tmp = tmp->next)
1416 GtkActionGroup *action_group = tmp->data;
1419 action = gtk_action_group_get_action (action_group, action_name);
1429 find_menu_position (GNode *node,
1430 GtkWidget **menushell_p,
1433 GtkWidget *menushell;
1436 g_return_val_if_fail (node != NULL, FALSE);
1437 g_return_val_if_fail (NODE_INFO (node)->type == NODE_TYPE_MENU ||
1438 NODE_INFO (node)->type == NODE_TYPE_POPUP ||
1439 NODE_INFO (node)->type == NODE_TYPE_MENU_PLACEHOLDER ||
1440 NODE_INFO (node)->type == NODE_TYPE_MENUITEM ||
1441 NODE_INFO (node)->type == NODE_TYPE_SEPARATOR,
1444 /* first sibling -- look at parent */
1445 if (node->prev == NULL)
1450 parent = node->parent;
1451 switch (NODE_INFO (parent)->type)
1453 case NODE_TYPE_MENUBAR:
1454 case NODE_TYPE_POPUP:
1455 menushell = NODE_INFO (parent)->proxy;
1458 case NODE_TYPE_MENU:
1459 menushell = NODE_INFO (parent)->proxy;
1460 if (GTK_IS_MENU_ITEM (menushell))
1461 menushell = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menushell));
1462 siblings = gtk_container_get_children (GTK_CONTAINER (menushell));
1463 if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
1468 case NODE_TYPE_MENU_PLACEHOLDER:
1469 menushell = gtk_widget_get_parent (NODE_INFO (parent)->proxy);
1470 g_return_val_if_fail (GTK_IS_MENU_SHELL (menushell), FALSE);
1471 pos = g_list_index (GTK_MENU_SHELL (menushell)->children,
1472 NODE_INFO (parent)->proxy) + 1;
1475 g_warning("%s: bad parent node type %d", G_STRLOC,
1476 NODE_INFO (parent)->type);
1482 GtkWidget *prev_child;
1485 sibling = node->prev;
1486 if (NODE_INFO (sibling)->type == NODE_TYPE_MENU_PLACEHOLDER)
1487 prev_child = NODE_INFO (sibling)->extra; /* second Separator */
1489 prev_child = NODE_INFO (sibling)->proxy;
1491 g_return_val_if_fail (GTK_IS_WIDGET (prev_child), FALSE);
1492 menushell = gtk_widget_get_parent (prev_child);
1493 g_return_val_if_fail (GTK_IS_MENU_SHELL (menushell), FALSE);
1495 pos = g_list_index (GTK_MENU_SHELL (menushell)->children, prev_child) + 1;
1499 *menushell_p = menushell;
1507 find_toolbar_position (GNode *node,
1508 GtkWidget **toolbar_p,
1514 g_return_val_if_fail (node != NULL, FALSE);
1515 g_return_val_if_fail (NODE_INFO (node)->type == NODE_TYPE_TOOLBAR ||
1516 NODE_INFO (node)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER ||
1517 NODE_INFO (node)->type == NODE_TYPE_TOOLITEM ||
1518 NODE_INFO (node)->type == NODE_TYPE_SEPARATOR,
1521 /* first sibling -- look at parent */
1522 if (node->prev == NULL)
1526 parent = node->parent;
1527 switch (NODE_INFO (parent)->type)
1529 case NODE_TYPE_TOOLBAR:
1530 toolbar = NODE_INFO (parent)->proxy;
1533 case NODE_TYPE_TOOLBAR_PLACEHOLDER:
1534 toolbar = gtk_widget_get_parent (NODE_INFO (parent)->proxy);
1535 g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), FALSE);
1536 pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar),
1537 GTK_TOOL_ITEM (NODE_INFO (parent)->proxy)) + 1;
1540 g_warning ("%s: bad parent node type %d", G_STRLOC,
1541 NODE_INFO (parent)->type);
1547 GtkWidget *prev_child;
1550 sibling = node->prev;
1551 if (NODE_INFO (sibling)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER)
1552 prev_child = NODE_INFO (sibling)->extra; /* second Separator */
1554 prev_child = NODE_INFO (sibling)->proxy;
1556 g_return_val_if_fail (GTK_IS_WIDGET (prev_child), FALSE);
1557 toolbar = gtk_widget_get_parent (prev_child);
1558 g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), FALSE);
1560 pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar),
1561 GTK_TOOL_ITEM (prev_child)) + 1;
1565 *toolbar_p = toolbar;
1573 SEPARATOR_MODE_SMART,
1574 SEPARATOR_MODE_VISIBLE,
1575 SEPARATOR_MODE_HIDDEN
1579 update_smart_separators (GtkWidget *proxy)
1581 GtkWidget *parent = NULL;
1583 if (GTK_IS_MENU (proxy) || GTK_IS_TOOLBAR (proxy))
1585 else if (GTK_IS_MENU_ITEM (proxy) || GTK_IS_TOOL_ITEM (proxy))
1586 parent = gtk_widget_get_parent (proxy);
1591 GList *children, *cur, *last;
1593 children = gtk_container_get_children (GTK_CONTAINER (parent));
1600 if (GTK_IS_SEPARATOR_MENU_ITEM (cur->data) ||
1601 GTK_IS_SEPARATOR_TOOL_ITEM (cur->data))
1604 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cur->data),
1605 "gtk-separator-mode"));
1608 case SEPARATOR_MODE_VISIBLE:
1609 gtk_widget_show (GTK_WIDGET (cur->data));
1613 case SEPARATOR_MODE_HIDDEN:
1614 gtk_widget_hide (GTK_WIDGET (cur->data));
1616 case SEPARATOR_MODE_SMART:
1619 gtk_widget_show (GTK_WIDGET (cur->data));
1624 gtk_widget_hide (GTK_WIDGET (cur->data));
1628 else if (GTK_WIDGET_VISIBLE (cur->data))
1631 if (GTK_IS_TEAROFF_MENU_ITEM (cur->data))
1641 gtk_widget_hide (GTK_WIDGET (last->data));
1646 update_node (GtkUIManager *self,
1648 gboolean add_tearoffs)
1653 #ifdef DEBUG_UI_MANAGER
1657 g_return_if_fail (node != NULL);
1658 g_return_if_fail (NODE_INFO (node) != NULL);
1660 info = NODE_INFO (node);
1662 #ifdef DEBUG_UI_MANAGER
1663 g_print ("update_node name=%s dirty=%d (", info->name, info->dirty);
1664 for (tmp = info->uifiles; tmp != NULL; tmp = tmp->next)
1666 NodeUIReference *ref = tmp->data;
1667 g_print("%s:%u", g_quark_to_string (ref->action_quark), ref->merge_id);
1676 const gchar *action_name;
1677 NodeUIReference *ref;
1679 if (info->uifiles == NULL) {
1680 /* We may need to remove this node.
1681 * This must be done in post order
1683 goto recurse_children;
1686 ref = info->uifiles->data;
1687 action_name = g_quark_to_string (ref->action_quark);
1688 action = get_action_by_name (self, action_name);
1690 info->dirty = FALSE;
1692 /* Check if the node doesn't have an action and must have an action */
1693 if (action == NULL &&
1694 info->type != NODE_TYPE_MENUBAR &&
1695 info->type != NODE_TYPE_TOOLBAR &&
1696 info->type != NODE_TYPE_SEPARATOR &&
1697 info->type != NODE_TYPE_MENU_PLACEHOLDER &&
1698 info->type != NODE_TYPE_TOOLBAR_PLACEHOLDER)
1700 /* FIXME: Should we warn here? */
1701 goto recurse_children;
1704 /* If the widget already has a proxy and the action hasn't changed, then
1705 * we only have to update the tearoff menu items.
1707 if (info->proxy != NULL && action == info->action)
1709 if (info->type == NODE_TYPE_MENU)
1714 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
1715 siblings = gtk_container_get_children (GTK_CONTAINER (menu));
1716 if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
1717 g_object_set (G_OBJECT (siblings->data), "visible", add_tearoffs, 0);
1720 goto recurse_children;
1725 case NODE_TYPE_MENUBAR:
1726 if (info->proxy == NULL)
1728 info->proxy = gtk_menu_bar_new ();
1729 gtk_widget_show (info->proxy);
1730 g_signal_emit (self, merge_signals[ADD_WIDGET], 0, info->proxy);
1733 case NODE_TYPE_POPUP:
1734 if (info->proxy == NULL)
1736 info->proxy = gtk_menu_new ();
1737 gtk_menu_set_accel_group (GTK_MENU (info->proxy),
1738 self->private_data->accel_group);
1741 case NODE_TYPE_MENU:
1743 GtkWidget *prev_submenu = NULL;
1746 /* remove the proxy if it is of the wrong type ... */
1747 if (info->proxy && G_OBJECT_TYPE (info->proxy) !=
1748 GTK_ACTION_GET_CLASS (action)->menu_item_type)
1750 prev_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
1753 g_object_ref (prev_submenu);
1754 gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
1756 gtk_action_disconnect_proxy (info->action, info->proxy);
1757 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1761 /* create proxy if needed ... */
1762 if (info->proxy == NULL)
1764 GtkWidget *menushell;
1767 if (find_menu_position (node, &menushell, &pos))
1771 info->proxy = gtk_action_create_menu_item (action);
1772 menu = gtk_menu_new ();
1773 tearoff = gtk_tearoff_menu_item_new ();
1774 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
1775 gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), menu);
1776 gtk_menu_set_accel_group (GTK_MENU (menu), self->private_data->accel_group);
1777 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos);
1781 gtk_action_connect_proxy (action, info->proxy);
1784 gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy),
1786 g_object_unref (prev_submenu);
1788 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
1789 siblings = gtk_container_get_children (GTK_CONTAINER (menu));
1790 if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
1791 g_object_set (G_OBJECT (siblings->data), "visible", add_tearoffs, 0);
1794 case NODE_TYPE_UNDECIDED:
1795 g_warning ("found 'undecided node!");
1797 case NODE_TYPE_ROOT:
1799 case NODE_TYPE_TOOLBAR:
1800 if (info->proxy == NULL)
1802 info->proxy = gtk_toolbar_new ();
1803 gtk_widget_show (info->proxy);
1804 g_signal_emit (self, merge_signals[ADD_WIDGET], 0, info->proxy);
1807 case NODE_TYPE_MENU_PLACEHOLDER:
1808 /* create menu items for placeholders if necessary ... */
1809 if (!GTK_IS_SEPARATOR_MENU_ITEM (info->proxy) ||
1810 !GTK_IS_SEPARATOR_MENU_ITEM (info->extra))
1814 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1820 gtk_container_remove (GTK_CONTAINER (info->extra->parent),
1825 if (info->proxy == NULL)
1827 GtkWidget *menushell;
1830 if (find_menu_position (node, &menushell, &pos))
1832 info->proxy = gtk_separator_menu_item_new ();
1833 g_object_set_data (G_OBJECT (info->proxy),
1834 "gtk-separator-mode",
1835 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
1836 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
1837 NODE_INFO (node)->proxy, pos);
1839 info->extra = gtk_separator_menu_item_new ();
1840 g_object_set_data (G_OBJECT (info->extra),
1841 "gtk-separator-mode",
1842 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
1843 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
1844 NODE_INFO (node)->extra, pos+1);
1848 case NODE_TYPE_TOOLBAR_PLACEHOLDER:
1849 /* create toolbar items for placeholders if necessary ... */
1850 if (!GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy) ||
1851 !GTK_IS_SEPARATOR_TOOL_ITEM (info->extra))
1855 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1861 gtk_container_remove (GTK_CONTAINER (info->extra->parent),
1866 if (info->proxy == NULL)
1871 if (find_toolbar_position (node, &toolbar, &pos))
1875 item = gtk_separator_tool_item_new ();
1876 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
1877 info->proxy = GTK_WIDGET (item);
1878 g_object_set_data (G_OBJECT (info->proxy),
1879 "gtk-separator-mode",
1880 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
1882 item = gtk_separator_tool_item_new ();
1883 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos+1);
1884 info->extra = GTK_WIDGET (item);
1885 g_object_set_data (G_OBJECT (info->extra),
1886 "gtk-separator-mode",
1887 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
1891 case NODE_TYPE_MENUITEM:
1892 /* remove the proxy if it is of the wrong type ... */
1893 if (info->proxy && G_OBJECT_TYPE (info->proxy) !=
1894 GTK_ACTION_GET_CLASS (action)->menu_item_type)
1896 g_signal_handlers_disconnect_by_func (info->proxy,
1897 G_CALLBACK (update_smart_separators),
1899 gtk_action_disconnect_proxy (info->action, info->proxy);
1900 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1904 /* create proxy if needed ... */
1905 if (info->proxy == NULL)
1907 GtkWidget *menushell;
1910 if (find_menu_position (node, &menushell, &pos))
1912 info->proxy = gtk_action_create_menu_item (action);
1914 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
1920 g_signal_handlers_disconnect_by_func (info->proxy,
1921 G_CALLBACK (update_smart_separators),
1923 gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
1924 gtk_action_connect_proxy (action, info->proxy);
1926 g_signal_connect (info->proxy, "notify::visible",
1927 G_CALLBACK (update_smart_separators), 0);
1929 case NODE_TYPE_TOOLITEM:
1930 /* remove the proxy if it is of the wrong type ... */
1931 if (info->proxy && G_OBJECT_TYPE (info->proxy) !=
1932 GTK_ACTION_GET_CLASS (action)->toolbar_item_type)
1934 g_signal_handlers_disconnect_by_func (info->proxy,
1935 G_CALLBACK (update_smart_separators),
1937 gtk_action_disconnect_proxy (info->action, info->proxy);
1938 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1942 /* create proxy if needed ... */
1943 if (info->proxy == NULL)
1948 if (find_toolbar_position (node, &toolbar, &pos))
1950 info->proxy = gtk_action_create_tool_item (action);
1952 gtk_toolbar_insert (GTK_TOOLBAR (toolbar),
1953 GTK_TOOL_ITEM (info->proxy), pos);
1958 g_signal_handlers_disconnect_by_func (info->proxy,
1959 G_CALLBACK (update_smart_separators),
1961 gtk_action_connect_proxy (action, info->proxy);
1963 g_signal_connect (info->proxy, "notify::visible",
1964 G_CALLBACK (update_smart_separators), 0);
1966 case NODE_TYPE_SEPARATOR:
1967 if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR ||
1968 NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER)
1973 if (GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy))
1975 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1980 if (find_toolbar_position (node, &toolbar, &pos))
1982 GtkToolItem *item = gtk_separator_tool_item_new ();
1983 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
1984 info->proxy = GTK_WIDGET (item);
1985 g_object_set_data (G_OBJECT (info->proxy),
1986 "gtk-separator-mode",
1987 GINT_TO_POINTER (SEPARATOR_MODE_SMART));
1988 gtk_widget_show (info->proxy);
1993 GtkWidget *menushell;
1996 if (GTK_IS_SEPARATOR_MENU_ITEM (info->proxy))
1998 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2003 if (find_menu_position (node, &menushell, &pos))
2005 info->proxy = gtk_separator_menu_item_new ();
2006 g_object_set_data (G_OBJECT (info->proxy),
2007 "gtk-separator-mode",
2008 GINT_TO_POINTER (SEPARATOR_MODE_SMART));
2009 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
2011 gtk_widget_show (info->proxy);
2018 g_object_ref (action);
2020 g_object_unref (info->action);
2021 info->action = action;
2025 /* process children */
2026 child = node->children;
2032 child = current->next;
2033 update_node (self, current, add_tearoffs && (info->type != NODE_TYPE_POPUP));
2036 if (info->type == NODE_TYPE_MENU)
2037 update_smart_separators (gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy)));
2038 else if (info->type == NODE_TYPE_TOOLBAR)
2039 update_smart_separators (info->proxy);
2042 /* handle cleanup of dead nodes */
2043 if (node->children == NULL && info->uifiles == NULL)
2046 gtk_widget_destroy (info->proxy);
2048 gtk_widget_destroy (info->extra);
2050 g_node_destroy (node);
2055 do_updates (GtkUIManager *self)
2057 /* this function needs to check through the tree for dirty nodes.
2058 * For such nodes, it needs to do the following:
2060 * 1) check if they are referenced by any loaded UI files anymore.
2061 * In which case, the proxy widget should be destroyed, unless
2062 * there are any subnodes.
2064 * 2) lookup the action for this node again. If it is different to
2065 * the current one (or if no previous action has been looked up),
2066 * the proxy is reconnected to the new action (or a new proxy widget
2067 * is created and added to the parent container).
2069 update_node (self, self->private_data->root_node,
2070 self->private_data->add_tearoffs);
2072 self->private_data->update_tag = 0;
2078 queue_update (GtkUIManager *self)
2080 if (self->private_data->update_tag != 0)
2083 self->private_data->update_tag = g_idle_add ((GSourceFunc)do_updates, self);
2088 * gtk_ui_manager_ensure_update:
2089 * @self: a #GtkUIManager
2091 * Makes sure that all pending updates to the UI have been completed.
2093 * This may occasionally be necessary, since #GtkUIManager updates the
2094 * UI in an idle function. A typical example where this function is
2095 * useful is to enforce that the menubar and toolbar have been added to
2096 * the main window before showing it:
2099 * gtk_container_add (GTK_CONTAINER (window), vbox);
2100 * g_signal_connect (merge, "add_widget",
2101 * G_CALLBACK (add_widget), vbox);
2102 * gtk_ui_manager_add_ui_from_file (merge, "my-menus");
2103 * gtk_ui_manager_add_ui_from_file (merge, "my-toolbars");
2104 * gtk_ui_manager_ensure_update (merge);
2105 * gtk_widget_show (window);
2107 * </informalexample>
2112 gtk_ui_manager_ensure_update (GtkUIManager *self)
2114 if (self->private_data->update_tag != 0)
2116 g_source_remove (self->private_data->update_tag);
2122 dirty_traverse_func (GNode *node,
2125 NODE_INFO (node)->dirty = TRUE;
2130 dirty_all_nodes (GtkUIManager *self)
2132 g_node_traverse (self->private_data->root_node,
2133 G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2134 dirty_traverse_func, NULL);
2135 queue_update (self);
2138 static const gchar *open_tag_format[] = {
2141 "%*s<menubar name=\"%s\">\n",
2142 "%*s<menu name='%s' action=\"%s\">\n",
2143 "%*s<toolbar name=\"%s\">\n",
2144 "%*s<placeholder name=\"%s\">\n",
2145 "%*s<placeholder name=\"%s\">\n",
2146 "%*s<popup name='%s' action=\"%s\">\n",
2147 "%*s<menuitem name=\"%s\" action=\"%s\"/>\n",
2148 "%*s<toolitem name=\"%s\" action=\"%s\"/>\n",
2149 "%*s<separator/>\n",
2152 static const gchar *close_tag_format[] = {
2153 "%*s</UNDECIDED>\n",
2158 "%*s</placeholder>\n",
2159 "%*s</placeholder>\n",
2167 print_node (GtkUIManager *self,
2177 g_string_append_printf (buffer, open_tag_format[mnode->type],
2180 g_quark_to_string (mnode->action_name));
2182 for (child = node->children; child != NULL; child = child->next)
2183 print_node (self, child, indent_level + 2, buffer);
2185 g_string_append_printf (buffer, close_tag_format[mnode->type],
2191 * gtk_ui_manager_get_ui:
2192 * @self: a #GtkUIManager
2194 * Creates a <link linkend="XML-UI">description</link> of the merged UI.
2196 * Return value: A newly allocated string containing an XML representation of
2202 gtk_ui_manager_get_ui (GtkUIManager *self)
2206 buffer = g_string_new (NULL);
2208 gtk_ui_manager_ensure_update (self);
2210 print_node (self, self->private_data->root_node, 0, buffer);
2212 return g_string_free (buffer, FALSE);